From 9b070eefb106c25032d6e077090d9f8a959aefc4 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 4 May 2020 18:48:01 -0600 Subject: fix note-tracking in Editor::write_one_track() We need a MidiStateTracker to determine notes whose end is not reached during the call to ::write_one_track(), so that we can resolve them in the output (SMF) source. This required some changes to the ::export_stuff() API for tracks. In addition, we now take the source "lock" just once during ::write_one_track() rather than every time we write. This isn't an integral part of the note tracking, but fell out along the way. Finally, note that although we use a vector to handle MIDI "sources" here, it is expected that there is only 1 MIDI source at present. Leave vectors in place since it is possible that ::write_one_track() could be modified in the future to change that. --- libs/ardour/ardour/audio_track.h | 3 +- libs/ardour/ardour/auditioner.h | 6 ++-- libs/ardour/ardour/midi_track.h | 3 +- libs/ardour/ardour/track.h | 4 ++- libs/ardour/audio_track.cc | 3 +- libs/ardour/midi_track.cc | 17 +++++++---- libs/ardour/session.cc | 64 +++++++++++++++++++++++++++++++++------- 7 files changed, 78 insertions(+), 22 deletions(-) (limited to 'libs/ardour') diff --git a/libs/ardour/ardour/audio_track.h b/libs/ardour/ardour/audio_track.h index 55def29b99..6b8f01986c 100644 --- a/libs/ardour/ardour/audio_track.h +++ b/libs/ardour/ardour/audio_track.h @@ -48,7 +48,8 @@ class LIBARDOUR_API AudioTrack : public Track boost::shared_ptr bounce_range (samplepos_t start, samplepos_t end, InterThreadInfo&, boost::shared_ptr endpoint, bool include_endpoint); int export_stuff (BufferSet& bufs, samplepos_t start_sample, samplecnt_t nframes, - boost::shared_ptr endpoint, bool include_endpoint, bool for_export, bool for_freeze); + boost::shared_ptr endpoint, bool include_endpoint, bool for_export, bool for_freeze, + MidiStateTracker&); int set_state (const XMLNode&, int version); diff --git a/libs/ardour/ardour/auditioner.h b/libs/ardour/ardour/auditioner.h index 6ca51fc0d6..2ad2fba96d 100644 --- a/libs/ardour/ardour/auditioner.h +++ b/libs/ardour/ardour/auditioner.h @@ -87,16 +87,16 @@ public: return boost::shared_ptr (); } - int export_stuff (BufferSet&, samplepos_t, samplecnt_t, boost::shared_ptr, bool, bool, bool) { return -1; } + int export_stuff (BufferSet&, samplepos_t, samplecnt_t, boost::shared_ptr, bool, bool, bool, MidiStateTracker&) { return -1; } void set_audition_synth_info(PluginInfoPtr in) { audition_synth_info = in; } samplecnt_t output_latency () const { return 0; } private: - + PluginInfoPtr audition_synth_info; //we will use this to create a new synth on-the-fly each time an audition is requested - + boost::shared_ptr the_region; boost::shared_ptr midi_region; samplepos_t current_sample; diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 5a1c1aadc4..6e98267b26 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -66,7 +66,8 @@ public: boost::shared_ptr endpoint, bool include_endpoint, bool for_export, - bool for_freeze); + bool for_freeze, + MidiStateTracker& tracker); int set_state (const XMLNode&, int version); diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index bb6b0dca37..3da9902c13 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -42,6 +42,7 @@ class DiskWriter; class IO; class RecordEnableControl; class RecordSafeControl; +class MidiStateTracker; /** A track is an route (bus) with a recordable diskstream and * related objects relevant to recording, playback and editing. @@ -110,7 +111,8 @@ public: virtual boost::shared_ptr bounce_range (samplepos_t start, samplepos_t end, InterThreadInfo& itt, boost::shared_ptr endpoint, bool include_endpoint) = 0; virtual int export_stuff (BufferSet& bufs, samplepos_t start_sample, samplecnt_t nframes, - boost::shared_ptr endpoint, bool include_endpoint, bool for_export, bool for_freeze) = 0; + boost::shared_ptr endpoint, bool include_endpoint, bool for_export, bool for_freeze, + MidiStateTracker&) = 0; virtual int set_state (const XMLNode&, int version); static void zero_diskstream_id_in_xml (XMLNode&); diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 74ecaeb6c3..1b0ed67ffd 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -251,7 +251,8 @@ AudioTrack::set_state_part_two () int AudioTrack::export_stuff (BufferSet& buffers, samplepos_t start, samplecnt_t nframes, - boost::shared_ptr endpoint, bool include_endpoint, bool for_export, bool for_freeze) + boost::shared_ptr endpoint, bool include_endpoint, bool for_export, bool for_freeze, + MidiStateTracker& /* ignored, this is audio */) { boost::scoped_array gain_buffer (new gain_t[nframes]); boost::scoped_array mix_buffer (new Sample[nframes]); diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 346ceff5a1..2e7d063048 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -482,7 +482,8 @@ MidiTrack::export_stuff (BufferSet& buffers, boost::shared_ptr endpoint, bool include_endpoint, bool for_export, - bool for_freeze) + bool for_freeze, + MidiStateTracker& tracker) { if (buffers.count().n_midi() == 0) { return -1; @@ -498,18 +499,18 @@ MidiTrack::export_stuff (BufferSet& buffers, buffers.get_midi(0).clear(); - /* Can't use a note tracker here, because the note off's might be in a * subsequent call */ MidiStateTracker ignored; - /* XXX thsi doesn't fail, other than if the lock cannot be obtained */ - mpl->rendered()->read(buffers.get_midi(0), start, start+nframes, ignored, start); + /* XXX this doesn't fail, other than if the lock cannot be obtained */ + mpl->rendered()->read (buffers.get_midi(0), start, start+nframes, ignored, start); + + MidiBuffer& buf = buffers.get_midi(0); if (endpoint && !for_export) { - MidiBuffer& buf = buffers.get_midi(0); for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { MidiBuffer::TimeType *t = i.timeptr (); *t -= start; @@ -518,6 +519,12 @@ MidiTrack::export_stuff (BufferSet& buffers, } mpl->reset_note_trackers (); + /* Add to tracker so that we can resolve at the end of the export (in Session::write_one_track()) */ + + for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { + tracker.track (*i); + } + return 0; } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 690b318035..3908fa755e 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -5655,6 +5655,14 @@ Session::freeze_all (InterThreadInfo& itt) return 0; } +struct MidiSourceLockMap +{ + boost::shared_ptr src; + Source::Lock lock; + + MidiSourceLockMap (boost::shared_ptr midi_source) : src (midi_source), lock (src->mutex()) {} +}; + boost::shared_ptr Session::write_one_track (Track& track, samplepos_t start, samplepos_t end, bool /*overwrite*/, vector >& srcs, @@ -5676,8 +5684,10 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end, ChanCount const max_proc = track.max_processor_streams (); string legal_playlist_name; string possible_path; - + MidiBuffer resolved (256); + MidiStateTracker tracker; DataType data_type = track.data_type(); + std::vector midi_source_locks; if (end <= start) { error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"), @@ -5761,14 +5771,25 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end, } buffers.set_count (max_proc); + + /* prepare MIDI files */ + + for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { + + boost::shared_ptr ms = boost::dynamic_pointer_cast(*src); + + if (ms) { + midi_source_locks.push_back (new MidiSourceLockMap (ms)); + ms->mark_streaming_write_started (midi_source_locks.back()->lock); + } + } + + /* prepare audio files */ + for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - boost::shared_ptr ms; if (afs) { afs->prepare_for_peakfile_writes (); - } else if ((ms = boost::dynamic_pointer_cast(*src))) { - Source::Lock lock(ms->mutex()); - ms->mark_streaming_write_started(lock); } } @@ -5776,7 +5797,7 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end, this_chunk = min (to_do, bounce_chunk_size); - if (track.export_stuff (buffers, start, this_chunk, endpoint, include_endpoint, for_export, for_freeze)) { + if (track.export_stuff (buffers, start, this_chunk, endpoint, include_endpoint, for_export, for_freeze, tracker)) { goto out; } @@ -5800,22 +5821,45 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end, if (afs->write (buffers.get_audio(n).data(latency_skip), current_chunk) != current_chunk) { goto out; } - } else if ((ms = boost::dynamic_pointer_cast(*src))) { - Source::Lock lock(ms->mutex()); + } + } + for (vector::iterator m = midi_source_locks.begin(); m != midi_source_locks.end(); ++m) { const MidiBuffer& buf = buffers.get_midi(0); for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) { Evoral::Event ev = *i; if (!endpoint || for_export) { ev.set_time(ev.time() - position); } - ms->append_event_samples(lock, ev, ms->natural_position()); + (*m)->src->append_event_samples ((*m)->lock, ev, (*m)->src->natural_position()); } - } } latency_skip = 0; } + tracker.resolve_notes (resolved, end-1); + + if (!resolved.empty()) { + + for (vector::iterator m = midi_source_locks.begin(); m != midi_source_locks.end(); ++m) { + + for (MidiBuffer::iterator i = resolved.begin(); i != resolved.end(); ++i) { + Evoral::Event ev = *i; + if (!endpoint || for_export) { + ev.set_time(ev.time() - position); + } + (*m)->src->append_event_samples ((*m)->lock, ev, (*m)->src->natural_position()); + } + } + } + + for (vector::iterator m = midi_source_locks.begin(); m != midi_source_locks.end(); ++m) { + delete *m; + } + + midi_source_locks.clear (); + + /* post-roll, pick up delayed processor output */ latency_skip = track.bounce_get_latency (endpoint, include_endpoint, for_export, for_freeze); -- cgit v1.2.3