Compare commits

...

10 Commits

Author SHA1 Message Date
Ben Loftis 42a4216f22 osc: publish session-provided trigger functions: bang/unbang 2022-10-01 09:13:14 -05:00
Ben Loftis 7fe7adb83c push2: adapt to new unbang and 'stop' semantics (needs testing) 2022-10-01 09:13:14 -05:00
Ben Loftis 6c292a83da basic_ui: use session-provided bang/unbang functions 2022-10-01 09:13:14 -05:00
Ben Loftis 29dc388bbc triggerbox: change bang/unbang semantics. UnBang != Stop
'unbang' is better described as a mouse-up or button-release event

* if launch-style is Gate or Repeat, then UnBang will stop the playing clip
* in other launch-styles, UnBang is ignored

some prior code using UnBang will change to
  stop_quantized()  or  request_stop()
2022-10-01 09:13:14 -05:00
Ben Loftis 2829f4385f triggerbox: publish some trigger functions to ::session (for the convenience of control surfaces)
Grid controllers will largely want to access clips in the order they appear on the Cue page

It is up to the device (and/or its ControlProtocol) to handle banking
2022-10-01 09:13:14 -05:00
Ben Loftis 07f47ff6a5 remove unimplemented bang_trigger() and replace with bang_trigger_at(n)
normally we operate on TriggerPtr's which are a safe way to track
 trigger lifetime, safely modify their properties, and launch them.

bang_trigger_at() is a convenience function to look up a trigger by index,
 and launch it, in one step.  Potentially useful for control surfaces.
2022-10-01 09:13:14 -05:00
Ben Loftis 1e283adb22 disambiguate trigger functions: stop_all_triggers -> trigger_stop_all 2022-10-01 09:13:14 -05:00
Ben Loftis 75ae0fd4b1 disambiguate trigger functions: cue_bang -> trigger_cue_row 2022-10-01 09:13:14 -05:00
Ben Loftis ddfefa2583 remove some debug printfs 2022-10-01 09:13:14 -05:00
Ben Loftis be28c9ff88 osc: add functions to trigger cue rows, and stop all triggers 2022-10-01 09:13:14 -05:00
17 changed files with 256 additions and 77 deletions

View File

@ -387,9 +387,9 @@ CueBoxUI::set_all_quantization (Temporal::BBT_Offset const& q, uint64_t idx)
}
void
CueBoxUI::trigger_cue (uint64_t n)
CueBoxUI::trigger_cue_row (uint64_t n)
{
_session->cue_bang (n);
_session->trigger_cue_row (n);
}
void
@ -439,7 +439,7 @@ CueBoxUI::event (GdkEvent* ev, uint64_t n)
switch (ev->type) {
case GDK_BUTTON_PRESS:
if (ev->button.button==1) {
trigger_cue (n);
trigger_cue_row (n);
}
break;
case GDK_2BUTTON_PRESS:

View File

@ -76,7 +76,7 @@ public:
CueBoxUI (ArdourCanvas::Item* parent);
~CueBoxUI ();
void trigger_cue (uint64_t n);
void trigger_cue_row (uint64_t n);
void _size_allocate (ArdourCanvas::Rect const&);

View File

@ -579,9 +579,9 @@ CueMaster::event_handler (GdkEvent* ev)
case GDK_BUTTON_PRESS:
if (ev->button.button == 1) {
if (Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier)) {
_session->stop_all_triggers (true); //stop 'now'
_session->trigger_stop_all (true); //stop 'now'
} else {
_session->stop_all_triggers (false); //stop quantized (bar end)
_session->trigger_stop_all (false); //stop quantized (bar end)
}
return true;
}

View File

@ -163,17 +163,17 @@ TriggerUI::register_actions ()
const std::string action_name = string_compose ("trigger-cue-%1", n);
const std::string display_name = string_compose (_("Trigger Cue %1"), cue_marker_name (n));
ActionManager::register_action (trigger_actions, action_name.c_str (), display_name.c_str (), sigc::bind (sigc::ptr_fun (TriggerUI::trigger_cue), n));
ActionManager::register_action (trigger_actions, action_name.c_str (), display_name.c_str (), sigc::bind (sigc::ptr_fun (TriggerUI::trigger_cue_row), n));
}
}
void
TriggerUI::trigger_cue (int32_t n)
TriggerUI::trigger_cue_row (int32_t n)
{
Session* s = AudioEngine::instance()->session();
if (s) {
s->cue_bang (n);
s->trigger_cue_row (n);
}
}

View File

@ -93,7 +93,7 @@ private:
/* Actions for Triggers: accessed via ardour_ui and shortcuts and lua */
static Glib::RefPtr<Gtk::ActionGroup> trigger_actions;
static void trigger_cue (int32_t);
static void trigger_cue_row (int32_t);
static Gtkmm2ext::Bindings* bindings;
static void load_bindings ();
static void register_actions ();

View File

@ -490,7 +490,7 @@ public:
double default_play_speed ();
void reset_transport_speed (TransportRequestSource origin = TRS_UI);
void stop_all_triggers (bool now = true);
void trigger_stop_all (bool now = true);
void request_transport_speed (double speed, TransportRequestSource origin = TRS_UI);
void request_default_play_speed (double speed, TransportRequestSource origin = TRS_UI);
@ -1371,9 +1371,12 @@ public:
PBD::TimingStats dsp_stats[NTT];
int32_t first_cue_within (samplepos_t s, samplepos_t e, bool& was_recorded);
void cue_bang (int32_t);
void trigger_cue_row (int32_t);
CueEvents const & cue_events() const { return _cue_events; }
bool bang_trigger_at(int32_t route_index, int32_t row_index);
bool unbang_trigger_at(int32_t route_index, int32_t row_index);
protected:
friend class AudioEngine;
void set_block_size (pframes_t nframes);

View File

