diff --git a/gtk2_ardour/audio_clip_editor.cc b/gtk2_ardour/audio_clip_editor.cc index 1497354132..17dc08094d 100644 --- a/gtk2_ardour/audio_clip_editor.cc +++ b/gtk2_ardour/audio_clip_editor.cc @@ -430,11 +430,11 @@ AudioClipEditor::position_lines () return; } - double width = sample_to_pixel (_region->start().samples()); - start_line->set (ArdourCanvas::Rect (0., 0., width, _visible_canvas_height)); + double start_x1 = sample_to_pixel (_region->start().samples()); + double end_x0 = start_x1 + sample_to_pixel (_region->length().samples()); - double offset = sample_to_pixel ((_region->start() + _region->length()).samples()); - end_line->set_position (ArdourCanvas::Duple (offset, 0.)); + start_line->set (ArdourCanvas::Rect (0., 0., start_x1, _visible_canvas_height)); + end_line->set_position (ArdourCanvas::Duple (end_x0, 0.)); end_line->set (ArdourCanvas::Rect (0., 0., ArdourCanvas::COORD_MAX, _visible_canvas_height)); } @@ -789,15 +789,23 @@ AudioClipEditor::maybe_update () if (playing_trigger->active ()) { if (playing_trigger->the_region()) { - _playhead_cursor->set_position (playing_trigger->current_pos().samples() + playing_trigger->the_region()->start().samples()); + + /* We can't know the precise sample + * position because we may be + * stretching. So figure out + */ + std::shared_ptr at (std::dynamic_pointer_cast (playing_trigger)); + if (at) { + const double f = playing_trigger->position_as_fraction (); + _playhead_cursor->set_position (playing_trigger->the_region()->start().samples() + (f * at->data_length())); + } } } else { _playhead_cursor->set_position (0); } } -#if 0 - } else if (view->midi_region()) { + } else if (_region) { /* Timeline region editor */ @@ -806,13 +814,13 @@ AudioClipEditor::maybe_update () } samplepos_t pos = _session->transport_sample(); - samplepos_t spos = view->midi_region()->source_position().samples(); + samplepos_t spos = _region->source_position().samples(); if (pos < spos) { _playhead_cursor->set_position (0); } else { _playhead_cursor->set_position (pos - spos); } -#endif + } else { _playhead_cursor->set_position (0); } diff --git a/gtk2_ardour/audio_trigger_properties_box.cc b/gtk2_ardour/audio_trigger_properties_box.cc index e387a98e02..d5651b40d7 100644 --- a/gtk2_ardour/audio_trigger_properties_box.cc +++ b/gtk2_ardour/audio_trigger_properties_box.cc @@ -30,6 +30,7 @@ #include "widgets/tooltips.h" +#include "ardour/audioregion.h" #include "ardour/location.h" #include "ardour/profile.h" #include "ardour/session.h" @@ -230,8 +231,14 @@ AudioTriggerPropertiesBox::on_trigger_changed (const PBD::PropertyChange& pc) _start_clock.set_mode (mode); _length_clock.set_mode (mode); - _start_clock.set (at->start_offset ()); - _length_clock.set (at->current_length ()); // set_duration() ? + std::shared_ptr ar (std::dynamic_pointer_cast (at->the_region())); + if (ar) { + _start_clock.set (ar->start()); + _length_clock.set (timepos_t (ar->length())); + } else { + _start_clock.set (timepos_t (0)); + _length_clock.set (timepos_t (0)); + } } if ( pc.contains (Properties::tempo_meter) || pc.contains (Properties::follow_length)) { @@ -305,11 +312,11 @@ AudioTriggerPropertiesBox::beats_changed () void AudioTriggerPropertiesBox::start_clock_changed () { - trigger()->set_start(_start_clock.last_when()); + /* XXX do something, probably to the region */ } void AudioTriggerPropertiesBox::length_clock_changed () { - trigger()->set_length(_length_clock.current_duration()); //? + /* XXX do something, probably to the region */ } diff --git a/gtk2_ardour/strip_import_dialog.cc b/gtk2_ardour/strip_import_dialog.cc index eb68b24673..5aeff03821 100644 --- a/gtk2_ardour/strip_import_dialog.cc +++ b/gtk2_ardour/strip_import_dialog.cc @@ -425,12 +425,24 @@ StripImportDialog::refill_import_table () _strip_table.attach (*manage (new ArdourHSpacer (1.0)), 0, 4, 1, 2, EXPAND | FILL, SHRINK, 4, 8); /* clang-format on */ + std::vector> sorted_map; + for (auto const& i : _import_map) { + sorted_map.push_back (i); + } + std::sort (sorted_map.begin (), sorted_map.end (), [=] (auto& a, auto& b) { + try { + return _route_map.at (a.first).pi.order () < _route_map.at (b.first).pi.order (); + } catch (...) { + } + return a.second < b.second; + }); + /* Refill table */ int r = 1; - for (auto& [rid, eid] : _import_map) { + for (auto& [rid, eid] : sorted_map /*_import_map*/) { ++r; if (_route_map.find (rid) != _route_map.end ()) { - l = manage (new Label (_route_map[rid], 0, 0.5)); + l = manage (new Label (_route_map.at (rid).name, 0, 0.5)); } else { l = manage (new Label (_("New Track"), 0, 0.5)); l->set_use_markup (); @@ -442,10 +454,10 @@ StripImportDialog::refill_import_table () #else using namespace Menu_Helpers; ArdourDropdown* dd = manage (new ArdourDropdown ()); - for (auto& [eid, ename] : _extern_map) { - dd->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (ename), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::change_mapping), dd, rid, eid, ename))); + for (auto& [eid, einfo] : _extern_map) { + dd->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (einfo.name), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::change_mapping), dd, rid, eid, einfo.name))); } - dd->set_text (_extern_map[eid]); + dd->set_text (_extern_map.at (eid).name); _strip_table.attach (*dd, 2, 3, r, r + 1, EXPAND | FILL, SHRINK); #endif @@ -485,18 +497,18 @@ StripImportDialog::refill_import_table () _add_rid_dropdown->add_menu_elem (MenuElem (_(" -- New Track -- "), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, next_new, _("New Track")))); sizing_texts.push_back (_(" -- New Track -- ")); - for (auto& [rid, rname] : _route_map) { + for (auto& [rid, rinfo] : _route_map) { if (_import_map.find (rid) != _import_map.end ()) { continue; } - _add_rid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (rname), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, rid, rname))); - sizing_texts.push_back (rname); + _add_rid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (rinfo.name), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, rid, rinfo.name))); + sizing_texts.push_back (rinfo.name); } _add_eid_dropdown = manage (new ArdourWidgets::ArdourDropdown ()); - for (auto& [eid, ename] : _extern_map) { - _add_eid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (ename), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), true, eid, ename))); - sizing_texts.push_back (ename); + for (auto& [eid, einfo] : _extern_map) { + _add_eid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (einfo.name), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), true, eid, einfo.name))); + sizing_texts.push_back (einfo.name); } _add_rid_dropdown->set_sizing_texts (sizing_texts); @@ -588,7 +600,7 @@ StripImportDialog::import_all_strips () _import_map.clear (); int64_t next_id = std::numeric_limits::max () - 1 - _extern_map.size (); - for (auto& [eid, ename] : _extern_map) { + for (auto& [eid, einfo] : _extern_map) { PBD::ID next_new = PBD::ID (next_id++); _import_map[next_new] = eid; } @@ -604,17 +616,23 @@ StripImportDialog::set_default_mapping (bool and_idle_update) if (_match_pbd_id) { /* try a 1:1 mapping */ - for (auto& [eid, ename] : _extern_map) { + for (auto& [eid, einfo] : _extern_map) { if (_route_map.find (eid) != _route_map.end ()) { _import_map[eid] = eid; } } } else { /* match by name */ - for (auto& [eid, ename] : _extern_map) { + for (auto& [eid, einfo] : _extern_map) { // TODO consider building a reverse [pointer] map - for (auto& [rid, rname] : _route_map) { - if (ename == rname) { + for (auto& [rid, rinfo] : _route_map) { +#ifdef MIXBUS + if (einfo.mixbus > 0 && einfo.mixbus == rinfo.mixbus) { + _import_map[rid] = eid; + break; + } +#endif + if (einfo == rinfo) { _import_map[rid] = eid; break; } @@ -632,7 +650,11 @@ StripImportDialog::setup_strip_import_page () _route_map.clear (); for (auto const& r : *_session->get_routes ()) { - _route_map[r->id ()] = r->name (); +#ifdef MIXBUS + _route_map.emplace (r->id (), Session::RouteImportInfo (r->name (), r->presentation_info (), c->mixbus ())); +#else + _route_map.emplace (r->id (), Session::RouteImportInfo (r->name (), r->presentation_info (), 0)); +#endif } set_default_mapping (false); diff --git a/gtk2_ardour/strip_import_dialog.h b/gtk2_ardour/strip_import_dialog.h index 9f1d6c2725..61e0daf5ea 100644 --- a/gtk2_ardour/strip_import_dialog.h +++ b/gtk2_ardour/strip_import_dialog.h @@ -28,17 +28,14 @@ #include #include -#include "ardour/search_paths.h" #include "pbd/id.h" +#include "ardour/search_paths.h" +#include "ardour/session.h" + #include "ardour/template_utils.h" #include "ardour_dialog.h" -namespace ARDOUR -{ - class Session; -} - namespace ArdourWidgets { class ArdourButton; @@ -126,11 +123,12 @@ private: ArdourWidgets::ArdourButton* _reset_mapping; ArdourWidgets::ArdourButton* _import_strips; - bool _match_pbd_id; - std::string _path; - std::map _extern_map; - std::map _route_map; - std::map _import_map; + bool _match_pbd_id; + std::string _path; + std::map _import_map; + + std::map _extern_map; + std::map _route_map; PBD::ID _add_rid; PBD::ID _add_eid; diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 65828b35d2..845dc504e2 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -664,17 +664,39 @@ public: std::vector possible_states() const; static std::vector possible_states (std::string path); - enum RouteGroupImportMode { IgnoreRouteGroup, UseRouteGroup, CreateRouteGroup }; + struct RouteImportInfo { + RouteImportInfo (std::string const& n, PresentationInfo const& p, int mb) + : name (n) + , pi (p) + , mixbus (mb) + {} + + std::string name; + PresentationInfo pi; + int mixbus; + + bool operator< (RouteImportInfo const& o) { + if (mixbus != o.mixbus) { + return mixbus < o.mixbus; + } + return name < o.name; + } + + bool operator== (RouteImportInfo const& o) { + return mixbus == o.mixbus && name == o.name; + } + }; + bool export_route_state (std::shared_ptr rl, const std::string& path, bool with_sources); int import_route_state (const std::string& path, std::map const&, RouteGroupImportMode rgim = CreateRouteGroup); - std::map parse_route_state (const std::string& path, bool& match_pbd_id); + std::map parse_route_state (const std::string& path, bool& match_pbd_id); /// The instant xml file is written to the session directory void add_instant_xml (XMLNode&, bool write_to_config = true); diff --git a/libs/ardour/ardour/triggerbox.h b/libs/ardour/ardour/triggerbox.h index 4aec665729..d3839dc0c1 100644 --- a/libs/ardour/ardour/triggerbox.h +++ b/libs/ardour/ardour/triggerbox.h @@ -286,10 +286,6 @@ class LIBARDOUR_API Trigger : public PBD::Stateful { virtual pframes_t run (BufferSet&, samplepos_t start_sample, samplepos_t end_sample, Temporal::Beats const & start, Temporal::Beats const & end, pframes_t nframes, pframes_t offset, double bpm, pframes_t& quantize_offset) = 0; - virtual void set_start (timepos_t const &) = 0; - virtual void set_end (timepos_t const &) = 0; - virtual void set_length (timecnt_t const &) = 0; - virtual void reload (BufferSet&, void*) = 0; virtual void io_change () {} virtual void set_legato_offset (timepos_t const & offset) = 0; @@ -319,10 +315,6 @@ class LIBARDOUR_API Trigger : public PBD::Stateful { virtual bool probably_oneshot () const = 0; - virtual timepos_t start_offset () const = 0; /* offset from start of data */ - virtual timepos_t current_length() const = 0; /* offset from start() */ - virtual timepos_t natural_length() const = 0; /* offset from start() */ - void process_state_requests (BufferSet& bufs, pframes_t dest_offset); bool active() const { return _state >= Running; } @@ -446,7 +438,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful { std::shared_ptr _region; samplecnt_t process_index; - samplepos_t final_processed_sample; /* where we stop playing, in process time, compare with process_index */ + samplepos_t final_process_index; /* where we stop playing, in process time, compare with process_index */ UIState ui_state; TriggerBox& _box; UIRequests _requests; @@ -519,7 +511,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful { std::atomic pending_swap; std::atomic old_pending_swap; - virtual void adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region) = 0; + virtual int load_pending_data (PendingSwap&) = 0; + virtual PendingSwap* pending_factory() const = 0; }; class LIBARDOUR_API AudioTrigger : public Trigger { @@ -546,15 +539,8 @@ class LIBARDOUR_API AudioTrigger : public Trigger { double segment_beatcnt () { return _beatcnt; } void set_segment_beatcnt (double count); - void set_start (timepos_t const &); - void set_end (timepos_t const &); void set_legato_offset (timepos_t const &); void set_length (timecnt_t const &); - void set_user_data_length (samplecnt_t); - timepos_t start_offset () const; /* offset from start of data */ - timepos_t current_length() const; /* offset from start of data */ - timepos_t natural_length() const; /* offset from start of data */ - void reload (BufferSet&, void*); void io_change (); bool probably_oneshot () const; @@ -585,28 +571,35 @@ class LIBARDOUR_API AudioTrigger : public Trigger { AudioData () : length (0), capacity (0) {} ~AudioData (); + AudioData& operator= (AudioData& other); /* really move semantics */ samplecnt_t append (Sample const * src, samplecnt_t cnt, uint32_t chan); void alloc (samplecnt_t cnt, uint32_t nchans); void reset () { length = 0; } + void drop (); }; Sample const * audio_data (size_t n) const; size_t data_length() const { return data.length; } - samplecnt_t user_data_length() const { return _user_data_length; } + + struct AudioPendingSwap : public PendingSwap { + AudioData audio_data; + + AudioPendingSwap() {} + ~AudioPendingSwap() {} + }; void check_edit_swap (timepos_t const &, bool playing, BufferSet&); protected: void retrigger (); - void adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region); + PendingSwap* pending_factory() const; + int load_pending_data (PendingSwap&); private: - AudioData data; - samplecnt_t _user_data_length; + AudioData data; RubberBand::RubberBandStretcher* _stretcher; - samplepos_t _start_offset; /* computed during run */ @@ -620,8 +613,8 @@ class LIBARDOUR_API AudioTrigger : public Trigger { virtual void setup_stretcher (); - void drop_data (); - int load_data (std::shared_ptr); + void drop_data (AudioData&); + int load_data (std::shared_ptr, AudioData&); void estimate_tempo (); void reset_stretcher (); void _startup (BufferSet&, pframes_t dest_offset, Temporal::BBT_Offset const &); @@ -652,9 +645,6 @@ class LIBARDOUR_API MIDITrigger : public Trigger { void set_length (timecnt_t const &); timepos_t start_offset () const; timepos_t end() const; /* offset from start of data */ - timepos_t current_length() const; /* offset from start of data */ - timepos_t natural_length() const; /* offset from start of data */ - void reload (BufferSet&, void*); bool probably_oneshot () const; void tempo_map_changed(); @@ -695,6 +685,8 @@ class LIBARDOUR_API MIDITrigger : public Trigger { std::vector const & channel_map() const { return _channel_map; } void check_edit_swap (timepos_t const &, bool playing, BufferSet&); + PendingSwap* pending_factory() const; + RTMidiBufferBeats const & rt_midi_buffer() const { return *rt_midibuffer.load(); } Temporal::Beats play_start() const { return _play_start; } @@ -706,6 +698,7 @@ class LIBARDOUR_API MIDITrigger : public Trigger { void retrigger (); void _arm (Temporal::BBT_Offset const &); void adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region); + int load_pending_data (PendingSwap&); private: PBD::ID data_source; @@ -926,7 +919,6 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro TriggerPtr get_next_trigger (); TriggerPtr peek_next_trigger (); - void request_reload (int32_t slot, void*); void set_region (uint32_t slot, std::shared_ptr region); void non_realtime_transport_stop (samplepos_t now, bool flush); @@ -1049,7 +1041,6 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro struct Request { enum Type { Use, - Reload, }; Type type; @@ -1077,8 +1068,6 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro void process_requests (BufferSet&); void process_request (BufferSet&, Request*); - void reload (BufferSet& bufs, int32_t slot, void* ptr); - void cancel_locate_armed (); void fast_forward_nothing_to_do (); int handle_stopped_trigger (BufferSet& bufs, pframes_t dest_offset); diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 6de1b6b4e0..ab34667110 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -1198,16 +1198,8 @@ Session::export_route_state (std::shared_ptr rl, const string& path, } static bool -allow_import_route_state (XMLNode const& node, int version) +allow_import_route_state (PresentationInfo const& pi) { - XMLNode* pnode = node.child (PresentationInfo::state_node_name.c_str ()); - if (!pnode) { - return false; - } - - PresentationInfo pi (PresentationInfo::Flag (0)); - pi.set_state (*pnode, version); - if (pi.special (false)) { // |SurroundMaster|MonitorOut|Auditioner return false; } @@ -1218,10 +1210,24 @@ allow_import_route_state (XMLNode const& node, int version) return true; } -std::map +static bool +allow_import_route_state (XMLNode const& node, int version) +{ + XMLNode* pnode = node.child (PresentationInfo::state_node_name.c_str ()); + if (!pnode) { + return false; + } + + PresentationInfo pi (PresentationInfo::Flag (0)); + pi.set_state (*pnode, version); + + return allow_import_route_state (pi); +} + +std::map Session::parse_route_state (const string& path, bool& match_pbd_id) { - std::map rv; + std::map rv; XMLTree tree; if (!tree.read (path)) { @@ -1256,11 +1262,24 @@ Session::parse_route_state (const string& path, bool& match_pbd_id) continue; } - if (!allow_import_route_state (*rxml, version)) { + XMLNode* pnode = rxml->child (PresentationInfo::state_node_name.c_str ()); + if (!pnode) { continue; } - rv[id] = name; + PresentationInfo pi (PresentationInfo::Flag (0)); + pi.set_state (*pnode, version); + + if (!allow_import_route_state (pi)) { + continue; + } + + int mixbus = 0; +#ifdef MIXBUS + rxml->get_property (X_("mixbus-num"), mixbus) +#endif + + rv.emplace (id, RouteImportInfo (name, pi, mixbus)); } } return rv; diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index a60d6252a5..286b8531ad 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -234,7 +234,7 @@ Trigger::Trigger (uint32_t n, TriggerBox& b) , _name (Properties::name, "") , _color (Properties::color, 0xBEBEBEFF) , process_index (0) - , final_processed_sample (0) + , final_process_index (0) , _box (b) , _state (Stopped) , _playout (false) @@ -726,6 +726,10 @@ Trigger::set_region (std::shared_ptr r, bool use_thread) /* load data, do analysis in another thread */ TriggerBox::worker->set_region (_box, index(), r); } else { + /* despite the name, this runs in the current thread. The name + comes from the fact that this is normally called from a worker + thread. It executes in thread it was called in. + */ set_region_in_worker_thread (r); } } @@ -769,7 +773,8 @@ Trigger::region_property_change (PropertyChange const & what_changed) void Trigger::bounds_changed (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & len) { - PendingSwap* pending = new PendingSwap; + PendingSwap* pending = pending_factory(); + assert (pending); pending->play_start = start; pending->play_end = end; @@ -777,6 +782,8 @@ Trigger::bounds_changed (Temporal::timepos_t const & start, Temporal::timepos_t pending->loop_end = pending->play_end; pending->length = len; + load_pending_data (*pending); + /* And set it. RT thread will find this and do what needs to be done */ DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 pushed pending swap @ %3 for bounds change\n", _box.order(), index(), pending)); @@ -804,7 +811,7 @@ Trigger::position_as_fraction () const return 0.0; } - return process_index / (double) final_processed_sample; + return process_index / (double) final_process_index; } void @@ -1373,6 +1380,37 @@ Trigger::start_and_roll_to (samplepos_t start_pos, samplepos_t end_position, Tri /*--------------------*/ +void +AudioTrigger::AudioData::drop () +{ + for (auto& d : *this) { + delete [] d; + } + + clear (); +} + +AudioTrigger::AudioData& +AudioTrigger::AudioData::operator= (AudioTrigger::AudioData& other) +{ + /* This is really implementing move semantics between two AudioData objects */ + + drop (); + + reserve (other.size()); + for (auto & sample_ptr : other) { + push_back (sample_ptr); + } + length = other.length; + capacity = other.capacity; + + other.clear (); + other.length = 0; + other.capacity = 0; + + return *this; +} + AudioTrigger::AudioData::~AudioData () { for (auto & s : *this) { @@ -1410,8 +1448,7 @@ AudioTrigger::AudioData::append (Sample const * src, samplecnt_t cnt, uint32_t c AudioTrigger::AudioTrigger (uint32_t n, TriggerBox& b) : Trigger (n, b) - , _stretcher (0) - , _start_offset (0) + , _stretcher (nullptr) , read_index (0) , last_readable_sample (0) , _legato_offset (0) @@ -1424,7 +1461,7 @@ AudioTrigger::AudioTrigger (uint32_t n, TriggerBox& b) AudioTrigger::~AudioTrigger () { - drop_data (); + data.drop (); delete _stretcher; } @@ -1546,9 +1583,6 @@ XMLNode& AudioTrigger::get_state () const { XMLNode& node (Trigger::get_state()); - - node.set_property (X_("start"), timepos_t (_start_offset)); - return node; } @@ -1561,41 +1595,18 @@ AudioTrigger::set_state (const XMLNode& node, int version) return -1; } - node.get_property (X_("start"), t); - _start_offset = t.samples(); - /* we've changed our internal values; we need to update our queued UIState or they will be lost when UIState is applied */ copy_to_ui_state (); return 0; } -void -AudioTrigger::set_start (timepos_t const & s) -{ - /* XXX better minimum size needed */ - _start_offset = std::max (samplepos_t (4096), s.samples ()); -} - -void -AudioTrigger::set_end (timepos_t const & e) -{ - assert (!data.empty()); - set_length (timecnt_t (e.samples() - _start_offset, timepos_t (_start_offset))); -} - void AudioTrigger::set_legato_offset (timepos_t const & offset) { _legato_offset = offset.samples(); } -timepos_t -AudioTrigger::start_offset () const -{ - return timepos_t (_start_offset); -} - void AudioTrigger::start_and_roll_to (samplepos_t start_pos, samplepos_t end_position, uint32_t cnt) { @@ -1609,7 +1620,7 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal: expected_end_sample: (TIMELINE!) the sample position where the data for the clip should run out (taking stretch into account) last_readable_sample: (DATA RELATIVE!) the sample in the data where we stop reading - final_processed_sample: (DATA RELATIVE!) the sample where the trigger stops and the follow action if any takes effect + final_process_index: (DATA RELATIVE!) the sample where the trigger stops and the follow action if any takes effect Things that affect these values: @@ -1626,51 +1637,37 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal: */ const Temporal::Beats bc (Temporal::Beats::from_double (_beatcnt)); - samplepos_t end_by_follow_length = tmap->sample_at (tmap->bbt_walk (transition_bba, _follow_length)); - samplepos_t end_by_beatcnt = tmap->sample_at (tmap->bbt_walk (transition_bba, Temporal::BBT_Offset (0, bc.get_beats(), bc.get_ticks()))); - samplepos_t end_by_user_data_length = transition_sample + (_user_data_length - _start_offset); - samplepos_t end_by_data_length = transition_sample + (data.length - _start_offset); - samplepos_t end_by_fixed_samples = std::min (end_by_user_data_length, end_by_data_length); + /* This is tempo-sensitive - we actually compute the sample position bc + beats after the transition sample, using either the follow length + or _beatcnt + */ - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 computing end with fl %2 bc %3 dl %4 udl %5\n", index(), - _follow_length, bc.str(), data.length, _user_data_length)); + const Temporal::BBT_Offset beat_length = internal_use_follow_length() ? _follow_length : Temporal::BBT_Offset (0, bc.get_beats(), bc.get_ticks()); + samplepos_t end_by_beats = tmap->sample_at (tmap->bbt_walk (transition_bba, beat_length)); - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 SO %9 @ %2 / %3 / %4 ends: FL %5 (from %6) BC %7 DL %8\n", + /* These are non-tempo-sensitive, and represent data-centric sample counts. */ + samplepos_t end_by_data_length = transition_sample + data.length; + + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 computing end with fl %2 bc %3 dl %4 EDL %5\n", index(), + _follow_length, bc.str(), data.length, end_by_data_length)); + + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 @ %2 / %3 / %4 ends: FL %5 BC %6 from %7 DL %8\n", index(), transition_sample, transition_beats, transition_bbt, - end_by_follow_length, _follow_length, end_by_beatcnt, end_by_data_length, _start_offset)); + _follow_length, end_by_beats, + internal_use_follow_length() ? _follow_length : Temporal::BBT_Offset (0, bc.get_beats(), bc.get_ticks()), + end_by_data_length)); if (stretching()) { - /* NOTES: beatcnt here will reflect the stretch, which is OK - Because we are stretching. But .. that's wrong when we're not - stretching. So we really need another value: something like - data_length but user-definable. - */ - if (internal_use_follow_length()) { - expected_end_sample = std::min (end_by_follow_length, end_by_beatcnt); - DEBUG_TRACE (DEBUG::Triggers, string_compose ("stretch and use follow, end up at %1\n", expected_end_sample)); - } else { - expected_end_sample = end_by_beatcnt; - DEBUG_TRACE (DEBUG::Triggers, string_compose ("stretch and no-follow, end up at %1\n", expected_end_sample)); - } + expected_end_sample = end_by_beats; + DEBUG_TRACE (DEBUG::Triggers, string_compose ("stretching, end up at %1\n", expected_end_sample)); } else { - if (internal_use_follow_length()) { - expected_end_sample = std::min (end_by_follow_length, end_by_fixed_samples); - DEBUG_TRACE (DEBUG::Triggers, string_compose ("no-stretch and use follow, end up at %1\n", expected_end_sample)); - } else { - expected_end_sample = end_by_fixed_samples; - DEBUG_TRACE (DEBUG::Triggers, string_compose ("no-stretch and no-follow, end up at %1\n", expected_end_sample)); - } + expected_end_sample = std::min (end_by_beats, end_by_data_length); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("no-stretch, end up at %1\n", expected_end_sample)); } - final_processed_sample = expected_end_sample - transition_sample; - - samplecnt_t usable_length; - - if (internal_use_follow_length() && (end_by_follow_length < end_by_data_length)) { - usable_length = std::min (end_by_follow_length - transition_samples, end_by_fixed_samples - transition_samples); - } else { - usable_length = end_by_fixed_samples - transition_samples; - } + final_process_index = expected_end_sample - transition_sample; + samplecnt_t usable_length = end_by_data_length - transition_samples; + DEBUG_TRACE (DEBUG::Triggers, string_compose ("usable length from end-by-samples %1 - transition @ %2 = %3\n", end_by_data_length, transition_samples, usable_length)); /* called from compute_end() when we know the time (audio & * musical time domains when we start starting. Our job here is to @@ -1681,7 +1678,7 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal: if (launch_style() != Repeat || (q == Temporal::BBT_Offset())) { - last_readable_sample = _start_offset + usable_length; + last_readable_sample = usable_length; } else { @@ -1693,14 +1690,14 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal: /* XXX MUST HANDLE BAR-LEVEL QUANTIZATION */ timecnt_t len (Temporal::Beats (q.beats, q.ticks), timepos_t (Temporal::Beats())); - last_readable_sample = _start_offset + len.samples(); + last_readable_sample = len.samples(); } - effective_length = tmap->quarters_at_sample (transition_sample + final_processed_sample) - tmap->quarters_at_sample (transition_sample); + effective_length = tmap->quarters_at_sample (transition_sample + final_process_index) - tmap->quarters_at_sample (transition_sample); _transition_bbt = transition_bbt; - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1: final sample %2 vs ees %3 ls %4\n", index(), final_processed_sample, expected_end_sample, last_readable_sample)); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1: final process index %2 expected end (timeline) sample %3 final read index %4\n", index(), final_process_index, expected_end_sample, last_readable_sample)); return timepos_t (expected_end_sample); } @@ -1711,30 +1708,6 @@ AudioTrigger::set_length (timecnt_t const & newlen) /* XXX what */ } -void -AudioTrigger::set_user_data_length (samplecnt_t s) -{ - _user_data_length = s; - send_property_change (ARDOUR::Properties::length); -} - -timepos_t -AudioTrigger::current_length() const -{ - if (_region) { - return timepos_t (data.length); - } - return timepos_t (Temporal::BeatTime); -} - -timepos_t -AudioTrigger::natural_length() const -{ - if (_region) { - return timepos_t::from_superclock (_region->length().magnitude()); - } - return timepos_t (Temporal::BeatTime); -} int AudioTrigger::set_region_in_worker_thread_from_capture (std::shared_ptr r) { @@ -1771,12 +1744,10 @@ AudioTrigger::set_region_in_worker_thread_internal (std::shared_ptr r, b } if (!from_capture) { - load_data (ar); + load_data (ar, data); } - if (from_capture) { - set_name (r->name()); - } + set_name (ar->name()); estimate_tempo (); /* NOTE: if this is an existing clip (D+D copy) then it will likely have a SD tempo, and that short-circuits minibpm for us */ @@ -1897,15 +1868,6 @@ AudioTrigger::setup_stretcher () _stretcher->setMaxProcessSize (rb_blocksize); } -void -AudioTrigger::drop_data () -{ - for (auto& d : data) { - delete [] d; - } - data.clear (); -} - void AudioTrigger::captured (SlotArmInfo& ai) { @@ -1919,7 +1881,6 @@ AudioTrigger::captured (SlotArmInfo& ai) data.length = ai.audio_buf.length; data.capacity = ai.audio_buf.capacity; - _user_data_length = data.length; DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 captured a total of %3\n", _box.order(), _index, data.length)); @@ -1960,27 +1921,25 @@ AudioTrigger::captured (SlotArmInfo& ai) } int -AudioTrigger::load_data (std::shared_ptr ar) +AudioTrigger::load_data (std::shared_ptr ar, AudioData& audio_data) { const uint32_t nchans = ar->n_channels(); - drop_data (); + audio_data.drop (); try { samplecnt_t len = ar->length_samples(); - data.alloc (len, nchans); + audio_data.alloc (len, nchans); for (uint32_t n = 0; n < nchans; ++n) { - ar->read (data[n], 0, len, n); + ar->read (audio_data[n], 0, len, n); } - data.length = len; - _user_data_length = len; - set_name (ar->name()); + audio_data.length = len; } catch (...) { - drop_data (); + audio_data.drop (); return -1; } @@ -1995,7 +1954,7 @@ AudioTrigger::retrigger () update_properties (); reset_stretcher (); - read_index = _start_offset + _legato_offset; + read_index = _legato_offset; retrieved = 0; _legato_offset = 0; /* used one time only */ @@ -2098,15 +2057,26 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t #ifdef HAVE_RUBBERBAND_3_0_0 to_pad = _stretcher->getPreferredStartPad(); to_drop = _stretcher->getStartDelay(); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 requires padding of %2 dropping %3 (RB v3\n", name(), to_pad, to_drop)); #else to_pad = _stretcher->getLatency(); to_drop = to_pad; + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 requires padding of %2 dropping %3 (RB < v3\n", name(), to_pad, to_drop)); #endif got_stretcher_padding = true; - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 requires %2 padding %3\n", name(), to_pad)); } while (to_pad > 0) { + /* It may seem wasteful to resilence each channel + buffer for each loop iteration here. But there's no + inherent guarantee that passing them to the + stretcher will leave them silent (logically, it + must, but that's not part of the stretcher API). + + Also, in many cases, we only actually do this once + (depending on the ratio of the audioengine buffer + size and the stretcher's latency). + */ const samplecnt_t limit = std::min ((samplecnt_t) scratch->get_audio (0).capacity(), to_pad); for (uint32_t chn = 0; chn < nchans; ++chn) { memset (bufp[chn], 0, sizeof (Sample) * limit); @@ -2145,6 +2115,10 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t in[chn] = data[chn % data.size ()] + read_index; } +#ifndef NDEBUG + samplecnt_t required = _stretcher->getSamplesRequired(); + samplecnt_t pre_avail = _stretcher->available (); +#endif /* Note: RubberBandStretcher's process() and retrieve() API's accepts Sample** * as their first argument. This code may appear to only be processing the first * channel, but actually processes them all in one pass. @@ -2155,14 +2129,17 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t read_index += to_stretcher; avail = _stretcher->available (); + samplecnt_t this_drop = 0; + if (to_drop && avail) { - samplecnt_t this_drop = std::min (std::min ((samplecnt_t) avail, to_drop), (samplecnt_t) scratch->get_audio (0).capacity()); + this_drop = std::min (std::min ((samplecnt_t) avail, to_drop), (samplecnt_t) scratch->get_audio (0).capacity()); _stretcher->retrieve (&bufp[0], this_drop); to_drop -= this_drop; avail = _stretcher->available (); } - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 process %2 at-end %3 avail %4 of %5\n", name(), to_stretcher, at_end, avail, nframes)); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 process %2 (ri now %6) at-end %3 avail %4 (was %7) of %5 (required was %8)\n", + name(), to_stretcher, at_end, avail, nframes, read_index, pre_avail, required)); } /* we've fed the stretcher enough data to have @@ -2180,18 +2157,19 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t } /* fetch the stretch */ - - retrieved += _stretcher->retrieve (&bufp[0], from_stretcher); + samplecnt_t this_retrieve = _stretcher->retrieve (&bufp[0], from_stretcher); + retrieved += this_retrieve; if (read_index >= last_readable_sample) { - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 no more data to deliver to stretcher, but retrieved %2 to put current end at %3 vs %4 / %5 pi %6\n", - index(), retrieved, transition_samples + retrieved, expected_end_sample, final_processed_sample, process_index)); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 no more data to deliver to stretcher, but with ri %8 or %9 retrieved %7(%2) to put current end at %3 vs %4 / %5 pi %6\n", + index(), retrieved, transition_samples + retrieved, expected_end_sample, final_process_index, process_index, this_retrieve, + read_index, last_readable_sample)); if (transition_samples + retrieved > expected_end_sample) { /* final pull from stretched data into output buffers */ - // cerr << "FS#2 from ees " << final_processed_sample << " - " << process_index << " & " << from_stretcher; - from_stretcher = std::min (from_stretcher, std::max (0, final_processed_sample - process_index)); + // cerr << "FS#2 from ees " << final_process_index << " - " << process_index << " & " << from_stretcher; + from_stretcher = std::min (from_stretcher, std::max (0, final_process_index - process_index)); // cerr << " => " << from_stretcher << endl; DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 total retrieved data %2 exceeds theoretical size %3, truncate from_stretcher to %4\n", @@ -2199,13 +2177,13 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t if (from_stretcher == 0) { - if (process_index < final_processed_sample) { - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached (EX) end, entering playout mode to cover %2 .. %3\n", index(), process_index, final_processed_sample)); + if (process_index < final_process_index) { + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached (EX) end, entering playout mode to cover %2 .. %3\n", index(), process_index, final_process_index)); _playout = true; } else { _state = Stopped; _loop_cnt++; - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached (EX) end, now stopped, retrieved %2, avail %3 pi %4 vs fs %5 LC now %6\n", index(), retrieved, avail, process_index, final_processed_sample, _loop_cnt)); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached (EX) end, now stopped, retrieved %2, avail %3 pi %4 vs fs %5 LC now %6\n", index(), retrieved, avail, process_index, final_process_index, _loop_cnt)); } break; @@ -2218,7 +2196,6 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t /* no stretch */ assert (last_readable_sample >= read_index); from_stretcher = std::min (nframes, last_readable_sample - read_index); - // cerr << "FS#3 from lrs " << last_readable_sample << " - " << read_index << " = " << from_stretcher << endl; } @@ -2267,8 +2244,8 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t if (read_index >= last_readable_sample && (!do_stretch || avail <= 0)) { - if (process_index < final_processed_sample) { - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end, entering playout mode to cover %2 .. %3\n", index(), process_index, final_processed_sample)); + if (process_index < final_process_index) { + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end, entering playout mode to cover %2 .. %3 avail = %4\n", index(), process_index, final_process_index, avail)); _playout = true; } else { _state = Stopped; @@ -2276,6 +2253,11 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end, now stopped, retrieved %2, avail %3 LC now %4\n", index(), retrieved, avail, _loop_cnt)); } break; + } else if (process_index >= final_process_index) { + _state = Stopped; + _loop_cnt++; + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end via pi alone, now stopped, retrieved %2, avail %3 LC now %4\n", index(), retrieved, avail, _loop_cnt)); + break; } } @@ -2291,18 +2273,18 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t } const pframes_t remaining_frames_for_run= orig_nframes - covered_frames; - const pframes_t remaining_frames_till_final = final_processed_sample - process_index; + const pframes_t remaining_frames_till_final = final_process_index - process_index; const pframes_t to_fill = std::min (remaining_frames_till_final, remaining_frames_for_run); DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 playout mode, remaining in run %2 till final %3 @ %5 ts %7 vs pi @ %6 to fill %4\n", - index(), remaining_frames_for_run, remaining_frames_till_final, to_fill, final_processed_sample, process_index, transition_samples)); + index(), remaining_frames_for_run, remaining_frames_till_final, to_fill, final_process_index, process_index, transition_samples)); if (remaining_frames_till_final != 0) { process_index += to_fill; covered_frames += to_fill; - if (process_index < final_processed_sample) { + if (process_index < final_process_index) { /* more playout to be done */ return covered_frames; } @@ -2313,6 +2295,8 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 playout finished, LC now %4\n", index(), _loop_cnt)); } + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 run() finished, ri %2 pi %3 r/p %4\n", index(), read_index, process_index, (double) read_index / process_index)); + if (_state == Stopped || _state == Stopping) { /* note: neither argument is used in the audio case */ when_stopped_during_run (bufs, dest_offset); @@ -2321,9 +2305,20 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t return covered_frames; } -void -AudioTrigger::reload (BufferSet&, void*) +Trigger::PendingSwap* +AudioTrigger::pending_factory () const { + return new AudioPendingSwap; +} + +int +AudioTrigger::load_pending_data (PendingSwap& ps) +{ + AudioPendingSwap* aps (dynamic_cast (&ps)); + assert (aps); + std::shared_ptr ar (std::dynamic_pointer_cast (_region)); + load_data (ar, aps->audio_data); + return 0; } void @@ -2343,21 +2338,53 @@ AudioTrigger::check_edit_swap (timepos_t const & time, bool playing, BufferSet& } DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 noticed pending swap @ %3\n", _box.order(), index(), pending)); - adjust_bounds (pending->play_start, pending->play_end, pending->length, true); + + /* Need to use the region's tempo (map) to convert between time domains here */ + + if (stretching()) { + + Temporal::TempoMap::SharedPtr rmap (_region->tempo_map()); + assert (rmap); + + if (pending->length.time_domain() == Temporal::BeatTime) { + _beatcnt = Temporal::DoubleableBeats (pending->length.beats()).to_double(); + } else { + _beatcnt = Temporal::DoubleableBeats (rmap->quarters_at_sample (pending->length.samples())).to_double(); + } + } + + /* Switch over data, which spans region->start() to region->end() aka + * region->start() + region->length() + */ + + AudioPendingSwap* aps (dynamic_cast (pending)); + assert (aps); + data = aps->audio_data; + + /* pending->audio_data is now unusable */ + + if (playing) { + + /* if the start has been moved past the current process + * position, we need to do something drastic. + */ + + if (pending->play_start > process_index) { + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 new start %3 is past process index %4\n", _box.order(), index(), pending->play_start, process_index)); + jump_stop (bufs, 0); + startup (bufs, 0, _quantization); + return; + } + + Temporal::Beats elen_ignored; + compute_end (Temporal::TempoMap::use(), _transition_bbt, transition_samples, elen_ignored); + } + + /* adjust read index to point to the same sample, if possible */ + old_pending_swap.store (pending); } - -void -AudioTrigger::adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool) -{ - Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use()); - set_start (timepos_t (start.samples())); - set_user_data_length (length.samples()); - /* XXX not 100% sure that we should set this here or not */ - _beatcnt = Temporal::DoubleableBeats (length.beats()).to_double(); -} - /*--------------------*/ MIDITrigger::MIDITrigger (uint32_t n, TriggerBox& b) @@ -2724,7 +2751,7 @@ MIDITrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal:: timepos_t e (final_beat); - final_processed_sample = e.samples() - transition_samples; + final_process_index = e.samples() - transition_samples; return e; } @@ -2952,24 +2979,6 @@ MIDITrigger::set_length (timecnt_t const & newlen) } -timepos_t -MIDITrigger::current_length() const -{ - if (_region) { - return timepos_t (data_length); - } - return timepos_t (Temporal::BeatTime); -} - -timepos_t -MIDITrigger::natural_length() const -{ - if (_region) { - return timepos_t::from_ticks (_region->length().magnitude()); - } - return timepos_t (Temporal::BeatTime); -} - void MIDITrigger::estimate_midi_patches () { @@ -3116,11 +3125,6 @@ MIDITrigger::retrigger () DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to start, ts = %2\n", _index, transition_beats)); } -void -MIDITrigger::reload (BufferSet&, void*) -{ -} - void MIDITrigger::tempo_map_changed () { @@ -3165,6 +3169,21 @@ MIDITrigger::tempo_map_changed () map_change = true; } +Trigger::PendingSwap* +MIDITrigger::pending_factory () const +{ + return new MIDIPendingSwap; +} + +int +MIDITrigger::load_pending_data (PendingSwap& ps) +{ + MIDIPendingSwap* mps (dynamic_cast (&ps)); + assert (mps); + _model->render (_model->read_lock(), *mps->rt_midibuffer); + return 0; +} + void MIDITrigger::model_contents_changed () { @@ -3440,28 +3459,28 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 not done with playout, all frames covered\n", index())); } else { /* finishing up playout */ - samplepos_t final_processed_sample = tmap->sample_at (timepos_t (final_beat)); + samplepos_t final_process_index = tmap->sample_at (timepos_t (final_beat)); if (map_change) { - if ((start_sample > final_processed_sample) || (final_processed_sample - start_sample > orig_nframes)) { + if ((start_sample > final_process_index) || (final_process_index - start_sample > orig_nframes)) { nframes = 0; _loop_cnt++; _state = Stopping; } else { - nframes = orig_nframes - (final_processed_sample - start_sample); + nframes = orig_nframes - (final_process_index - start_sample); } } else { - nframes = orig_nframes - (final_processed_sample - start_sample); + nframes = orig_nframes - (final_process_index - start_sample); _loop_cnt++; _state = Stopped; } - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 playout done, nf = %2 fb %3 fs %4 %5 LC %6\n", index(), nframes, final_beat, final_processed_sample, start_sample, _loop_cnt)); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 playout done, nf = %2 fb %3 fs %4 %5 LC %6\n", index(), nframes, final_beat, final_process_index, start_sample, _loop_cnt)); } } else { - const samplepos_t final_processed_sample = tmap->sample_at (timepos_t (final_beat)); - const samplecnt_t nproc = (final_processed_sample - start_sample); + const samplepos_t final_process_index = tmap->sample_at (timepos_t (final_beat)); + const samplecnt_t nproc = (final_process_index - start_sample); if (nproc > orig_nframes) { /* tempo map changed, probably */ @@ -3471,7 +3490,7 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en } _loop_cnt++; _state = Stopped; - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached final event, now stopped, nf = %2 fb %3 fs %4 %5 LC %6\n", index(), nframes, final_beat, final_processed_sample, start_sample, _loop_cnt)); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached final event, now stopped, nf = %2 fb %3 fs %4 %5 LC %6\n", index(), nframes, final_beat, final_process_index, start_sample, _loop_cnt)); } } else { @@ -4919,6 +4938,8 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp const Location* const loop_loc = _loop_location; + DEBUG_TRACE (DEBUG::Triggers, string_compose ("**** Triggerbox::run(%1) from %2 to %3 nf = %4\n", order(), start_sample, end_sample, nframes)); + if (!loop_loc) { run_cycle (bufs, start_sample, end_sample, speed, nframes); } else { @@ -4956,7 +4977,7 @@ TriggerBox::run_cycle (BufferSet& bufs, samplepos_t start_sample, samplepos_t en const Temporal::Beats __end_beats (timepos_t (end_sample).beats()); const double __bpm = __tmap->quarters_per_minute_at (timepos_t (__start_beats)); - DEBUG_TRACE (DEBUG::Triggers, string_compose ("**** Triggerbox::run() for %6, ss %1 es %2 sb %3 eb %4 bpm %5 nf %7\n", start_sample, end_sample, __start_beats, __end_beats, __bpm, order(), nframes)); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("**** Triggerbox::run_cycle() for %6, ss %1 es %2 sb %3 eb %4 bpm %5 nf %7\n", start_sample, end_sample, __start_beats, __end_beats, __bpm, order(), nframes)); } #endif @@ -5566,15 +5587,6 @@ TriggerBox::Request::operator delete (void *ptr, size_t /*size*/) return pool->release (ptr); } -void -TriggerBox::request_reload (int32_t slot, void* ptr) -{ - Request* r = new Request (Request::Reload); - r->slot = slot; - r->ptr = ptr; - requests.write (&r, 1); -} - void TriggerBox::process_requests (BufferSet& bufs) { @@ -5591,23 +5603,11 @@ TriggerBox::process_request (BufferSet& bufs, Request* req) switch (req->type) { case Request::Use: break; - case Request::Reload: - reload (bufs, req->slot, req->ptr); - break; } delete req; /* back to the pool, RT-safe */ } -void -TriggerBox::reload (BufferSet& bufs, int32_t slot, void* ptr) -{ - if (slot >= (int32_t) all_triggers.size()) { - return; - } - all_triggers[slot]->reload (bufs, ptr); -} - double TriggerBox::position_as_fraction () const { diff --git a/share/scripts/brutalize_midi.lua b/share/scripts/brutalize_midi.lua index bfe8077b6e..7692982aca 100644 --- a/share/scripts/brutalize_midi.lua +++ b/share/scripts/brutalize_midi.lua @@ -1,7 +1,7 @@ ardour { ["type"] = "EditorAction", name = "Brutalize MIDI", license = "MIT", author = "Ardour Team", - description = [[Randomize MIDI Note position (de-quantize).]] + description = [[Randomize MIDI Note position (de-quantize) of selected MIDI regions.]] } function factory () return function ()