MIDI Data Handling
MIDI Sources
The only instantiable data type that can be used a MIDI source
is SMFSource
. This inherits from 3 direct parent
classes:
- FileSource
- This provides concepts like a
path
that identifies a file) - MidiSource
- Provides and defines a set of generic methods to read and write MIDI data somewhere
- SMF
- An object that abstracts a Standard MIDI file, and provides methods to read and write MIDI data in that format. It wraps libsmf, a C library that implements the SMF specifications.
In addition, an SMFSource HAS-A
MidiModel, which is an
object used for editing MIDI. It provides methods to add, remove,
move and alter MIDI events. This is the object that is modified when
using the GUI to edit MIDI. It does not automatically flush its
state to disk, but will do so
(via MidiModel::sync_to_source()
) when the session is
saved. Reading data from an SMFSource that has a non-null MidiModel
will pull information from the model, not the file on disk. So think
of the MidiModel as a cache of the disk data, but specifically
designed to allow for editing operations.
Capturing MIDI
When MIDI data is received during a process callback, it is written to
in a ringbuffer of suitable size (we hope). When the disk writer
thread calls flush_midi()
, it reads from the ringbuffer
and adds events to an SMFSource object that already exists. The
SMFSource object will use the SMF/libsmf API(s) to get the data
ready for being written to disk, and will also (if
its _model
member is non-null) append the events to
a MidiModel
.
The data is not written to disk until the recording pass finishes,
and mark_midi_streaming_write_finished()
is called. At
this point, the total intended length of the SMF will be passed as
an argument, and the libsmf API will be used
(notably smf_save()
) to write the SMF contents to
disk. The SMF "track-end" meta-event will record the intended
length.
If the length passed to mark_midi_streaming_write_finished()
is std::numeric_limits
then the track-end
meta-event will be timed to match the final event in the SMF.
Note that at the time of writing (November 2024), after a capture
pass, the MIDI will be written to disk twice. Once
as mark_midi_streaming_write_finished()
is called, and
then again when Session::save_state()
is called. The
latter will be written from the state of MidiModel
, so
it is important than the capture process ensures that the model
fully represents the state written by the initial flush-to-disk.