@ -256,9 +256,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
*/
void bang ();
/* Calling ::unbang() will cause a running Trigger to begin the process
of stopping. If the Trigger is not running, it will move it to a
full Stopped state.
/* Calling ::unbang() is equivalent to a mouse-up or note-off
... it MIGHT cause a clip to stop, but more likely has no effect, depending on the slot's launch-style.
*/
void unbang ();
@ -267,6 +266,10 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
*/
void request_stop ();
/* Call ::stop_quantized() to stop a Trigger at the next quantization point.
*/
void stop_quantized ();
virtual void tempo_map_changed() {}
virtual pframes_t run (BufferSet&, samplepos_t start_sample, samplepos_t end_sample,
@ -726,8 +729,9 @@ class LIBARDOUR_API TriggerBox : public Processor
TriggerPtr trigger (Triggers::size_type);
bool bang_trigger (TriggerPtr);
bool unbang_trigger (TriggerPtr);
void bang_trigger_at (Triggers::size_type row);
void unbang_trigger_at (Triggers::size_type row);
void add_trigger (TriggerPtr);
void fast_forward (CueEvents const &, samplepos_t transport_postiion);

View File

@ -1741,10 +1741,8 @@ Route::stop_triggers (bool now)
{
if (_triggerbox) {
if (now) {
std::cerr << "stop immedaitely\n";
_triggerbox->stop_all_immediately ();
} else {
std::cerr << "stop quantized\n";
_triggerbox->stop_all_quantized();
}
}

View File

@ -1721,12 +1721,65 @@ Session::cue_marker_change (Location* /* ignored */)
}
void
Session::cue_bang (int32_t cue)
Session::trigger_cue_row (int32_t cue)
{
_pending_cue.store (cue);
request_transport_speed (1.0);
}
bool
Session::bang_trigger_at (int32_t route_index, int32_t row_index)
{
/* this is a convenience function for simple control surfaces to bang a trigger without any regards to banking */
int index = 0;
StripableList sl;
get_stripables (sl);
sl.sort (Stripable::Sorter ());
for (StripableList::iterator s = sl.begin (); s != sl.end (); ++s) {
boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*s);
if (!r || !r->triggerbox ()) {
continue;
}
/* we're only interested in Trigger Tracks */
if (!(r->presentation_info ().trigger_track ())) {
continue;
}
if (index == route_index) {
r->triggerbox()->bang_trigger_at(row_index);
return 1;
}
index++;
}
}
bool
Session::unbang_trigger_at (int32_t route_index, int32_t row_index)
{
/* this is a convenience function for simple control surfaces to un-bang a trigger without any regards to banking */
int index = 0;
StripableList sl;
get_stripables (sl);
sl.sort (Stripable::Sorter ());
for (StripableList::iterator s = sl.begin (); s != sl.end (); ++s) {
boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*s);
if (!r || !r->triggerbox ()) {
continue;
}
/* we're only interested in Trigger Tracks */
if (!(r->presentation_info ().trigger_track ())) {
continue;
}
if (index == route_index) {
r->triggerbox()->unbang_trigger_at(row_index);
return 1;
}
index++;
}
}
void
Session::maybe_find_pending_cue ()
{

View File

@ -434,7 +434,7 @@ Session::set_transport_speed (double speed)
/** Called from the gui thread */
void
Session::stop_all_triggers (bool now)
Session::trigger_stop_all (bool now)
{
boost::shared_ptr<RouteList> rl = routes.reader();

View File

@ -781,6 +781,10 @@ Trigger::jump_stop (BufferSet& bufs, pframes_t dest_offset)
void
Trigger::begin_stop (bool explicit_stop)
{
if (_state == Stopped) {
return; /* nothing to do */
}
/* this is used when we start a tell a currently playing trigger to
stop, but wait for quantization first.
*/
@ -790,6 +794,12 @@ Trigger::begin_stop (bool explicit_stop)
send_property_change (ARDOUR::Properties::running);
}
void
Trigger::stop_quantized ()
{
begin_stop(true);
}
void
Trigger::begin_switch (TriggerPtr nxt)
{
@ -845,14 +855,16 @@ Trigger::process_state_requests (BufferSet& bufs, pframes_t dest_offset)
_state = WaitingForRetrigger;
send_property_change (ARDOUR::Properties::running);
break;
case Gate:
case Toggle:
stop_quantized ();
break;
case Gate:
case Repeat:
if (_box.active_scene() >= 0) {
std::cerr << "should not happen, cue launching but launch_style() said " << enum_2_string (launch_style()) << std::endl;
} else {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 gate/toggle/repeat => %3\n", index(), enum_2_string (Running), enum_2_string (WaitingToStop)));
begin_stop (true);
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 gate/repeat => %3\n", index(), enum_2_string (Running), enum_2_string (WaitingToStop)));
stop_quantized ();
}
}
break;
@ -880,8 +892,19 @@ Trigger::process_state_requests (BufferSet& bufs, pframes_t dest_offset)
switch (_state) {
case Running:
begin_stop (true);
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 unbanged, now in WaitingToStop\n", index()));
switch (launch_style()) {
case OneShot:
case ReTrigger:
case Toggle:
/* unbang does nothing, just let it keep playing */
break;
case Gate:
request_stop (); /* stop now */
break;
case Repeat:
stop_quantized ();
break;
}
break;
case Stopped:
@ -3435,7 +3458,7 @@ TriggerBox::queue_explict (uint32_t n)
DEBUG_TRACE (DEBUG::Triggers, string_compose ("explicit queue %1, EQ = %2\n", n, explicit_queue.read_space()));
if (_currently_playing) {
_currently_playing->unbang ();
_currently_playing->begin_stop (false); /* @paul is this necessary/desired? the current clip should stop (only) when the new one starts */
}
}
@ -3664,7 +3687,31 @@ void
TriggerBox::stop_all_quantized ()
{
for (uint32_t n = 0; n < all_triggers.size(); ++n) {
all_triggers[n]->unbang ();
all_triggers[n]->stop_quantized ();
}
}
void
TriggerBox::bang_trigger_at (Triggers::size_type row)
{
TriggerPtr t = trigger(row);
if (t && t->region()) {
t->bang();
} else {
/* by convention, an empty slot is effectively a STOP button */
stop_all_quantized();
}
}
void
TriggerBox::unbang_trigger_at (Triggers::size_type row)
{
TriggerPtr t = trigger(row);
if (t && t->region()) {
t->unbang();
} else {
/* you shouldn't be able to unbang an empty slot; but if this somehow happens we'll just treat it as a */
stop_all_quantized();
}
}
@ -3981,7 +4028,7 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
*/
if (_currently_playing) {
_currently_playing->unbang ();
_currently_playing->stop_quantized ();
}
_locate_armed = false;

View File

@ -462,6 +462,18 @@ BasicUI::transport_rolling () const
return !session->transport_stopped_or_stopping ();
}
void
BasicUI::trigger_stop_all (bool stop_all)
{
session->trigger_stop_all (stop_all);
}
void
BasicUI::trigger_cue_row (int cue_idx)
{
session->trigger_cue_row (cue_idx);
}
void
BasicUI::undo ()
{
@ -824,43 +836,15 @@ BasicUI::find_trigger (int x, int y)
}
void
BasicUI::bang (int x, int y)
BasicUI::bang_trigger_at (int x, int y)
{
boost::shared_ptr<Route> r = session->get_remote_nth_route (x);
if (!r) {
return;
}
boost::shared_ptr<TriggerBox> tb = r->triggerbox();
if (!tb || !tb->active()) {
return;
}
TriggerPtr tp (tb->trigger (y));
if (!tp) {
return;
}
if (tp) {
tp->bang ();
}
session->bang_trigger_at (x, y);
}
void
BasicUI::unbang (int x)
BasicUI::unbang_trigger_at (int x, int y)
{
boost::shared_ptr<Route> r = session->get_remote_nth_route (x);
if (!r) {
return;
}
boost::shared_ptr<TriggerBox> tb = r->triggerbox();
if (!tb || !tb->active()) {
return;
}
tb->stop_all_quantized ();
session->unbang_trigger_at (x, y);
}

View File

@ -100,6 +100,9 @@ class LIBCONTROLCP_API BasicUI {
void toggle_click();
void midi_panic();
void trigger_cue_row (int cue);
void trigger_stop_all (bool stop_now = false);
void toggle_monitor_mute();
void toggle_monitor_dim();
void toggle_monitor_mono();
@ -164,9 +167,9 @@ class LIBCONTROLCP_API BasicUI {
bool rewind_button_onoff() const;
bool loop_button_onoff() const;
void bang (int x, int y);
/* stop whatever is playing in the nth triggerbox */
void unbang (int x);
void bang_trigger_at (int x, int y);
void unbang_trigger_at (int x, int y);
/* it would be nice to use TriggerPtr here but that implies including ardour/triggerbox.h */
boost::shared_ptr<ARDOUR::Trigger> find_trigger (int x, int y);

View File

@ -458,6 +458,14 @@ OSC::register_callbacks()
REGISTER_CALLBACK (serv, X_("/set_transport_speed"), "f", set_transport_speed);
// locate ii is position and bool roll
REGISTER_CALLBACK (serv, X_("/locate"), "ii", locate);
REGISTER_CALLBACK (serv, X_("/trigger_cue_row"), "i", trigger_cue_row);
REGISTER_CALLBACK (serv, X_("/trigger_stop_all"), "i", trigger_stop_all);
REGISTER_CALLBACK (serv, X_("/trigger_stop"), "ii", trigger_stop); //Route num (position on the Cue page), Stop now
REGISTER_CALLBACK (serv, X_("/trigger_bang"), "ii", trigger_bang); //Route num (position on the Cue page), Trigger index
REGISTER_CALLBACK (serv, X_("/trigger_unbang"), "ii", trigger_unbang); //Route num (position on the Cue page), Trigger index
REGISTER_CALLBACK (serv, X_("/save_state"), "", save_state);
REGISTER_CALLBACK (serv, X_("/save_state"), "f", save_state);
REGISTER_CALLBACK (serv, X_("/prev_marker"), "", prev_marker);
@ -5432,6 +5440,62 @@ OSC::route_plugin_parameter (int ssid, int piid, int par, float val, lo_message
return 0;
}
int
OSC::trigger_stop (int route_index, int now, lo_message msg)
{
if (!session) {
return -1;
}
/* this function doesn't refer to surface banking but instead directly controls routes in the order they appear on the trigger_page
*/
int index = 0;
StripableList sl;
session->get_stripables (sl);
sl.sort (Stripable::Sorter ());
for (StripableList::iterator s = sl.begin (); s != sl.end (); ++s) {
boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*s);
if (!r || !r->triggerbox ()) {
continue;
}
/* we're only interested in Trigger Tracks */
if (!(r->presentation_info ().trigger_track ())) {
continue;
}
if (index == route_index) {
r->stop_triggers(now!=0);
break;
}
index++;
}
return 0;
}
int
OSC::trigger_bang (int route_index, int row_index, lo_message msg)
{
if (!session) {
return -1;
}
bang_trigger_at(route_index, row_index);
return 0;
}
int
OSC::trigger_unbang (int route_index, int row_index, lo_message msg)
{
if (!session) {
return -1;
}
unbang_trigger_at(route_index, row_index);
return 0;
}
//prints to cerr only
int
OSC::route_plugin_parameter_print (int ssid, int piid, int par, lo_message msg)

View File

@ -486,6 +486,9 @@ class OSC : public ARDOUR::ControlProtocol, public AbstractUI<OSCUIRequest>
return 0; \
}
PATH_CALLBACK1(trigger_cue_row,i,);
PATH_CALLBACK1(trigger_stop_all,i,); //0 = "stop at end of bar" 1 = "stop now"
PATH_CALLBACK1(set_transport_speed,f,);
PATH_CALLBACK1(add_marker_name,s,&);
PATH_CALLBACK1(access_action,s,&);
@ -629,6 +632,9 @@ class OSC : public ARDOUR::ControlProtocol, public AbstractUI<OSCUIRequest>
PATH_CALLBACK1_MSG(route_plugin_list,i);
PATH_CALLBACK2_MSG(route_plugin_descriptor,i,i);
PATH_CALLBACK2_MSG(route_plugin_reset,i,i);
PATH_CALLBACK2_MSG(trigger_bang,i,i);
PATH_CALLBACK2_MSG(trigger_unbang,i,i);
PATH_CALLBACK2_MSG(trigger_stop,i,i); /* second arg is 'stop now' */
int strip_parse (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg);
int master_parse (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg);
@ -652,6 +658,10 @@ class OSC : public ARDOUR::ControlProtocol, public AbstractUI<OSCUIRequest>
int route_plugin_descriptor(int ssid, int piid, lo_message msg);
int route_plugin_reset(int ssid, int piid, lo_message msg);
int trigger_bang(int rid, int stop_now, lo_message msg);
int trigger_unbang(int rid, int stop_now, lo_message msg);
int trigger_stop(int rid, int row_id, lo_message msg);
//banking functions
int set_bank (uint32_t bank_start, lo_message msg);
int _set_bank (uint32_t bank_start, lo_address addr);

View File

@ -274,16 +274,19 @@ CueLayout::show_knob_function ()
void
CueLayout::button_lower (uint32_t n)
{
boost::shared_ptr<Route> r = _session.get_remote_nth_route (n);
if (!r) {
return;
}
if (_p2.stop_down() || _long_stop) {
_p2.unbang (n + track_base);
boost::shared_ptr<TriggerBox> tb = r->triggerbox();
if (tb) {
tb->stop_all_quantized();
}
} else {
/* select track */
boost::shared_ptr<Route> r = _session.get_remote_nth_route (n);
if (r) {
_session.selection().set (r, boost::shared_ptr<AutomationControl>());
}
_session.selection().set (r, boost::shared_ptr<AutomationControl>());
}
}
@ -480,14 +483,14 @@ CueLayout::strip_vpot_touch (int n, bool touching)
void
CueLayout::button_rhs (int row)
{
_p2.get_session().cue_bang (row + scene_base);
_p2.get_session().trigger_cue_row (row + scene_base);
}
void
CueLayout::button_stop_press ()
{
if (_p2.modifier_state() == Push2::ModShift) {
_p2.get_session().stop_all_triggers (false); /* quantized global stop */
_p2.get_session().trigger_stop_all (false); /* quantized global stop */
}
}
void
@ -576,15 +579,24 @@ CueLayout::pad_press (int y, int x) /* fix coordinate order one day */
return;
}
if (!tb->trigger (y + scene_base)->region()) {
_p2.unbang (x + track_base);
tb->bang_trigger_at (y + scene_base);
}
void
CueLayout::pad_release (int y, int x) /* fix coordinate order one day */
{
if (!_route[x]) {
return;
}
_p2.bang (x + track_base, y + scene_base);
boost::shared_ptr<TriggerBox> tb = _route[x]->triggerbox();
if (!tb) {
/* unpossible! */
return;
}
tb->unbang_trigger_at (y + scene_base);
}
void

View File

@ -83,6 +83,7 @@ class CueLayout : public Push2Layout
void strip_vpot_touch (int, bool);
void pad_press (int x, int y);
void pad_release (int x, int y);
/* override to use for clip progress */
void update_meters();