NiCMidi 1.1.0
A MIDI library derived from J.D.Koftinoff jdksmidi
|
If we want to play the contents of a MIDIMultiTrack we must send them to an hardware MIDI port with accurate timing.
As said in previous sections, this can easily be done by the AdvancedSequencer object. This section is a more in-depth examination of how this is done by mean of some library classes.
The MIDITimer is a static class that provides MIDI timing. This is accomplished by starting a background thread that calls a user-defined callback function at a regular rate. The timing is provided by the C++ <std::chrono> classes, so you need to compile the library according to (at least) the C++ 0x11 standard; the default temporal resolution is 10 msecs, but you can change it with the MIDITimer::SetResolution() method.
When playing MIDI, the timer is usually controlled by the MIDIManager class (described later), so it is rarely necessary to use its methods directly. You may find useful its MIDITimer::Wait() method (which waits a certain number of milliseconds) or the MIDITimer::GetSysTimeMs() method (which returns the absolute time in milliseconds from the class instantiation).
The MIDIOutDriver and MIDIInDriver classes are objects which communicate between the library software and the hardware MIDI ports regardless the underlying OS; NiCMidi uses the RTMidi library of Gary Scavone (see http://www.music.mcgill.ca/~gary/rtmidi/) to have a common interface for all OS. Every port has an Id number and a readable name (given by the OS).
If you want to send (or receive) MIDI messages to (from) a port you must open it with the MIDIOutDriver::OpenPort() or MIDIInDriver::OpenPort() methods. The MIDIOutDriver has a method MIDIOutDriver::OutputMessage() which sends a MIDITimedMessage to the port; the MIDIInDriver is a bit more complicated, because it manages a queue for incoming messages, and you have various methods for inspecting them (see the class documentation for details).
However, when using the high-level objects of the library (such as AdvancedSequencer, MIDISequencer, MIDIRecorder, etc.), all the work (opening and closing ports, sending/receiving messages ...) is done by these classes, so even in this case the user rarely needs to use the methods of this class directly.
The MIDIManager is a static class that handles the temporized communications between the software and the hardware MIDI ports.
This modular design allows the user to stack many components which all share the same timing (for example a sequencer, a metronome, a recorder ...) and encourages him to develop its own objects.
The core of MIDI timed playback is the pure virtual MIDITickComponent class: this is the prototype for all objects that have a callback procedure to be called at every tick of the MIDITimer; the AdvancedSequencer, MIDISequencer, MIDIThru, Metronome and MIDIRecorder classes all inherit from it. Let us analyze its most important methods:
Due to the difficulty of calling member functions as callbacks in C++ this class actually needs two methods (one static and one member) to implement the callback mechanism:
This is an example of the usage of the Metronome:
In the AdvancedSequencer class the Start() method is aliased with Play(), following the ordinary naming conventions in a sequencer.
More detailed examples of the usage of the derived classes are in the following files: