From ebcc6b825047091cd1b7954da0222104f0e159c8 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 24 Oct 2024 11:43:30 -0600 Subject: [PATCH] add the concept of a length that is data-independent to MIDI files (libs) --- libs/ardour/ardour/audiofilesource.h | 2 +- libs/ardour/ardour/audiosource.h | 2 +- libs/ardour/ardour/disk_writer.h | 2 +- libs/ardour/ardour/midi_source.h | 4 +-- libs/ardour/ardour/session.h | 2 +- libs/ardour/ardour/smf_source.h | 5 +-- libs/ardour/ardour/source.h | 7 +++- libs/ardour/ardour/track.h | 2 +- libs/ardour/ardour/triggerbox.h | 13 ++++---- libs/ardour/audiofilesource.cc | 4 +-- libs/ardour/audiosource.cc | 2 +- libs/ardour/disk_writer.cc | 34 +++++++------------ libs/ardour/import.cc | 19 ++--------- libs/ardour/luaproc.cc | 2 -- libs/ardour/midi_model.cc | 22 ++++++++----- libs/ardour/midi_source.cc | 8 ++--- libs/ardour/session.cc | 12 ++++--- libs/ardour/session_state.cc | 6 ++-- libs/ardour/smf_source.cc | 19 ++++++----- libs/ardour/track.cc | 4 +-- libs/ardour/triggerbox.cc | 49 ++++++++++++++++------------ libs/evoral/SMF.cc | 27 +++++++++++---- libs/evoral/Sequence.cc | 43 +++++++++++++++++++++++- libs/evoral/evoral/SMF.h | 3 ++ libs/evoral/evoral/Sequence.h | 7 ++++ 25 files changed, 182 insertions(+), 118 deletions(-) diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h index fac0f4313c..7632b172ba 100644 --- a/libs/ardour/ardour/audiofilesource.h +++ b/libs/ardour/ardour/audiofilesource.h @@ -60,7 +60,7 @@ public: virtual int update_header (samplepos_t when, struct tm&, time_t) = 0; virtual int flush_header () = 0; - void mark_streaming_write_completed (const WriterLock& lock); + void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration); int setup_peakfile (); void set_gain (float g, bool temporarily = false); diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 9ea6c25f60..4160118601 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -57,7 +57,7 @@ class LIBARDOUR_API AudioSource : virtual public Source, public ARDOUR::AudioRea virtual float sample_rate () const = 0; - virtual void mark_streaming_write_completed (const WriterLock& lock); + virtual void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration); virtual bool can_truncate_peaks() const { return true; } diff --git a/libs/ardour/ardour/disk_writer.h b/libs/ardour/ardour/disk_writer.h index 23f14e90db..9144275ebe 100644 --- a/libs/ardour/ardour/disk_writer.h +++ b/libs/ardour/ardour/disk_writer.h @@ -74,7 +74,7 @@ public: std::string steal_write_source_name (); int use_new_write_source (DataType, uint32_t n = 0); - void reset_write_sources (bool, bool force = false); + void reset_write_sources (); AlignStyle alignment_style () const { return _alignment_style; } void set_align_style (AlignStyle, bool force = false); diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index b00a16574b..6f5725cc85 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -138,7 +138,7 @@ class LIBARDOUR_API MidiSource : virtual public Source virtual void mark_streaming_midi_write_started (const WriterLock& lock, NoteMode mode); virtual void mark_streaming_write_started (const WriterLock& lock); - virtual void mark_streaming_write_completed (const WriterLock& lock); + virtual void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration); /** Mark write starting with the given time parameters. * @@ -160,7 +160,7 @@ class LIBARDOUR_API MidiSource : virtual public Source virtual void mark_midi_streaming_write_completed ( const WriterLock& lock, Evoral::Sequence::StuckNoteOption stuck_option, - Temporal::Beats when = Temporal::Beats()); + Temporal::timecnt_t const & when); virtual void session_saved(); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 42bd582e02..73822b8d95 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -1978,7 +1978,7 @@ public: ARDOUR::CueMarkers pending_source_markers; // source markers created while recording private: - void reset_write_sources (bool mark_write_complete, bool force = false); + void reset_write_sources (); SourceMap sources; int load_sources (const XMLNode& node); diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index 07ecb2c53a..19cfcc1bef 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -58,10 +58,11 @@ public: void update_length (timepos_t const & dur); void mark_streaming_midi_write_started (const WriterLock& lock, NoteMode mode); - void mark_streaming_write_completed (const WriterLock& lock); + + void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration); void mark_midi_streaming_write_completed (const WriterLock& lock, Evoral::Sequence::StuckNoteOption, - Temporal::Beats when = Temporal::Beats()); + Temporal::timecnt_t const & duration); XMLNode& get_state () const; int set_state (const XMLNode&, int version); diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index 087dbb861c..1770e70ad9 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -83,7 +83,12 @@ public: void mark_for_remove(); virtual void mark_streaming_write_started (const WriterLock& lock) {} - virtual void mark_streaming_write_completed (const WriterLock& lock) = 0; + /* The duration argument is ignored for audio data, where length is + implicitly given by the sample data. It matters for MIDI data, where + the file may be intended to be N bars long, but has no events that + occur at that duration. + */ + virtual void mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration) = 0; virtual void session_saved() {} diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index 650b910cb7..46c0061ec6 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -145,7 +145,7 @@ public: void ensure_input_monitoring (bool); std::list > & last_capture_sources (); std::string steal_write_source_name (); - void reset_write_sources (bool, bool force = false); + void reset_write_sources (); float playback_buffer_load () const; float capture_buffer_load () const; int do_refill (); diff --git a/libs/ardour/ardour/triggerbox.h b/libs/ardour/ardour/triggerbox.h index ece9a8d18b..cf1d67ad08 100644 --- a/libs/ardour/ardour/triggerbox.h +++ b/libs/ardour/ardour/triggerbox.h @@ -715,7 +715,7 @@ class LIBARDOUR_API TriggerBoxThread void set_region (TriggerBox&, uint32_t slot, std::shared_ptr); void request_delete_trigger (Trigger* t); - void request_build_source (Trigger* t); + void request_build_source (Trigger* t, Temporal::timecnt_t const & duration); void summon(); void stop(); @@ -743,6 +743,7 @@ class LIBARDOUR_API TriggerBoxThread std::shared_ptr region; /* for DeleteTrigger and BuildSourceAndRegion */ Trigger* trigger; + Temporal::timecnt_t duration; void* operator new (size_t); void operator delete (void* ptr, size_t); @@ -757,9 +758,9 @@ class LIBARDOUR_API TriggerBoxThread CrossThreadChannel _xthread; void queue_request (Request*); void delete_trigger (Trigger*); - void build_source (Trigger*); - void build_midi_source (MIDITrigger*); - void build_audio_source (AudioTrigger*); + void build_source (Trigger*, Temporal::timecnt_t const & duration); + void build_midi_source (MIDITrigger*, Temporal::timecnt_t const &); + void build_audio_source (AudioTrigger*, Temporal::timecnt_t const &); }; struct CueRecord { @@ -783,7 +784,7 @@ struct SlotArmInfo { samplepos_t start_samples; Temporal::Beats end_beats; samplepos_t end_samples; - Temporal::timecnt_t captured; + samplecnt_t captured; RTMidiBufferBeats* midi_buf; AudioTrigger::AudioData audio_buf; RubberBand::RubberBandStretcher* stretcher; @@ -942,7 +943,7 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro void dump (std::ostream &) const; - PBD::Signal Captured; + PBD::Signal Captured; private: struct Requests { diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index c3e83bfa13..e1c68d6852 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -249,13 +249,13 @@ AudioFileSource::set_state (const XMLNode& node, int version) } void -AudioFileSource::mark_streaming_write_completed (const WriterLock& lock) +AudioFileSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration) { if (!writable()) { return; } - AudioSource::mark_streaming_write_completed (lock); + AudioSource::mark_streaming_write_completed (lock, duration); } int diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index 970854610b..c67860064a 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -1150,7 +1150,7 @@ AudioSource::available_peaks (double zoom_factor) const } void -AudioSource::mark_streaming_write_completed (const WriterLock& lock) +AudioSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const &) { Glib::Threads::Mutex::Lock lm (_peaks_ready_lock); diff --git a/libs/ardour/disk_writer.cc b/libs/ardour/disk_writer.cc index c0e8607b32..79bcd8b2f4 100644 --- a/libs/ardour/disk_writer.cc +++ b/libs/ardour/disk_writer.cc @@ -123,7 +123,7 @@ DiskWriter::set_write_source_name (string const & str) { _write_source_name = str; - reset_write_sources (false); + reset_write_sources (); return true; } @@ -390,7 +390,7 @@ DiskWriter::set_state (const XMLNode& node, int version) node.get_property (X_("record-safe"), rec_safe); _record_safe.store (rec_safe); - reset_write_sources (false, true); + reset_write_sources (); return 0; } @@ -1072,7 +1072,7 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush) } void -DiskWriter::reset_write_sources (bool mark_write_complete, bool /*force*/) +DiskWriter::reset_write_sources () { std::shared_ptr c = channels.reader(); uint32_t n = 0; @@ -1087,12 +1087,6 @@ DiskWriter::reset_write_sources (bool mark_write_complete, bool /*force*/) if (chan->write_source) { - if (mark_write_complete) { - Source::WriterLock lock(chan->write_source->mutex()); - chan->write_source->mark_streaming_write_completed (lock); - chan->write_source->done_with_peakfile_writes (); - } - if (chan->write_source->removable()) { chan->write_source->mark_for_remove (); chan->write_source->drop_references (); @@ -1108,13 +1102,6 @@ DiskWriter::reset_write_sources (bool mark_write_complete, bool /*force*/) } } - if (_midi_write_source) { - if (mark_write_complete) { - Source::WriterLock lm(_midi_write_source->mutex()); - _midi_write_source->mark_streaming_write_completed (lm); - } - } - if (_playlists[DataType::MIDI]) { use_new_write_source (DataType::MIDI); } @@ -1186,7 +1173,6 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo ChannelList::const_iterator chan; std::shared_ptr c = channels.reader(); uint32_t n = 0; - bool mark_write_completed = false; finish_capture (c); @@ -1315,7 +1301,11 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo total_capture += timecnt_t ((*ci)->samples); } - _midi_write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence::ResolveStuckNotes, total_capture.beats()); + /* XXX we need to consider snapping the duration up to the next + * beat/bar here + */ + + _midi_write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence::ResolveStuckNotes, timecnt_t (total_capture)); } _last_capture_sources.insert (_last_capture_sources.end(), audio_srcs.begin(), audio_srcs.end()); @@ -1325,10 +1315,8 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo _track.use_captured_sources (audio_srcs, capture_info); _track.use_captured_sources (midi_srcs, capture_info); - mark_write_completed = true; - out: - reset_write_sources (mark_write_completed); + reset_write_sources (); for (vector::iterator ci = capture_info.begin(); ci != capture_info.end(); ++ci) { delete *ci; @@ -1457,7 +1445,7 @@ DiskWriter::configure_io (ChanCount in, ChanCount out) } if (record_enabled() || changed) { - reset_write_sources (false, true); + reset_write_sources (); } return true; @@ -1472,7 +1460,7 @@ DiskWriter::use_playlist (DataType dt, std::shared_ptr playlist) return -1; } if (reset_ws) { - reset_write_sources (false, true); + reset_write_sources (); } return 0; } diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 1a73351d0e..7232bc158a 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -134,7 +134,7 @@ open_importable_source (const string& path, samplecnt_t samplerate, ARDOUR::SrcQ } /* rewrap as a resampled source */ - return std::shared_ptr(new ResampledImportableSource(source, samplerate, quality)); + return std::shared_ptr(new ResampledImportableSource(source, samplerate, quality)); } catch (...) { } throw failed_constructor (); @@ -464,18 +464,7 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status, /* we wrote something */ - /* try to guess at the meter, for 5/4 midi loop oddballs */ - int pulses_per_bar = 4; - Evoral::SMF::Tempo *tempo = source->nth_tempo (0); - if (tempo && (tempo->numerator>0) ) { - pulses_per_bar = tempo->numerator; - } - - /* extend the length of the region to the end of a bar */ - const Temporal::Beats length_beats = Temporal::Beats::ticks_at_rate(t, source->ppqn()); - smfs->update_length (timepos_t (length_beats.round_up_to_multiple(Temporal::Beats(pulses_per_bar,0)))); - - smfs->mark_streaming_write_completed (source_lock); + smfs->mark_streaming_write_completed (source_lock, timecnt_t (source->duration())); /* the streaming write that we've just finished * only wrote data to the SMF object, which is @@ -486,12 +475,10 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status, smfs->load_model (source_lock, true); - /* Now that there is a model, we can set interpolation of parameters. */ - smfs->mark_streaming_write_completed (source_lock); - if (status.cancel) { break; } + } else { info << string_compose (_("Track %1 of %2 contained no usable MIDI data"), i, total_files) << endmsg; } diff --git a/libs/ardour/luaproc.cc b/libs/ardour/luaproc.cc index 9bc69bb4de..859d549e58 100644 --- a/libs/ardour/luaproc.cc +++ b/libs/ardour/luaproc.cc @@ -1165,7 +1165,6 @@ LuaProc::parse_scale_points (luabridge::LuaRef* lr) return std::shared_ptr (); } - int cnt = 0; std::shared_ptr rv = std::shared_ptr(new ScalePoints()); luabridge::LuaRef scalepoints ((*lr)["scalepoints"]); @@ -1174,7 +1173,6 @@ LuaProc::parse_scale_points (luabridge::LuaRef* lr) if (!i.value ().isNumber ()) { continue; } rv->insert(make_pair(i.key ().cast (), i.value ().cast ())); - ++cnt; } if (rv->size() > 0) { diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index c35ab34291..fc016a67c6 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -1244,15 +1244,15 @@ MidiModel::write_to (std::shared_ptr source, { ReadLock lock (read_lock()); /* Sequence read-lock */ - source->drop_model(source_lock); + source->drop_model (source_lock); /* as of March 2022 or long before , the note mode argument does nothing */ source->mark_streaming_midi_write_started (source_lock, Sustained); for (Evoral::Sequence::const_iterator i = begin(TimeType(), true); i != end(); ++i) { - source->append_event_beats(source_lock, *i); + source->append_event_beats (source_lock, *i); } - source->mark_streaming_write_completed(source_lock); + source->mark_streaming_write_completed (source_lock, timecnt_t (duration())); /* no call to set_edited() because writing to "newsrc" doesn't remove * the need to write to "our own" source in ::sync_to_source() @@ -1284,7 +1284,7 @@ MidiModel::sync_to_source (const Source::WriterLock& source_lock) _midi_source.append_event_beats(source_lock, *i); } - _midi_source.mark_streaming_write_completed (source_lock); + _midi_source.mark_streaming_write_completed (source_lock, timecnt_t (duration())); set_edited (false); @@ -1300,10 +1300,10 @@ MidiModel::sync_to_source (const Source::WriterLock& source_lock) */ bool MidiModel::write_section_to (std::shared_ptr source, - const Source::WriterLock& source_lock, - TimeType begin_time, - TimeType end_time, - bool offset_events) + const Source::WriterLock& source_lock, + TimeType begin_time, + TimeType end_time, + bool offset_events) { ReadLock lock(read_lock()); MidiNoteTracker mst; @@ -1348,7 +1348,11 @@ MidiModel::write_section_to (std::shared_ptr source, } mst.resolve_notes (*source, source_lock, end_time); - source->mark_streaming_write_completed(source_lock); + /* the new source will have precisely the length given by begin_time + * and end_time. That might not be quite right in some cases. + */ + + source->mark_streaming_write_completed (source_lock, timecnt_t (end_time - begin_time)); set_edited(false); diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index a899b1396b..f7be87cb75 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -376,10 +376,10 @@ MidiSource::mark_streaming_write_started (const WriterLock& lock) void MidiSource::mark_midi_streaming_write_completed (const WriterLock& lock, Evoral::Sequence::StuckNoteOption option, - Temporal::Beats end) + Temporal::timecnt_t const & duration) { if (_model) { - _model->end_write (option, end); + _model->end_write (option, duration.beats()); /* Make captured controls discrete to play back user input exactly. */ for (MidiModel::Controls::iterator i = _model->controls().begin(); i != _model->controls().end(); ++i) { @@ -395,9 +395,9 @@ MidiSource::mark_midi_streaming_write_completed (const WriterLock& } void -MidiSource::mark_streaming_write_completed (const WriterLock& lock) +MidiSource::mark_streaming_write_completed (const WriterLock& lock, Temporal::timecnt_t const & duration) { - mark_midi_streaming_write_completed (lock, Evoral::Sequence::DeleteStuckNotes); + mark_midi_streaming_write_completed (lock, Evoral::Sequence::DeleteStuckNotes, duration); } int diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index dd608d3faa..3537569706 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -2411,7 +2411,7 @@ Session::set_sample_rate (samplecnt_t frames_per_second) sync_time_vars(); clear_clicks (); - reset_write_sources (false); + reset_write_sources (); DiskReader::alloc_loop_declick (nominal_sample_rate()); Location* loc = _locations->auto_loop_location (); @@ -6123,7 +6123,7 @@ Session::reset_native_file_format () if (tr) { /* don't save state as we do this, there's no point */ _state_of_the_state = StateOfTheState (_state_of_the_state | InCleanup); - tr->reset_write_sources (false); + tr->reset_write_sources (); _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup); } } @@ -6468,6 +6468,10 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end, time (&now); xnow = localtime (&now); + /* XXX we may want to round this up to the next beat or bar */ + + const timecnt_t duration (end - start); + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { std::shared_ptr afs = std::dynamic_pointer_cast(*src); std::shared_ptr ms; @@ -6479,9 +6483,9 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end, plist.add (Properties::start, timepos_t (0)); } else if ((ms = std::dynamic_pointer_cast(*src))) { Source::WriterLock lock (ms->mutex()); - ms->mark_streaming_write_completed(lock); + ms->mark_streaming_write_completed (lock, duration); plist.add (Properties::start, timepos_t (Beats())); - } + } } /* construct a whole-file region to represent the bounced material */ diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 7edb0f6f19..f3b41a480d 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -2687,14 +2687,14 @@ Session::get_sources_as_xml () } void -Session::reset_write_sources (bool mark_write_complete, bool force) +Session::reset_write_sources () { std::shared_ptr rl = routes.reader(); for (auto const& i : *rl) { std::shared_ptr tr = std::dynamic_pointer_cast (i); if (tr) { _state_of_the_state = StateOfTheState (_state_of_the_state | InCleanup); - tr->reset_write_sources(mark_write_complete, force); + tr->reset_write_sources (); _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup); } } @@ -5442,7 +5442,7 @@ Session::save_as (SaveAs& saveas) /* ensure that all existing tracks reset their current capture source paths */ - reset_write_sources (true, true); + reset_write_sources (); /* creating new write sources marks the session as dirty. If the new session is empty, then diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index e5eeea6b88..98a24dc501 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -462,6 +462,7 @@ void SMFSource::update_length (timepos_t const & dur) { assert (!_length || (_length.time_domain() == dur.time_domain())); + Evoral::SMF::set_duration (dur.beats()); _length = dur; } @@ -470,7 +471,7 @@ void SMFSource::append_event_beats (const WriterLock& lock, const Evoral::Event& ev) { - if (!_writing || ev.size() == 0) { + if (!_writing || ev.size() == 0 || ev.is_realtime()) { return; } @@ -516,7 +517,7 @@ SMFSource::append_event_beats (const WriterLock& lock, const Temporal::Beats delta_time_beats = time - _last_ev_time_beats; const uint32_t delta_time_ticks = delta_time_beats.to_ticks(ppqn()); - Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id); + Evoral::SMF::append_event_delta (delta_time_ticks, ev.size(), ev.buffer(), event_id); _last_ev_time_beats = time; _flags = Source::Flag (_flags & ~Empty); _flags = Source::Flag (_flags & ~Missing); @@ -528,7 +529,7 @@ SMFSource::append_event_samples (const WriterLock& lock, const Evoral::Event& ev, samplepos_t position) { - if (!_writing || ev.size() == 0) { + if (!_writing || ev.size() == 0 || ev.is_realtime()) { return; } @@ -622,15 +623,15 @@ SMFSource::mark_streaming_midi_write_started (const WriterLock& lock, NoteMode m } void -SMFSource::mark_streaming_write_completed (const WriterLock& lock) +SMFSource::mark_streaming_write_completed (const WriterLock& lm, Temporal::timecnt_t const & duration) { - mark_midi_streaming_write_completed (lock, Evoral::Sequence::DeleteStuckNotes); + mark_midi_streaming_write_completed (lm, Evoral::Sequence::DeleteStuckNotes, duration); } void -SMFSource::mark_midi_streaming_write_completed (const WriterLock& lm, Evoral::Sequence::StuckNoteOption stuck_notes_option, Temporal::Beats when) +SMFSource::mark_midi_streaming_write_completed (const WriterLock& lm, Evoral::Sequence::StuckNoteOption stuck_notes_option, Temporal::timecnt_t const & duration) { - MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, when); + MidiSource::mark_midi_streaming_write_completed (lm, stuck_notes_option, duration); if (!writable()) { warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg; @@ -638,10 +639,11 @@ SMFSource::mark_midi_streaming_write_completed (const WriterLock& lm, Evoral::Se } if (_model) { - _model->set_edited(false); + _model->set_edited (false); } try { + Evoral::SMF::set_duration (duration.beats()); Evoral::SMF::end_write (_path); } catch (std::exception & e) { error << string_compose (_("Exception while writing %1, file may be corrupt/unusable"), _path) << endmsg; @@ -812,6 +814,7 @@ SMFSource::load_model_unlocked (bool force_reload) } _num_channels = _used_channels.size(); + _length = duration(); eventlist.sort(compare_eventlist); diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 3a26d38505..4fc20558f3 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -592,9 +592,9 @@ Track::steal_write_source_name() } void -Track::reset_write_sources (bool r, bool force) +Track::reset_write_sources () { - _disk_writer->reset_write_sources (r, force); + _disk_writer->reset_write_sources (); } float diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index fb84925ac6..93ecabb0b6 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -1966,6 +1966,8 @@ AudioTrigger::captured (SlotArmInfo& ai, BufferSet&) _box.queue_explict (index()); + TriggerBox::worker->request_build_source (this, timecnt_t (data.length)); + _armed = false; ArmChanged(); /* EMIT SIGNAL */ TriggerArmChanged (this); @@ -2452,8 +2454,11 @@ MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs) old_rt_midibuffer = rt_midibuffer.exchange (ai.midi_buf); + Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use()); + timecnt_t dur = tmap->convert_duration (timecnt_t (ai.captured), timepos_t (ai.start_samples), Temporal::BeatTime); + loop_start = Temporal::Beats(); - loop_end = (*ai.midi_buf)[ai.midi_buf->size()-1].timestamp; + loop_end = dur.beats(); data_length = loop_end; _follow_length = Temporal::BBT_Offset (0, data_length.get_beats(), 0); set_length (timecnt_t (data_length)); @@ -2471,6 +2476,9 @@ MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs) /* Meanwhile, build a new source and region from the data now in rt_midibuffer */ + // std::cerr << "capture done, ask for a source of length " << loop_end.str() << std::endl; + TriggerBox::worker->request_build_source (this, timecnt_t (loop_end)); + _armed = false; ArmChanged(); /* EMIT SIGNAL */ TriggerArmChanged (this); @@ -3437,6 +3445,7 @@ SlotArmInfo::SlotArmInfo (Trigger& s) : slot (s) , start_samples (0) , end_samples (0) + , captured (0) , midi_buf (nullptr) , stretcher (nullptr) { @@ -3571,11 +3580,9 @@ TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, uint32_t ch ai->start_samples = t_samples; ai->start_beats = t_beats; - if (_data_type == DataType::AUDIO) { - ai->captured = timecnt_t (timepos_t (0), timepos_t (0)); - } else { - ai->captured = timecnt_t::from_ticks (0, timepos_t (Beats())); - } + // std::cerr << "Will start at " << t_beats.str() << std::endl; + + ai->captured = 0; _arm_info = ai; } @@ -3638,7 +3645,8 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_ t_bbt, t_beats, t_samples, tmap, ai->slot.quantization()); ai->end_samples = t_samples; ai->end_beats = t_beats; - return; + + //std::cerr << "will end at " << t_beats.str() << " samples " << ai->start_samples << " .. " << ai->end_samples << " = " << (ai->end_samples - ai->start_samples) << std::endl; } } @@ -3665,12 +3673,14 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_ offset = ai->start_samples - start_sample; nframes -= offset; _record_state = Recording; + // std::cerr << "Hit start @ " << ai->start_samples << " within " << start_sample << " ... " << end_sample << " offset will be " << offset << " nf " << nframes << std::endl; } if ((ai->end_samples != 0) && (start_sample <= ai->end_samples && ai->end_samples < end_sample)) { /* we're going to stop */ nframes -= (end_sample - ai->end_samples); reached_end = true; + // std::cerr << "Hit end @ " << ai->end_samples << " within " << start_sample << " ... " << end_sample << " nf " << nframes << std::endl; } /* Audio */ @@ -3686,8 +3696,6 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_ AudioBuffer& buf (bufs.get_audio (n)); ai->audio_buf.append (buf.data() + offset, nframes, n); } - - ai->captured += timecnt_t (start_sample, timepos_t (ai->start_samples)); } n_buffers = bufs.count().n_midi(); @@ -3734,11 +3742,11 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_ } } } - - timecnt_t dur = tmap->convert_duration (timecnt_t (nframes), timepos_t (start_sample), Temporal::BeatTime); - ai->captured += dur; } + ai->captured += nframes; + //std::cerr << "Captured " << nframes << " total " << ai->captured << std::endl; + Captured (ai->captured); /* EMIT SIGNAL */ if (reached_end) { @@ -5562,7 +5570,7 @@ TriggerBoxThread::thread_work () delete_trigger (req->trigger); break; case BuildSourceAndRegion: - build_source (req->trigger); + build_source (req->trigger, req->duration); break; default: break; @@ -5632,10 +5640,11 @@ TriggerBoxThread::request_delete_trigger (Trigger* t) } void -TriggerBoxThread::request_build_source (Trigger* t) +TriggerBoxThread::request_build_source (Trigger* t, Temporal::timecnt_t const & len) { TriggerBoxThread::Request* req = new TriggerBoxThread::Request (BuildSourceAndRegion); req->trigger = t; + req->duration = len; queue_request (req); } @@ -5646,20 +5655,20 @@ TriggerBoxThread::delete_trigger (Trigger* t) } void -TriggerBoxThread::build_source (Trigger* t) +TriggerBoxThread::build_source (Trigger* t, Temporal::timecnt_t const & duration) { MIDITrigger* mt = dynamic_cast (t); AudioTrigger* at; if (mt) { - build_midi_source (mt); + build_midi_source (mt, duration); } else if ((at = dynamic_cast (t))) { - build_audio_source (at); + build_audio_source (at, duration); } } void -TriggerBoxThread::build_audio_source (AudioTrigger* t) +TriggerBoxThread::build_audio_source (AudioTrigger* t, Temporal::timecnt_t const & duration) { Track* trk = static_cast (t->box().owner()); SourceList sources; @@ -5710,7 +5719,7 @@ TriggerBoxThread::build_audio_source (AudioTrigger* t) } void -TriggerBoxThread::build_midi_source (MIDITrigger* t) +TriggerBoxThread::build_midi_source (MIDITrigger* t, Temporal::timecnt_t const & duration) { Track* trk = static_cast (t->box().owner()); std::shared_ptr ms = t->box().session().create_midi_source_for_session (trk->name()); @@ -5729,7 +5738,7 @@ TriggerBoxThread::build_midi_source (MIDITrigger* t) ms->append_event_beats (lock, ev); } - ms->mark_streaming_write_completed (lock); + ms->mark_streaming_write_completed (lock, duration); ms->load_model (lock); } diff --git a/libs/evoral/SMF.cc b/libs/evoral/SMF.cc index f8af97be0b..caadc2d918 100644 --- a/libs/evoral/SMF.cc +++ b/libs/evoral/SMF.cc @@ -49,7 +49,7 @@ using namespace std; namespace Evoral { SMF::SMF() - : _smf (0) + : _smf (nullptr) , _smf_track (0) , _empty (true) , _n_note_on_events (0) @@ -238,7 +238,7 @@ SMF::create(const std::string& path, int track, uint16_t ppqn) _smf = smf_new(); - if (_smf == NULL) { + if (_smf == nullptr) { return -1; } @@ -508,7 +508,7 @@ SMF::begin_write() } void -SMF::end_write(string const & path) +SMF::end_write (string const & path) { Glib::Threads::Mutex::Lock lm (_smf_lock); @@ -516,6 +516,9 @@ SMF::end_write(string const & path) return; } + + + FILE* f = g_fopen (path.c_str(), "w+b"); if (f == 0) { throw FileError (path); @@ -530,20 +533,30 @@ SMF::end_write(string const & path) } void -SMF::set_length (Temporal::Beats const & b) +SMF::set_duration (Temporal::Beats const & b) { if (!_smf) { return; } + size_t their_pulses = b.to_ticks (ppqn()); + for (uint16_t n = 0; n < _smf->number_of_tracks; ++n) { smf_track_t* trk = smf_get_track_by_number (_smf, n+1); - if (trk) { - smf_track_add_eot_pulses (trk, (int) floor (b.get_ticks() * ((double) ppqn() / Temporal::ticks_per_beat))); - } + (void) smf_track_add_eot_pulses (trk, their_pulses); } } +Temporal::Beats +SMF::duration () const +{ + if (!_smf) { + return Temporal::Beats(); + } + + return Temporal::Beats::ticks_at_rate (smf_get_length_pulses (_smf), ppqn()); +} + double SMF::round_to_file_precision (double val) const { diff --git a/libs/evoral/Sequence.cc b/libs/evoral/Sequence.cc index db9615b5f3..8a04122b8c 100644 --- a/libs/evoral/Sequence.cc +++ b/libs/evoral/Sequence.cc @@ -482,6 +482,7 @@ Sequence