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 paththat 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.
