summaryrefslogtreecommitdiff
path: root/QMidiPlayer/doc/APIdoc.md
diff options
context:
space:
mode:
Diffstat (limited to 'QMidiPlayer/doc/APIdoc.md')
-rw-r--r--QMidiPlayer/doc/APIdoc.md217
1 files changed, 217 insertions, 0 deletions
diff --git a/QMidiPlayer/doc/APIdoc.md b/QMidiPlayer/doc/APIdoc.md
new file mode 100644
index 0000000..be7bbc5
--- /dev/null
+++ b/QMidiPlayer/doc/APIdoc.md
@@ -0,0 +1,217 @@
+# The API documentation of QMidiPlayer for plugin developers
+
+*This manual is not yet complete. It's only a working draft for the always-changing plugin system in QMP.*
+*Handle with care.*
+
+# 0. Overview
+
+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
+the source tree. It includes classes used by QMidiPlayer's internal plugin infrastructure.
+
+# 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 symbols `qmpPluginGetInterface` and `qmpPluginGetAPIRev`. Specifically, what you should do is to add
+the following snipplet to somewhere of your code:
+
+```C++
+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
+plugin class should derive from. The parameter api provides access to QMidiPlayer's plugin API, which should be stored
+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`.
+
+# 3. A peek into the class `qmpPluginIntf`
+
+It has 6 public members: one default constructor, one default destructor and four methods:
+
+- `void init()`
+ Called on start up if the plugin is loaded successfully and enabled.
+- `void deinit()`
+ Called on shutdown if the plugin is enabled.
+- `const char* pluginGetName()`
+ This function should return the display name of the plugin.
+- `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 (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 host 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. And with the introduction of the general functionality API, you can now virtually add anything to QMidiPlayer!
+
+## 4.1 What is a functionality?
+Have a look at the main window. By default there're three or four buttons at the bottom of it.
+These are functionalities. Functionalties go into two types: checkable and non-checkable.
+Checkable functionalities can be toggled on or off, while non-checkable functionalities have
+no such states. For non-checkable functionalities, only show() is called when the user invokes it.
+The user can arrange functionalities shown on the toolbar and the action menu to their needs.
+
+## 4.2 Visualization
+Visualization was once a feature of QMidiPlayer's core. But you can now write your own visualization with the
+Visualization interface(`qmpVisualizationIntf`). The methods in this interface should be self-explanatory.
+
+## 4.3 MIDI File Reader
+This is not strictly a "functionality", because its interface IMidiFileReader does not inherit qmpFuncBaseIntf.
+When the user requests to open a file, the core tries to load the file with registered file readers and
+accepts the first valid result. Therefore you can implement your own file reader, which may even add
+eXtended Module support to QMidiPlayer!
+
+## 4.4 MIDI Output Device
+This is not a functionality either. By implementing the interface qmpMidiOutDevice and registering it, you can
+add custom MIDI Devices in the built-in MIDI mapper.
+
+# 5. Generic Considerations
+
+1. If you implemented a API that returns a pointer to something, you can forget about the pointer after returning
+it. The core will free its memory after it is no longer used. You shouldn't extend the class pointed to because
+if you do so, the core will not be able to destruct it correctly. Examples include IMidiFileReader::readFile which
+returns a pointer to a CMidiFile class.
+2. However if you passed a pointer to the core through a function in qmpPluginAPI, you cannot forget about the pointer
+later. As these pointers are mostly polymorphic, the core cannot handle their destruction. You have to delete them
+yourself in the deinit() function of your plugin.
+3. Do not throw exceptions to the core. The core doesn't handle exceptions and they will crash the entire program.
+Use the return value to indicate failure of a procedure instead.
+
+# 6. Reference
+Well, everything above is just nonsense compared to this one. The full reference of the API is here.
+
+## Structures & Classes
+
+### struct `SEvent`
+Describes an MIDI event.
+
+- `uint32_t iid`
+internal id. Usually set to the size of the event pool when current event is read.
+- `uint32_t time`
+timestamp of the event in MIDI tick.
+- `uint32_t p1`
+parameter 1 for the midi event.
+- `uint32_t p2`
+parameter 2 for the midi event.
+- `uint8_t type`
+type of the event together with the channel this event goes to.
+- `std::string str`
+Contains the raw data for string-like events.
+- default constructor: `SEvent()`
+sets everything to zero or empty.
+- constructor with parameters: `SEvent(uint32_t _iid,uint32_t _t,char _tp,uint32_t _p1,uint32_t _p2,const char* s=NULL)`
+fills the event with the parameters given.
+- `friend bool operator <(const SEvent& a,const SEvent& b)`
+compares events by their timestamps. Ties are broken by comparing precedence in file.
+
+### struct `SEventCallBackData`
+A stripped down version of SEvent that is used to pass event data in APIs.
+
+- `uint32_t time`
+- `uint32_t type`
+- `uint32_t p1`
+- `uint32_t p2`
+
+### class `CMidiTrack`
+Describes a single MIDI track. A MIDI track consists of multiple MIDI events.
+
+- `std::vector<SEvent> eventList`
+Vector of SEvent's.
+- `void appendEvent(SEvent e)`
+Append an event to the end of the event list.
+- `SEvent& operator[](size_t sub)`
+Get the reference to the contained event with the given index.
+
+### class `CMidiFile`
+Describes a MIDI file. A MIDI file consists of multiple MIDI tracks.
+
+- `bool valid`
+Is the MIDI file a valid one?
+- `char* title`
+Title of the MIDI file.
+- `char* copyright`
+Copyright information of the MIDI file.
+- `std::vector<CMidiTrack> tracks`
+Tracks in the MIDI file.
+- `uint32_t std`
+File standard of the MIDI file.
+ - 0: unknown
+ - 1: GM Level 1
+ - 2: GM Level 2
+ - 3: GS
+ - 4: XG
+- `uint32_t div`
+Ticks per quarter note. SMTPE format is not supported by QMidiPlayer.
+- `~CMidiFile()`
+Frees memory occupied by the title and copyright string.
+
+### class `IMidiCallBack`
+Generic callback function that can be used for hooking the core.
+
+- `IMidiCallBack()`
+Default empty constructor.
+- `virtual void callBack(void* callerdata,void* userdata)=0;`
+Abstract function of the callback. callerdata is filled by the caller and
+userdata is set to whatever you asked for when registering the callback.
+- `virtual ~IMidiCallBack()`
+Virtual empty destructor.
+
+### class `IMidiFileReader`
+MIDI file reader interface. Use this to implement your file importer.
+
+- `IMidiFileReader()`
+Default empty constructor.
+- `virtual ~IMidiFileReader()`
+Virtual empty destructor.
+- `virtual CMidiFile* readFile(const char* fn)=0`
+Abstract function to be implemented by the plugin.
+This function should read file from the given path (fn) and return a
+pointer to the resulting CMidiFile structure. You shoudn't handle the
+destruction of the resulting structure as the core will handle it.
+Read 5.1 for more details.
+After reading each event, you should call qmpPluginAPI::callEventReaderCB
+to invoke event reader callbacks and process their requests.
+If a file not supported by the reader is provided, this function should
+return a CMidiFile* whose `valid` field is `false`.
+- `virtual void discardCurrentEvent()=0`
+Only called by event reader callbacks.
+Expected behavior: Sets the discard flag for the last event you have read.
+If an event has its discard flag set, it shouldn't be pushed into its track.
+discardCurrentEvent may be called multiple times by different event reader
+callbacks. In such case, there's still only one event discarded.
+- `virtual void commitEventChange(SEventCallBackData d)=0`
+Only called by event reader callbacks.
+Expected behavior: modifies the last event you have read to the provided data.
+commitEventChange may be called multiple times by different event reader callbacks.
+In such case, only the last modification to the current event counts.