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.
This commit is contained in:
parent
7644168536
commit
9b070eefb1
@ -48,7 +48,8 @@ class LIBARDOUR_API AudioTrack : public Track
|
||||
boost::shared_ptr<Region> bounce_range (samplepos_t start, samplepos_t end, InterThreadInfo&,
|
||||
boost::shared_ptr<Processor> endpoint, bool include_endpoint);
|
||||
int export_stuff (BufferSet& bufs, samplepos_t start_sample, samplecnt_t nframes,
|
||||
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze);
|
||||
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze,
|
||||
MidiStateTracker&);
|
||||
|
||||
int set_state (const XMLNode&, int version);
|
||||
|
||||
|
@ -87,16 +87,16 @@ public:
|
||||
return boost::shared_ptr<Region> ();
|
||||
}
|
||||
|
||||
int export_stuff (BufferSet&, samplepos_t, samplecnt_t, boost::shared_ptr<Processor>, bool, bool, bool) { return -1; }
|
||||
int export_stuff (BufferSet&, samplepos_t, samplecnt_t, boost::shared_ptr<Processor>, 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<AudioRegion> the_region;
|
||||
boost::shared_ptr<MidiRegion> midi_region;
|
||||
samplepos_t current_sample;
|
||||
|
@ -66,7 +66,8 @@ public:
|
||||
boost::shared_ptr<Processor> endpoint,
|
||||
bool include_endpoint,
|
||||
bool for_export,
|
||||
bool for_freeze);
|
||||
bool for_freeze,
|
||||
MidiStateTracker& tracker);
|
||||
|
||||
int set_state (const XMLNode&, int version);
|
||||
|
||||
|
@ -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<Region> bounce_range (samplepos_t start, samplepos_t end, InterThreadInfo& itt,
|
||||
boost::shared_ptr<Processor> endpoint, bool include_endpoint) = 0;
|
||||
virtual int export_stuff (BufferSet& bufs, samplepos_t start_sample, samplecnt_t nframes,
|
||||
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze) = 0;
|
||||
boost::shared_ptr<Processor> 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&);
|
||||
|
@ -251,7 +251,8 @@ AudioTrack::set_state_part_two ()
|
||||
|
||||
int
|
||||
AudioTrack::export_stuff (BufferSet& buffers, samplepos_t start, samplecnt_t nframes,
|
||||
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze)
|
||||
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze,
|
||||
MidiStateTracker& /* ignored, this is audio */)
|
||||
{
|
||||
boost::scoped_array<gain_t> gain_buffer (new gain_t[nframes]);
|
||||
boost::scoped_array<Sample> mix_buffer (new Sample[nframes]);
|
||||
|
@ -482,7 +482,8 @@ MidiTrack::export_stuff (BufferSet& buffers,
|
||||
boost::shared_ptr<Processor> 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;
|
||||
}
|
||||
|
||||
|
@ -5655,6 +5655,14 @@ Session::freeze_all (InterThreadInfo& itt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct MidiSourceLockMap
|
||||
{
|
||||
boost::shared_ptr<MidiSource> src;
|
||||
Source::Lock lock;
|
||||
|
||||
MidiSourceLockMap (boost::shared_ptr<MidiSource> midi_source) : src (midi_source), lock (src->mutex()) {}
|
||||
};
|
||||
|
||||
boost::shared_ptr<Region>
|
||||
Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
|
||||
bool /*overwrite*/, vector<boost::shared_ptr<Source> >& 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<MidiSourceLockMap*> 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<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
|
||||
|
||||
boost::shared_ptr<MidiSource> ms = boost::dynamic_pointer_cast<MidiSource>(*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<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
|
||||
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
|
||||
boost::shared_ptr<MidiSource> ms;
|
||||
if (afs) {
|
||||
afs->prepare_for_peakfile_writes ();
|
||||
} else if ((ms = boost::dynamic_pointer_cast<MidiSource>(*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<MidiSource>(*src))) {
|
||||
Source::Lock lock(ms->mutex());
|
||||
}
|
||||
}
|
||||
|
||||
for (vector<MidiSourceLockMap*>::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<samplepos_t> 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<MidiSourceLockMap*>::iterator m = midi_source_locks.begin(); m != midi_source_locks.end(); ++m) {
|
||||
|
||||
for (MidiBuffer::iterator i = resolved.begin(); i != resolved.end(); ++i) {
|
||||
Evoral::Event<samplepos_t> 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<MidiSourceLockMap*>::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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user