aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2017-02-08 23:45:18 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2017-02-08 23:45:18 +0800
commit1976c49f835267d33ef88bd3fc20d18363e12c0b (patch)
treee1eaca367a37238c1b2a5fc0f4ff7cb29a723a86
parent1afedc4cc39c1dcbe49f8c99843a1732bf22fa28 (diff)
downloadQMidiPlayer-1976c49f835267d33ef88bd3fc20d18363e12c0b.tar.xz
Add API version verification. This breaks compatibility
with old versions of plugins. Add RIFF MIDI support to the SMF reader. Documentation.
-rw-r--r--ChangeLog6
-rw-r--r--core/qmpmidiplay.hpp1
-rw-r--r--core/qmpmidiread.cpp13
-rw-r--r--doc/APIdoc.md65
-rw-r--r--doc/version.html2
-rw-r--r--include/qmpcorepublic.hpp10
-rw-r--r--qmidiplayer-desktop/qmpplistwindow.cpp4
-rw-r--r--qmidiplayer-desktop/qmpplugin.cpp10
-rw-r--r--qmidiplayer-desktop/qmpplugin.hpp1
-rw-r--r--sample-plugin/sampleplugin.hpp2
-rw-r--r--visualization/qmpvisualization.hpp2
11 files changed, 90 insertions, 26 deletions
diff --git a/ChangeLog b/ChangeLog
index a3c82b3..c66874f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2017-02-08 0.8.5 indev
+Add API version verification. This breaks compatibility
+with old versions of plugins.
+Documentation.
+Add RIFF MIDI support to the SMF reader.
+
2017-02-07 0.8.5 indev
First steps for the file reader API.
API additions and changes.
diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp
index 8d78c4e..b911844 100644
--- a/core/qmpmidiplay.hpp
+++ b/core/qmpmidiplay.hpp
@@ -6,6 +6,7 @@
#include <utility>
#include <vector>
#include <fluidsynth.h>
+#define QMP_MAIN
#include "../include/qmpcorepublic.hpp"
#include "qmpmidimappers.hpp"
class CMidiPlayer;
diff --git a/core/qmpmidiread.cpp b/core/qmpmidiread.cpp
index 4ac2ac8..b36fffc 100644
--- a/core/qmpmidiread.cpp
+++ b/core/qmpmidiread.cpp
@@ -203,10 +203,21 @@ void CSMFReader::headerChunkReader()
int CSMFReader::chunkReader(int hdrXp)
{
char hdr[6];
- if(!fgets(hdr,5,f))error(1,"E: Unexpected EOF.");
+ fread(hdr,1,4,f);
+ if(feof(f))error(1,"E: Unexpected EOF.");
if(hdrXp)
+ {
+ if(!strncmp(hdr,"RIFF",4))
+ {
+ fseek(f,4,SEEK_CUR);
+ fread(hdr,1,4,f);
+ if(strncmp(hdr,"RMID",4)){error(1,"E: Wrong file type in RIFF container.");throw std::runtime_error("Wrong file type in RIFF container");}
+ fseek(f,8,SEEK_CUR);
+ fread(hdr,1,4,f);
+ }
if(strncmp(hdr,"MThd",4)){error(1,"E: Wrong MIDI header.");throw std::runtime_error("Wrong MIDI header");}
else return headerChunkReader(),0;
+ }
else
if(strncmp(hdr,"MTrk",4))
{
diff --git a/doc/APIdoc.md b/doc/APIdoc.md
index 043c1c0..5b69b5e 100644
--- a/doc/APIdoc.md
+++ b/doc/APIdoc.md
@@ -5,43 +5,72 @@
# 0. Overview
-Plugin for QMidiPlayer is a dynamically-loaded library that exports the symbol "qmpPluginGetInterface".
+Plugin for QMidiPlayer is a dynamically-loaded library that exports the symbol `qmpPluginGetInterface` and `qmpPluginGetAPIRev`.
Before starting developing your own plugin, make sure to have a look at the sample plugin in the "sample-plugin" folder.
# 1. "QMidiPlayer Plugin SDK"
-SDK for developing QMidiPlayer plugins is merely the "qmpcorepublic.hpp" header found in the "include" directory in
+SDK for developing QMidiPlayer plugins is merely the `qmpcorepublic.hpp` header found in the "include" directory in
the source tree. It includes classes used by QMidiPlayer's internal plugin infrastructure.
-# 2. Basics for a working plugin.
+# 2. Basics for a working plugin
First of all, you should make your library distinct from other libraries that are not QMidiPlayer plugins. You can achive
-it by exporting the symbol "qmpPluginGetInterface". Specifically, what you should do is to add the following snipplet to
-somewhere of your code:
+it by exporting the symbols `qmpPluginGetInterface` and `qmpPluginGetAPIRev`. Specifically, what you should do is to add
+the following snipplet to somewhere of your code:
-> extern "C"{
-> EXPORTSYM qmpPluginIntf* qmpPluginGetInterface(qmpPluginAPI* api)
-> //semicolon or implementation here.
-> }
+```
+extern "C"{
+ EXPORTSYM qmpPluginIntf* qmpPluginGetInterface(qmpPluginAPI* api)
+ //semicolon or implementation here.
+ EXPORTSYM const char* qmpPluginGetAPIRev()
+ {return QMP_PLUGIN_API_REV;}
+}
+```
-The EXPORTSYM macro tells the compiler to export the following symbol. qmpPluginIntf is the abstract class which every
+The `EXPORTSYM` macro tells the compiler to export the following symbol. `qmpPluginIntf` is the abstract class which every
plugin class should derive from. The parameter api provides access to QMidiPlayer's plugin API, which should be stored
-for future use.
+for future use. `qmpPluginGetAPIRev` helps the core to determine whether the plugin is compatible with the API it exports.
-Next you should create your own plugin class which implements the abstract class "qmpPluginIntf".
+Next you should create your own plugin class which implements the abstract class `qmpPluginIntf`.
-# 3. A Peek into the class "qmpPluginIntf"
+# 3. A peek into the class `qmpPluginIntf`
It has 6 public members: one default constructor, one default destructor and four methods:
-- void init()
+- `void init()`
Called on start up if the plugin is loaded successfully and enabled.
-- void deinit()
+- `void deinit()`
Called on shutdown if the plugin is enabled.
-- const char* pluginGetName()
+- `const char* pluginGetName()`
This function should return the display name of the plugin.
-- const char* pluginGetVersion()
+- `const char* pluginGetVersion()`
This function should return the version of the plugin, which will be shown in the plugin manager.
-Your plugin is expected to register handlers and functionalities when init() is called by the host,
+Your plugin is expected to register handlers (hooks) and functionalities when init() is called by the host,
and do clean-up jobs when deinit() is caled.
+
+Currently plugins can register handlers for these functionalities:
+
+- Visualization (via `(un)registerVisualizationIntf`)
+- MIDI File Reader (via `(un)registerFileReader`)
+
+...and can hooks into the following processes:
+
+- Event reader, after an event is read or after the whole file reading process is completed
+ (via `(un)registerEventReaderIntf` and `(un)registerFileReadFinishedHandlerIntf`)
+- Event handler, when an event is going to be sent by the player (via `(un)registerEventHandlerIntf`)
+
+Functionalities has their own interfaces you need to implement(`qmpVisualizationIntf` and `IMidiFileReader`, respectively),
+while hooks uses the universal `IMidiCallBack` interface. Functionalities are discussed later.
+
+When you register a hook, you provide the core with a instance of your class that implements the `IMidiCallBack` interface
+and your `userdata` to be used when the core is calling the callback. When the callback is called, it will be fed with
+proper `callerdata` generated by the core and the `userdata` you provided. Type of `callerdata` varies by hooks. Event
+reader and handler hooks have `SEventCallBackData*` as their `callerdata` while file read finish hook doesn't provide
+`callerdata` (`NULL`).
+
+# 4. Functionalities
+Plugins extend the hosts with extra functionalities. With hooks, handlers and the built-in core API, you can already do a
+lot of hacking. If that cannot make you satisfied, QMidiPlayer have several vacancies that are expected to be implemented
+by plugins.
diff --git a/doc/version.html b/doc/version.html
index 93bd344..d3b3218 100644
--- a/doc/version.html
+++ b/doc/version.html
@@ -26,7 +26,7 @@
<h1>Version information</h1>
<div style="text-align:center;">
<img src="../img/qmidiplyr.png"><br>
- QMidiPlayer documentation for version 0.8.2<br>
+ QMidiPlayer documentation for version 0.8.5<br>
An MIDI player based on fluidsynth and Qt.<br>
Written by Chris Xiong.<br>
</div><br>
diff --git a/include/qmpcorepublic.hpp b/include/qmpcorepublic.hpp
index 6e56310..f42d42a 100644
--- a/include/qmpcorepublic.hpp
+++ b/include/qmpcorepublic.hpp
@@ -1,6 +1,5 @@
-#ifndef QMPCOREPUBLIC_H
-#define QMPCOREPUBLIC_H
-#include <cstring>
+#ifndef QMPCOREPUBLIC_HPP
+#define QMPCOREPUBLIC_HPP
#include <cstdint>
#include <vector>
#include <string>
@@ -9,6 +8,7 @@
#else
#define EXPORTSYM __attribute__ ((visibility ("default")))
#endif
+#define QMP_PLUGIN_API_REV "1+indev"
//MIDI Event structure
struct SEvent
{
@@ -162,4 +162,6 @@ class qmpPluginAPI
//through the parameter. This function should return a pointer to a class
//that implementes the plugin pinterface (qmpPluginIntf).
typedef qmpPluginIntf*(*qmpPluginEntry)(qmpPluginAPI*);
-#endif // QMPCOREPUBLIC_H
+//The following symbol only presents in plugins. Its purpose is to help the core reject incompatible plugins.
+typedef const char*(*qmpPluginAPIRevEntry)();
+#endif // QMPCOREPUBLIC_HPP
diff --git a/qmidiplayer-desktop/qmpplistwindow.cpp b/qmidiplayer-desktop/qmpplistwindow.cpp
index 75e2e44..5805fcb 100644
--- a/qmidiplayer-desktop/qmpplistwindow.cpp
+++ b/qmidiplayer-desktop/qmpplistwindow.cpp
@@ -149,7 +149,7 @@ void qmpPlistWindow::on_pbAdd_clicked()
if(qmpSettingsWindow::getSettingsIntf()->value("Behavior/DialogStatus","").toInt())
sl=QFileDialog::getOpenFileNames(this,"Add File",qmpSettingsWindow::getSettingsIntf()->value("DialogStatus/FileDialogPath","").toString(),"Midi files (*.mid *.midi)");
else
- sl=QFileDialog::getOpenFileNames(this,"Add File","","Midi files (*.mid *.midi)");
+ sl=QFileDialog::getOpenFileNames(this,"Add File","","Midi files (*.mid *.midi *.rmi)");
if(sl.empty())return;
for(int i=0;i<sl.size();++i)
{
@@ -166,7 +166,7 @@ void qmpPlistWindow::on_pbAddFolder_clicked()
while(di.hasNext())
{
QString c=di.next();
- if((c.endsWith(".mid")||c.endsWith(".midi"))&&fluid_is_midifile(c.toStdString().c_str()))
+ if((c.endsWith(".mid")||c.endsWith(".midi")||c.endsWith(".rmi"))&&fluid_is_midifile(c.toStdString().c_str()))
ui->lwFiles->addItem(new QListWidgetItem(c));
}
}
diff --git a/qmidiplayer-desktop/qmpplugin.cpp b/qmidiplayer-desktop/qmpplugin.cpp
index 5f5d82e..96936bd 100644
--- a/qmidiplayer-desktop/qmpplugin.cpp
+++ b/qmidiplayer-desktop/qmpplugin.cpp
@@ -31,6 +31,11 @@ void qmpPluginManager::scanPlugins()
if(!hso){fprintf(stderr,"Error while loading library: %d\n",GetLastError());continue;}
FARPROC hndi=GetProcAddress(hso,"qmpPluginGetInterface");
if(!hndi){fprintf(stderr,"file %s doesn't seem to be a qmidiplayer plugin.\n",cpluginpaths[i].c_str());continue;}
+ FARPROC hndiv=GetProcAddress(hso,"qmpPluginGetAPIRev");
+ if(!hndiv){fprintf(stderr,"file %s is incompatible with this version of qmidiplayer.\n",cpluginpaths[i].c_str());continue;}
+ qmpPluginAPIRevEntry getv=(qmpPluginAPIRevEntry)hndiv;
+ if(strcmp(getv(),QMP_PLUGIN_API_REV))
+ {fprintf(stderr,"file %s is incompatible with this version of qmidiplayer.\n",cpluginpaths[i].c_str());continue;}
qmpPluginEntry e=(qmpPluginEntry)hndi;
qmpPluginIntf* intf=e(pluginAPI);
plugins.push_back(qmpPlugin(std::string(intf->pluginGetName()),std::string(intf->pluginGetVersion()),std::string(cpluginpaths[i]),intf));
@@ -65,6 +70,11 @@ void qmpPluginManager::scanPlugins()
if(!hso){fprintf(stderr,"%s\n",dlerror());continue;}
void* hndi=dlsym(hso,"qmpPluginGetInterface");
if(!hndi){fprintf(stderr,"file %s doesn't seem to be a qmidiplayer plugin.\n",cpluginpaths[i].c_str());continue;}
+ void* hndiv=dlsym(hso,"qmpPluginGetAPIRev");
+ if(!hndiv){fprintf(stderr,"file %s is incompatible with this version of qmidiplayer.\n",cpluginpaths[i].c_str());continue;}
+ qmpPluginAPIRevEntry getv=(qmpPluginAPIRevEntry)hndiv;
+ if(strcmp(getv(),QMP_PLUGIN_API_REV))
+ {fprintf(stderr,"file %s is incompatible with this version of qmidiplayer.\n",cpluginpaths[i].c_str());continue;}
qmpPluginEntry e=(qmpPluginEntry)hndi;
qmpPluginIntf* intf=e(pluginAPI);
plugins.push_back(qmpPlugin(std::string(intf->pluginGetName()),std::string(intf->pluginGetVersion()),std::string(cpluginpaths[i]),intf));
diff --git a/qmidiplayer-desktop/qmpplugin.hpp b/qmidiplayer-desktop/qmpplugin.hpp
index d69adeb..512fc35 100644
--- a/qmidiplayer-desktop/qmpplugin.hpp
+++ b/qmidiplayer-desktop/qmpplugin.hpp
@@ -3,6 +3,7 @@
#define QMP_MAIN
#include <string>
#include <vector>
+#define QMP_MAIN
#include "../include/qmpcorepublic.hpp"
struct qmpPlugin
{
diff --git a/sample-plugin/sampleplugin.hpp b/sample-plugin/sampleplugin.hpp
index a47e037..ffb3d77 100644
--- a/sample-plugin/sampleplugin.hpp
+++ b/sample-plugin/sampleplugin.hpp
@@ -19,6 +19,8 @@ class qmpSamplePlugin:public qmpPluginIntf
extern "C"{
EXPORTSYM qmpPluginIntf* qmpPluginGetInterface(qmpPluginAPI* api)
{return new qmpSamplePlugin(api);}
+ EXPORTSYM const char* qmpPluginGetAPIRev()
+ {return QMP_PLUGIN_API_REV;}
}
#endif // SAMPLEPLUGIN_H
diff --git a/visualization/qmpvisualization.hpp b/visualization/qmpvisualization.hpp
index 6a66785..28e9b52 100644
--- a/visualization/qmpvisualization.hpp
+++ b/visualization/qmpvisualization.hpp
@@ -133,6 +133,8 @@ class CDemoVisualization:public qmpVisualizationIntf
extern "C"{
EXPORTSYM qmpPluginIntf* qmpPluginGetInterface(qmpPluginAPI* api)
{return new qmpVisualization(api);}
+ EXPORTSYM const char* qmpPluginGetAPIRev()
+ {return QMP_PLUGIN_API_REV;}
}
#endif // QMPVISUALIZATION_H