diff --git a/libs/ardour/ardour/triggerbox.h b/libs/ardour/ardour/triggerbox.h index 41128649b1..23f7cacb4d 100644 --- a/libs/ardour/ardour/triggerbox.h +++ b/libs/ardour/ardour/triggerbox.h @@ -43,6 +43,7 @@ class XMLNode; namespace ARDOUR { namespace Properties { LIBARDOUR_API extern PBD::PropertyDescriptor running; + LIBARDOUR_API extern PBD::PropertyDescriptor legato; } } @@ -163,6 +164,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful { bool legato () const { return _legato; } virtual void startup (); + virtual void jump_start (); + virtual void jump_stop (); protected: TriggerBox& _box; @@ -184,7 +187,6 @@ class LIBARDOUR_API Trigger : public PBD::Stateful { void request_state (State s); virtual void retrigger() = 0; virtual void set_usable_length () = 0; - void maybe_startup (); }; class LIBARDOUR_API AudioTrigger : public Trigger { @@ -207,6 +209,8 @@ class LIBARDOUR_API AudioTrigger : public Trigger { int set_region (boost::shared_ptr); void startup (); + void jump_start (); + void jump_stop (); XMLNode& get_state (void); int set_state (const XMLNode&, int version); @@ -263,8 +267,9 @@ class LIBARDOUR_API TriggerBox : public Processor void queue_explict (Trigger*); void queue_implicit (Trigger*); + void clear_implicit (); Trigger* get_next_trigger (); - Trigger* peak_next_trigger (); + Trigger* peek_next_trigger (); void prepare_next (size_t current); private: diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index 211ba6389e..3f50900405 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -32,6 +32,7 @@ using std::endl; namespace ARDOUR { namespace Properties { PBD::PropertyDescriptor running; + PBD::PropertyDescriptor legato; } } @@ -129,6 +130,13 @@ Trigger::set_state (const XMLNode& node, int version) return 0; } +void +Trigger::set_legato (bool yn) +{ + _legato = yn; + PropertyChanged (Properties::legato); +} + void Trigger::set_follow_action_probability (int n) { @@ -176,6 +184,26 @@ Trigger::startup() PropertyChanged (ARDOUR::Properties::running); } +void +Trigger::jump_start() +{ + /* this is used when we start a new trigger in legato mode. We do not + wait for quantization. + */ + _state = Running; + PropertyChanged (ARDOUR::Properties::running); +} + +void +Trigger::jump_stop() +{ + /* this is used when we start a new trigger in legato mode. We do not + wait for quantization. + */ + _state = Stopped; + PropertyChanged (ARDOUR::Properties::running); +} + void Trigger::process_state_requests () { @@ -188,7 +216,7 @@ Trigger::process_state_requests () switch (new_state) { case Stopped: if (_state != WaitingToStop) { - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (_state), enum_2_string (WaitingToStop))); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 => %3\n", index(), enum_2_string (_state), enum_2_string (WaitingToStop))); _state = WaitingToStop; PropertyChanged (ARDOUR::Properties::running); } @@ -219,21 +247,22 @@ Trigger::process_state_requests () case Running: switch (launch_style()) { case OneShot: - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (Running), enum_2_string (WaitingForRetrigger))); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 oneshot %2 => %3\n", index(), enum_2_string (Running), enum_2_string (WaitingForRetrigger))); _state = WaitingForRetrigger; PropertyChanged (ARDOUR::Properties::running); break; case Gate: case Toggle: case Repeat: - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (Running), enum_2_string (Stopped))); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 gate/toggle/repeat => %3\n", index(), enum_2_string (Running), enum_2_string (WaitingToStop))); _state = WaitingToStop; + _box.clear_implicit (); PropertyChanged (ARDOUR::Properties::running); } break; case Stopped: - DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (Stopped), enum_2_string (WaitingToStart))); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 stopped => %3\n", index(), enum_2_string (Stopped), enum_2_string (WaitingToStart))); _box.queue_explict (this); break; @@ -353,6 +382,20 @@ AudioTrigger::startup () retrigger (); } +void +AudioTrigger::jump_start () +{ + Trigger::jump_start (); + retrigger (); +} + +void +AudioTrigger::jump_stop () +{ + Trigger::jump_stop (); + retrigger (); +} + XMLNode& AudioTrigger::get_state (void) { @@ -655,6 +698,7 @@ void AudioTrigger::retrigger () { read_index = _start_offset + _legato_offset; + _legato_offset = 0; /* used one time only */ DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to %2\n", _index, read_index)); } @@ -691,7 +735,7 @@ AudioTrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bo /* We reached the end */ - if ((_launch_style == Repeat) || (_box.peak_next_trigger() == this)) { /* self repeat */ + if ((_launch_style == Repeat) || (_box.peek_next_trigger() == this)) { /* self repeat */ nframes -= this_read; dest_offset += this_read; DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end, but set to loop, so retrigger\n", index())); @@ -771,6 +815,12 @@ TriggerBox::TriggerBox (Session& s, DataType dt) midi_trigger_map.insert (midi_trigger_map.end(), std::make_pair (uint8_t (69), 9)); } +void +TriggerBox::clear_implicit () +{ + implicit_queue.reset (); +} + void TriggerBox::queue_explict (Trigger* t) { @@ -796,8 +846,12 @@ TriggerBox::queue_implicit (Trigger* t) } Trigger* -TriggerBox::peak_next_trigger () +TriggerBox::peek_next_trigger () { + /* allows us to check if there's a next trigger queued, without + * actually reading it from either of the queues. + */ + RingBuffer::rw_vector rwv; explicit_queue.get_read_vector (&rwv); @@ -1015,6 +1069,8 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp all_triggers[n]->process_state_requests (); } + Trigger* nxt = 0; + if (!currently_playing) { if ((currently_playing = get_next_trigger ()) != 0) { currently_playing->startup (); @@ -1031,11 +1087,6 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp _session.start_transport_from_processor (); } - if (_stop_all) { - stop_all (); - _stop_all = false; - } - timepos_t start (start_sample); timepos_t end (end_sample); Temporal::Beats start_beats (start.beats()); @@ -1044,6 +1095,47 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp size_t max_chans = 0; bool first = false; + /* see if there's another trigger explicitly queued that has legato set. */ + + RingBuffer::rw_vector rwv; + explicit_queue.get_read_vector (&rwv); + + if (rwv.len[0] > 0) { + + /* actually fetch it (guaranteed to pull from the explicit queue */ + + nxt = get_next_trigger (); + + /* if user triggered same clip, with legato set, then there is + * nothing to do + */ + + if (nxt != currently_playing) { + + if (nxt->legato()) { + /* We want to start this trigger immediately, without + * waiting for quantization points, and it should start + * playing at the same internal offset as the current + * trigger. + */ + + nxt->set_legato_offset (currently_playing->current_pos()); + nxt->jump_start (); + currently_playing->jump_stop (); + prepare_next (nxt->index()); + /* and switch */ + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 => %2 switched to in legato mode\n", currently_playing->index(), nxt->index())); + currently_playing = nxt; + + } + } + } + + if (_stop_all) { + stop_all (); + _stop_all = false; + } + while (currently_playing) { assert (currently_playing->state() >= Trigger::WaitingToStart); @@ -1164,6 +1256,8 @@ TriggerBox::prepare_next (size_t current) { int nxt = determine_next_trigger (current); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("nxt for %1 = %2\n", current, nxt)); + if (nxt >= 0) { queue_implicit (all_triggers[nxt]); } @@ -1234,6 +1328,7 @@ TriggerBox::determine_next_trigger (size_t current) } if (n == current) { + cerr << "outa here\n"; break; } @@ -1370,5 +1465,3 @@ TriggerBox::set_state (const XMLNode& node, int version) return 0; } - -/*--------------------*/