From b693d07fcb2cd1f228591a4ce85738029c0ab1fb Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 25 Jun 2024 22:03:00 -0600 Subject: [PATCH] Add a new ::render() method to MidiSource that writes to an EventSink --- libs/ardour/ardour/midi_source.h | 1 + libs/ardour/ardour/smf_source.h | 2 ++ libs/ardour/smf_source.cc | 56 ++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index 2631094473..e28ef43e5e 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -109,6 +109,7 @@ class LIBARDOUR_API MidiSource : virtual public Source MidiNoteTracker* tracker, MidiChannelFilter* filter, const std::set& filtered); + virtual void render (const ReaderLock& lock, Evoral::EventSink& dst) = 0; /** Write data from a MidiRingBuffer to this source. * @param lock Reference to the Mutex to lock before modification diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index c7309cad52..02994f55ea 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -79,6 +79,8 @@ public: /** Query the smf file for its channel info */ SMF::UsedChannels used_midi_channels(); + void render (const ReaderLock& lock, Evoral::EventSink& dst); + protected: void close (); void flush_midi (const WriterLock& lock); diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index f761cd2133..71051b20e4 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -217,6 +217,62 @@ SMFSource::close () extern PBD::Timing minsert; +void +SMFSource::render (const ReaderLock& lock, Evoral::EventSink& destination) +{ + int ret = 0; + Temporal::Beats time; // in SMF ticks, 1 tick per _ppqn + + if (writable() && !_open) { + /* nothing to read since nothing has ben written */ + return; + } + + // Output parameters for read_event (which will allocate scratch in buffer as needed) + uint32_t ev_delta_t = 0; + uint32_t ev_size = 0; + uint8_t* ev_buffer = 0; + size_t scratch_size = 0; // keep track of scratch to minimize reallocs + + /* start of read in SMF ticks (which may differ from our own musical ticks */ + + Evoral::SMF::seek_to_start(); + + while (true) { + + Evoral::event_id_t ignored; /* XXX don't ignore note id's ??*/ + + ret = read_event (&ev_delta_t, &ev_size, &ev_buffer, &ignored); + + if (ret == -1) { // EOF + break; + } + + if (ret == 0) { // meta-event (skipped, just accumulate time) + continue; + } + + time += Temporal::Beats::ticks_at_rate (ev_delta_t, ppqn()); // accumulate delta time + + DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF render delta %1, time %2, buf[0] %3\n", + ev_delta_t, time, ev_buffer[0])); + + /* Note that we add on the source start time (in session samples) here so that ev_sample_time + is in session samples. + */ + destination.write (time, Evoral::MIDI_EVENT, ev_size, ev_buffer); + + if (ev_size > scratch_size) { + scratch_size = ev_size; + } + + ev_size = scratch_size; // ensure read_event only allocates if necessary + } + + _smf_last_read_end = time; + _smf_last_read_time = time; +} + timecnt_t SMFSource::read_unlocked (const ReaderLock& lock, Evoral::EventSink& destination,