Compare commits

...

17 commits

Author SHA1 Message Date
Ben Loftis
d6815d7e40 trigger page: rough-in a control for Cue Recording 2022-01-21 17:45:26 -06:00
Ben Loftis
094fbfa132 trigger page: shrink Cue column to minimum (?) 2022-01-21 17:45:26 -06:00
Ben Loftis
b0a2d678dc trigger_ui: implement direct-Jump actions from trigger slots; context-menu and icons 2022-01-21 17:45:26 -06:00
Ben Loftis
8ed6f685cf trigger_ui: remove Jump action from trigger-master (column), seems pointless? 2022-01-21 17:45:26 -06:00
Ben Loftis
f62c4908e0 trigger_ui: implement direct-Jump actions for the slot properties (left&right FA) 2022-01-21 17:45:26 -06:00
Ben Loftis
a23f3aec50 trigger_ui: implement direct Jump actions for Cues (rows) 2022-01-21 17:45:26 -06:00
Robin Gareus
fc9f796257
Revert "Process-lock is required when changing processors"
This reverts commit 0a213e71b3.
which needs a better solution to prevent deadlock in case the lock is
already taken.
2022-01-22 00:31:54 +01:00
Robin Gareus
0a213e71b3
Process-lock is required when changing processors
This is somewhat unfortunate, since session.cc also takes the
lock shortly afterward to call ->ensure_io()

This should be consolidated before release.
2022-01-22 00:06:13 +01:00
Paul Davis
2fa8c7cd42 triggerbox: if any cues were recorded, remove all existing cue markers in transport-roll-range before adding new ones 2022-01-21 13:08:47 -07:00
Paul Davis
fd3ddce80a do not play existing cue markers while recording cues 2022-01-21 10:55:55 -07:00
Paul Davis
e14acfc07d prevent multiple cue markers at the same location 2022-01-21 10:55:55 -07:00
Paul Davis
76f4813ee6 triggerbox: experimentally enable cue recording by default 2022-01-21 10:55:55 -07:00
Paul Davis
b023b97538 triggerbox: flush newly recorded cue markers back to RT context 2022-01-21 10:55:55 -07:00
Paul Davis
d830800ed9 triggerbox: add flush-at-transport-stop from recorded cues to locations 2022-01-21 10:55:54 -07:00
Paul Davis
8d1684e1f7 triggerbox: store cue bangs in process thread when discovered 2022-01-21 10:55:54 -07:00
Paul Davis
9abf6bc260 triggerbox: introduce member var to control cue recording 2022-01-21 10:55:54 -07:00
Paul Davis
f63c049d87 triggerbox: add struct and ringbuffer for recording cues 2022-01-21 10:55:54 -07:00
14 changed files with 248 additions and 87 deletions

View file

@ -92,7 +92,7 @@ CueEntry::event_handler (GdkEvent* ev)
break;
case GDK_ENTER_NOTIFY:
if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) {
// name_button->set_fill_color (UIConfiguration::instance ().color ("neutral:foregroundest"));
name_button->set_fill_color (UIConfiguration::instance ().color ("neutral:foregroundest"));
set_fill_color (HSV (fill_color ()).lighter (0.15).color ());
}
break;
@ -123,7 +123,7 @@ CueEntry::_size_allocate (ArdourCanvas::Rect const& alloc)
name_text->size_allocate (ArdourCanvas::Rect (0, 0, height, height));
name_text->set_position (Duple (4. * scale, 2.5 * scale));
name_text->clamp_width (width - height);
name_text->clamp_width (height);
/* font scale may have changed. uiconfig 'embeds' the ui-scale in the font */
name_text->set_font_description (UIConfiguration::instance ().get_SmallBoldMonospaceFont ());
@ -157,7 +157,7 @@ CueEntry::render (ArdourCanvas::Rect const& area, Cairo::RefPtr<Cairo::Context>
render_children (area, context);
{ //Play triangle, needs to match TriggerEntry buttons exactly
if (false) { //Play triangle, needs to match TriggerEntry buttons exactly
context->set_line_width (1 * scale);
float margin = 4 * scale;
@ -249,7 +249,14 @@ CueBoxUI::context_menu (uint64_t idx)
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::Again)), sigc::bind (sigc::mem_fun (*this, &CueBoxUI::set_all_follow_action), FollowAction (FollowAction::Again), idx)));
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::ReverseTrigger)), sigc::bind (sigc::mem_fun (*this, &CueBoxUI::set_all_follow_action), FollowAction (FollowAction::ReverseTrigger), idx)));
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::ForwardTrigger)), sigc::bind (sigc::mem_fun (*this, &CueBoxUI::set_all_follow_action), FollowAction (FollowAction::ForwardTrigger), idx)));
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::JumpTrigger)), sigc::bind (sigc::mem_fun (*this, &CueBoxUI::set_all_follow_action), FollowAction (FollowAction::JumpTrigger), idx)));
Menu* jump_menu = manage (new Menu);
MenuList& jitems = jump_menu->items ();
for (int i = 0; i < default_triggers_per_box; i++) {
FollowAction jump_fa = (FollowAction::JumpTrigger);
jump_fa.targets.set(i);
jitems.push_back (MenuElem (string_compose ("%1", (char)('A' + i)), sigc::bind (sigc::mem_fun (*this, &CueBoxUI::set_all_follow_action), jump_fa, idx)));
}
fitems.push_back (MenuElem (_("Jump..."), *jump_menu));
Menu* launch_menu = manage (new Menu);
MenuList& litems = launch_menu->items ();

View file

@ -142,7 +142,15 @@ SlotPropertyTable::SlotPropertyTable ()
_follow_left.AddMenuElem (MenuElem (follow_action_to_string(FollowAction (FollowAction::Again)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), FollowAction (FollowAction::Again), 0)));
_follow_left.AddMenuElem (MenuElem (follow_action_to_string(FollowAction (FollowAction::ReverseTrigger)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), FollowAction (FollowAction::ReverseTrigger), 0)));
_follow_left.AddMenuElem (MenuElem (follow_action_to_string(FollowAction (FollowAction::ForwardTrigger)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), FollowAction (FollowAction::ForwardTrigger), 0)));
_follow_left.AddMenuElem (MenuElem (follow_action_to_string(FollowAction (FollowAction::JumpTrigger)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), FollowAction (FollowAction::JumpTrigger), 0)));
Menu* jump_menu = manage (new Menu);
MenuList& jitems = jump_menu->items ();
for (int i = 0; i < default_triggers_per_box; i++) {
FollowAction jump_fa = (FollowAction::JumpTrigger);
jump_fa.targets.set(i);
jitems.push_back (MenuElem (string_compose ("%1", (char)('A' + i)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), jump_fa, 0)));
}
//jitems.push_back (MenuElem ("Combo...", sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), FollowAction (FollowAction::JumpTrigger), 0)));
_follow_left.AddMenuElem (MenuElem (_("Jump..."), *jump_menu));
_follow_left.set_sizing_text (longest_follow);
_follow_right.set_name("FollowAction");
@ -151,7 +159,14 @@ SlotPropertyTable::SlotPropertyTable ()
_follow_right.AddMenuElem (MenuElem (follow_action_to_string(FollowAction (FollowAction::Again)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), FollowAction (FollowAction::Again), 1)));
_follow_right.AddMenuElem (MenuElem (follow_action_to_string(FollowAction (FollowAction::ReverseTrigger)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), FollowAction (FollowAction::ReverseTrigger), 1)));
_follow_right.AddMenuElem (MenuElem (follow_action_to_string(FollowAction (FollowAction::ForwardTrigger)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), FollowAction (FollowAction::ForwardTrigger), 1)));
_follow_right.AddMenuElem (MenuElem (follow_action_to_string(FollowAction (FollowAction::JumpTrigger)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), FollowAction (FollowAction::JumpTrigger), 1)));
Menu* jump_menu_1 = manage (new Menu);
MenuList& jitems_1 = jump_menu_1->items ();
for (int i = 0; i < default_triggers_per_box; i++) {
FollowAction jump_fa = (FollowAction::JumpTrigger);
jump_fa.targets.set(i);
jitems_1.push_back (MenuElem (string_compose ("%1", (char)('A' + i)), sigc::bind (sigc::mem_fun (*this, &SlotPropertyTable::set_follow_action), jump_fa, 1)));
}
_follow_right.AddMenuElem (MenuElem (_("Jump..."), *jump_menu_1));
_follow_right.set_sizing_text (longest_follow);
_launch_style_button.set_name("FollowAction");

View file

@ -346,7 +346,6 @@ TriggerMaster::context_menu ()
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::Again)), sigc::bind (sigc::mem_fun (*this, &TriggerMaster::set_all_follow_action), FollowAction (FollowAction::Again))));
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::ForwardTrigger)), sigc::bind (sigc::mem_fun (*this, &TriggerMaster::set_all_follow_action), FollowAction (FollowAction::ForwardTrigger))));
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::ReverseTrigger)), sigc::bind (sigc::mem_fun (*this, &TriggerMaster::set_all_follow_action), FollowAction (FollowAction::ReverseTrigger))));
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::JumpTrigger)), sigc::bind (sigc::mem_fun (*this, &TriggerMaster::set_all_follow_action), FollowAction (FollowAction::JumpTrigger))));
Menu* launch_menu = manage (new Menu);
MenuList& litems = launch_menu->items ();
@ -565,7 +564,7 @@ CueMaster::CueMaster (Item* parent)
Event.connect (sigc::mem_fun (*this, &CueMaster::event_handler));
stop_shape = new ArdourCanvas::Polygon (this);
stop_shape->set_outline (true);
stop_shape->set_outline (false);
stop_shape->set_fill (true);
stop_shape->name = X_("stopbutton");
stop_shape->set_ignore_events (true);
@ -640,7 +639,6 @@ CueMaster::event_handler (GdkEvent* ev)
break;
case GDK_ENTER_NOTIFY:
if (ev->crossing.detail != GDK_NOTIFY_INFERIOR) {
stop_shape->set_outline_color (UIConfiguration::instance ().color ("neutral:foreground"));
stop_shape->set_fill_color (UIConfiguration::instance ().color ("neutral:foreground"));
set_fill_color (HSV (fill_color ()).lighter (0.25).color ());
}
@ -668,7 +666,7 @@ CueMaster::_size_allocate (ArdourCanvas::Rect const& alloc)
Rectangle::_size_allocate (alloc);
const double scale = UIConfiguration::instance ().get_ui_scale ();
_poly_margin = 2. * scale;
_poly_margin = 2 * scale;
const Distance width = _rect.width ();
const Distance height = _rect.height ();
@ -690,8 +688,7 @@ void
CueMaster::set_default_colors ()
{
set_fill_color (HSV (UIConfiguration::instance ().color ("theme:bg")).darker (0.5).color ());
stop_shape->set_outline_color (UIConfiguration::instance ().color ("neutral:foreground"));
stop_shape->set_fill_color (fill_color());
stop_shape->set_fill_color (UIConfiguration::instance ().color ("neutral:midground"));
}
void
@ -723,8 +720,6 @@ CueMaster::context_menu ()
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::Again)), sigc::bind (sigc::mem_fun (*this, &CueMaster::set_all_follow_action), FollowAction (FollowAction::Again))));
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::ForwardTrigger)), sigc::bind (sigc::mem_fun (*this, &CueMaster::set_all_follow_action), FollowAction (FollowAction::ForwardTrigger))));
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::ReverseTrigger)), sigc::bind (sigc::mem_fun (*this, &CueMaster::set_all_follow_action), FollowAction (FollowAction::ReverseTrigger))));
fitems.push_back (MenuElem (TriggerUI::follow_action_to_string(FollowAction (FollowAction::JumpTrigger)), sigc::bind (sigc::mem_fun (*this, &CueMaster::set_all_follow_action), FollowAction (FollowAction::JumpTrigger))));
Menu* launch_menu = manage (new Menu);
MenuList& litems = launch_menu->items ();

View file

@ -66,8 +66,8 @@ using namespace std;
TriggerPage::TriggerPage ()
: Tabbable (_content, _("Trigger Drom"), X_("trigger"))
, _cue_area_frame (0.5, 0, 1.0, 0)
, _cue_box (32, 16 * default_triggers_per_box)
, _master_widget (32, 16)
, _cue_box (16, 16 * default_triggers_per_box)
, _master_widget (16, 16)
, _master (_master_widget.root ())
{
load_bindings ();
@ -84,6 +84,7 @@ TriggerPage::TriggerPage ()
_cue_area_box.pack_start (*spacer, Gtk::PACK_SHRINK);
_cue_area_box.pack_start (_cue_box, Gtk::PACK_SHRINK);
_cue_area_box.pack_start (_master_widget, Gtk::PACK_SHRINK);
_cue_area_box.pack_start (_cue_rec_enable, Gtk::PACK_SHRINK);
/* left-side frame, same layout as TriggerStrip.
* use Alignment instead of Frame with SHADOW_IN (2px)
@ -286,6 +287,10 @@ TriggerPage::set_session (Session* s)
Editor::instance ().get_selection ().TriggersChanged.connect (sigc::mem_fun (*this, &TriggerPage::selection_changed));
TriggerBox::CueRecordingChanged.connect (_session_connections, invalidator (*this), boost::bind (&TriggerPage::rec_state_changed, this), gui_context ());
rec_state_changed();
_cue_rec_enable.signal_clicked.connect(sigc::mem_fun(*this, &TriggerPage::rec_state_clicked));
initial_track_display ();
_slot_prop_box.set_session (s);
@ -481,6 +486,18 @@ TriggerPage::redisplay_track_list ()
}
}
void
TriggerPage::rec_state_clicked ()
{
TriggerBox::set_cue_recording(!TriggerBox::cue_recording());
}
void
TriggerPage::rec_state_changed ()
{
_cue_rec_enable.set_active_state( TriggerBox::cue_recording() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off);
}
void
TriggerPage::parameter_changed (string const& p)
{

View file

@ -74,6 +74,9 @@ private:
void pi_property_changed (PBD::PropertyChange const&);
void stripable_property_changed (PBD::PropertyChange const&, boost::weak_ptr<ARDOUR::Stripable>);
void rec_state_changed ();
void rec_state_clicked ();
void add_sidebar_page (std::string const&, Gtk::Widget&);
bool no_strip_button_event (GdkEventButton*);
@ -111,6 +114,7 @@ private:
CueBoxWidget _cue_box;
FittedCanvasWidget _master_widget;
CueMaster _master;
ArdourWidgets::ArdourButton _cue_rec_enable;
SlotPropertiesBox _slot_prop_box;

View file

@ -588,40 +588,18 @@ TriggerUI::follow_context_menu ()
_ignore_menu_action = true;
fitems.push_back (RadioMenuElem (fagroup, TriggerUI::follow_action_to_string(FollowAction (FollowAction::None)), sigc::bind(sigc::mem_fun (*this, &TriggerUI::set_follow_action), FollowAction (FollowAction::None))));
if (trigger ()->follow_action (0) == FollowAction::None) {
dynamic_cast<Gtk::CheckMenuItem*> (&fitems.back ())->set_active (true);
}
fitems.push_back (RadioMenuElem (fagroup, TriggerUI::follow_action_to_string(FollowAction (FollowAction::Stop)), sigc::bind(sigc::mem_fun (*this, &TriggerUI::set_follow_action), FollowAction (FollowAction::Stop))));
if (trigger ()->follow_action (0) == FollowAction::Stop) {
dynamic_cast<Gtk::CheckMenuItem*> (&fitems.back ())->set_active (true);
}
fitems.push_back (RadioMenuElem (fagroup, TriggerUI::follow_action_to_string(FollowAction (FollowAction::Again)), sigc::bind(sigc::mem_fun (*this, &TriggerUI::set_follow_action), FollowAction (FollowAction::Again))));
if (trigger ()->follow_action (0) == FollowAction::Again) {
dynamic_cast<Gtk::CheckMenuItem*> (&fitems.back ())->set_active (true);
}
fitems.push_back (RadioMenuElem (fagroup, TriggerUI::follow_action_to_string(FollowAction (FollowAction::ForwardTrigger)), sigc::bind(sigc::mem_fun (*this, &TriggerUI::set_follow_action), FollowAction (FollowAction::ForwardTrigger))));
if (trigger ()->follow_action (0) == FollowAction::ForwardTrigger) {
dynamic_cast<Gtk::CheckMenuItem*> (&fitems.back ())->set_active (true);
}
fitems.push_back (RadioMenuElem (fagroup, TriggerUI::follow_action_to_string(FollowAction (FollowAction::ReverseTrigger)), sigc::bind(sigc::mem_fun (*this, &TriggerUI::set_follow_action), FollowAction (FollowAction::ReverseTrigger))));
if (trigger ()->follow_action (0) == FollowAction::ReverseTrigger) {
dynamic_cast<Gtk::CheckMenuItem*> (&fitems.back ())->set_active (true);
}
#if 0
fitems.push_back (RadioMenuElem (fagroup, TriggerUI::follow_action_to_string(FollowAction (FollowAction::FirstTrigger)), sigc::bind(sigc::mem_fun (*this, &TriggerUI::set_follow_action), FollowAction (FollowAction::FirstTrigger))));
if (trigger ()->follow_action (0) == FollowAction::FirstTrigger) {
dynamic_cast<Gtk::CheckMenuItem*> (&fitems.back ())->set_active (true);
}
fitems.push_back (RadioMenuElem (fagroup, TriggerUI::follow_action_to_string(FollowAction (FollowAction::LastTrigger)), sigc::bind(sigc::mem_fun (*this, &TriggerUI::set_follow_action), FollowAction (FollowAction::LastTrigger))));
if (trigger ()->follow_action (0) == FollowAction::LastTrigger) {
dynamic_cast<Gtk::CheckMenuItem*> (&fitems.back ())->set_active (true);
}
#endif
fitems.push_back (RadioMenuElem (fagroup, TriggerUI::follow_action_to_string(FollowAction (FollowAction::JumpTrigger)), sigc::bind(sigc::mem_fun (*this, &TriggerUI::set_follow_action), FollowAction (FollowAction::JumpTrigger))));
if (trigger ()->follow_action (0) == FollowAction::JumpTrigger) {
dynamic_cast<Gtk::CheckMenuItem*> (&fitems.back ())->set_active (true);
Menu* jump_menu = manage (new Menu);
MenuList& jitems = jump_menu->items ();
for (int i = 0; i < default_triggers_per_box; i++) {
FollowAction jump_fa = (FollowAction::JumpTrigger);
jump_fa.targets.set(i);
jitems.push_back (MenuElem (string_compose ("%1", (char)('A' + i)), sigc::bind (sigc::mem_fun (*this, &TriggerUI::set_follow_action), jump_fa)));
}
fitems.push_back (MenuElem (_("Jump..."), *jump_menu));
_ignore_menu_action = false;

View file

@ -212,41 +212,47 @@ TriggerEntry::draw_follow_icon (Cairo::RefPtr<Cairo::Context> context, FollowAct
break;
case FollowAction::ForwardTrigger:
context->move_to (size / 2, 3 * scale);
context->line_to (size / 2, size - 3 * scale);
context->line_to (size / 2, size - 5 * scale);
context->stroke ();
context->arc (size / 2, 7 * scale, 2 * scale, 0, 2 * M_PI);
set_source_rgba (context, fg_color);
context->fill ();
context->arc (size / 2, 7 * scale, 1 * scale, 0, 2 * M_PI);
set_source_rgba (context, fill_color ());
context->fill ();
set_source_rgba (context, fg_color);
context->arc (size / 2, size - 3 * scale, 2 * scale, 0, 2 * M_PI); // arrow head
context->arc (size / 2, size - 5 * scale, 2 * scale, 0, 2 * M_PI); // arrow head
context->fill ();
break;
case FollowAction::ReverseTrigger:
context->arc (size / 2, 3 * scale, 2 * scale, 0, 2 * M_PI); // arrow head
set_source_rgba (context, fg_color);
context->fill ();
context->move_to (size / 2, 3 * scale);
context->move_to (size / 2, 5 * scale);
context->line_to (size / 2, size - 3 * scale);
context->stroke ();
context->arc (size / 2, size - 7 * scale, 2 * scale, 0, 2 * M_PI);
set_source_rgba (context, fg_color);
context->arc (size / 2, 5 * scale, 2 * scale, 0, 2 * M_PI); // arrow head
context->fill ();
context->arc (size / 2, size - 7 * scale, 1 * scale, 0, 2 * M_PI);
set_source_rgba (context, bg_color);
context->fill ();
break;
/* ben: new shape here ? */
case FollowAction::JumpTrigger: {
if ( icon.targets.count() == 1 ) { //jump to a specific row
int cue_idx = -1;
for (int i = 0; i < default_triggers_per_box; i++) {
if (icon.targets.test(i)) {
cue_idx = i;
break;
}
}
Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (context);
layout->set_font_description (UIConfiguration::instance ().get_SmallMonospaceFont ());
layout->set_text (string_compose ("%1", (char)('A' + cue_idx))); //XXX translate?
int tw, th;
layout->get_pixel_size (tw, th);
context->move_to (size / 2, size / 2);
context->rel_move_to (-tw / 2, -th / 2);
layout->show_in_cairo_context (context);
} else if (false) { // 'ANY' jump
for (int i = 0; i < 6; i++) {
Cairo::Matrix m = context->get_matrix ();
context->translate (size / 2, size / 2);
context->rotate (i * M_PI / 3);
context->move_to (0, 0);
context->line_to (0, (size / 2) - 4 * scale);
context->stroke ();
context->set_matrix (m);
}
context->set_identity_matrix ();
} else { // 'OTHER' jump
context->set_line_width (1.5 * scale);
set_source_rgba (context, HSV (UIConfiguration::instance ().color ("neutral:midground")).lighter (0.25).color ()); // needs to be brighter to maintain balance
for (int i = 0; i < 6; i++) {
@ -259,6 +265,7 @@ TriggerEntry::draw_follow_icon (Cairo::RefPtr<Cairo::Context> context, FollowAct
context->set_matrix (m);
}
context->set_identity_matrix ();
}
} break;
case FollowAction::None:
default:

View file

@ -212,6 +212,8 @@ public:
bool clear_xrun_markers ();
bool clear_ranges ();
void clear_cue_markers (samplepos_t start, samplepos_t end);
void ripple (timepos_t const & at, timecnt_t const & distance, bool include_locked, bool notify);
XMLNode& get_state (void);

View file

@ -1876,6 +1876,7 @@ private:
void set_track_loop (bool);
bool select_playhead_priority_target (samplepos_t&);
void follow_playhead_priority ();
void flush_cue_recording ();
/* These are synchronous and so can only be called from within the process
* cycle

View file

@ -522,6 +522,15 @@ class LIBARDOUR_API TriggerBoxThread
void delete_trigger (Trigger*);
};
struct CueRecord {
int32_t cue_number;
samplepos_t when;
CueRecord (int32_t cn, samplepos_t t): cue_number (cn), when (t) {}
CueRecord () : cue_number (0), when (0) {}
};
typedef PBD::RingBuffer<CueRecord> CueRecords;
class LIBARDOUR_API TriggerBox : public Processor
{
@ -529,6 +538,11 @@ class LIBARDOUR_API TriggerBox : public Processor
TriggerBox (Session&, DataType dt);
~TriggerBox ();
static CueRecords cue_records;
static bool cue_recording () { return _cue_recording; }
static void set_cue_recording (bool yn);
static PBD::Signal0<void> CueRecordingChanged;
void run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool result_required);
bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
bool configure_io (ChanCount in, ChanCount out);
@ -698,6 +712,7 @@ class LIBARDOUR_API TriggerBox : public Processor
static void init_pool();
static std::atomic<int> active_trigger_boxes;
static std::atomic<bool> _cue_recording;
};
class TriggerReference

View file

@ -980,6 +980,18 @@ Locations::add (Location *loc, bool make_current)
{
Glib::Threads::RWLock::WriterLock lm (_lock);
/* Do not allow multiple cue markers in the same location */
if (loc->is_cue_marker()) {
for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
if ((*i)->is_cue_marker() && (*i)->start() == loc->start()) {
locations.erase (i);
break;
}
}
}
locations.push_back (loc);
if (make_current) {
@ -1610,3 +1622,54 @@ Locations::ripple (timepos_t const & at, timecnt_t const & distance, bool includ
changed(); /* EMIT SIGNAL */
}
}
void
Locations::clear_cue_markers (samplepos_t start, samplepos_t end)
{
TempoMap::SharedPtr tmap (TempoMap::use());
Temporal::Beats sb;
Temporal::Beats eb;
bool have_beats = false;
vector<Location*> r;
{
Glib::Threads::RWLock::WriterLock lm (_lock);
for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
if ((*i)->is_cue_marker()) {
Location* l (*i);
if (l->start().time_domain() == AudioTime) {
samplepos_t when = l->start().samples();
if (when >= start && when < end) {
i = locations.erase (i);
r.push_back (l);
continue;
}
} else {
if (!have_beats) {
sb = tmap->quarters_at (timepos_t (start));
eb = tmap->quarters_at (timepos_t (end));
have_beats = true;
}
Temporal::Beats when = l->start().beats();
if (when >= sb && when < eb) {
r.push_back (l);
i = locations.erase (i);
continue;
}
}
}
++i;
}
} /* end lock scope */
for (auto & l : r) {
removed (l); /* EMIT SIGNAL */
delete l;
}
}

View file

@ -1653,7 +1653,7 @@ Session::first_cue_within (samplepos_t s, samplepos_t e)
}
void
Session::cue_marker_change (Location* loc)
Session::cue_marker_change (Location* /* ignored */)
{
SessionEvent* ev = new SessionEvent (SessionEvent::SyncCues, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
queue_event (ev);
@ -1671,6 +1671,12 @@ Session::maybe_find_pending_cue ()
int32_t ac = _pending_cue.exchange (-1);
if (ac >= 0) {
_active_cue.store (ac);
if (TriggerBox::cue_recording()) {
CueRecord cr (ac, _transport_sample);
TriggerBox::cue_records.write (&cr, 1);
/* failure is acceptable, but unlikely */
}
}
}

View file

@ -1355,6 +1355,11 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
auditioner->cancel_audition ();
}
/* This must be called while _transport_sample still reflects where we stopped
*/
flush_cue_recording ();
if (did_record) {
begin_reversible_command (Operations::capture);
_have_captured = true;
@ -2079,3 +2084,35 @@ Session::actual_speed() const
if (_transport_fsm->transport_speed() < 0) return - _engine_speed;
return 0;
}
void
Session::flush_cue_recording ()
{
if (!TriggerBox::cue_records.read_space()) {
return;
}
CueRecord cr;
TempoMap::SharedPtr tmap (TempoMap::use());
_locations->clear_cue_markers (_last_roll_location, _transport_sample);
while (TriggerBox::cue_records.read (&cr, 1) == 1) {
BBT_Time bbt = tmap->bbt_at (timepos_t (cr.when));
bbt = bbt.round_up_to_bar ();
timepos_t when;
if (tmap->time_domain() == Temporal::AudioTime) {
when = timepos_t (tmap->sample_at (bbt));
} else {
when = timepos_t (tmap->quarters_at (bbt));
}
Location* l = new Location (*this, when, when, std::string(), Location::Flags (Location::IsMark|Location::IsCueMarker), cr.cue_number);
_locations->add (l);
}
/* scheduled sync of cue markers in RT thread */
cue_marker_change (0);
}

View file

@ -2183,6 +2183,9 @@ TriggerBox::TriggerMidiMapMode TriggerBox::_midi_map_mode (TriggerBox::Sequentia
int TriggerBox::_first_midi_note = 60;
std::atomic<int> TriggerBox::active_trigger_boxes (0);
TriggerBoxThread* TriggerBox::worker = 0;
CueRecords TriggerBox::cue_records (256);
std::atomic<bool> TriggerBox::_cue_recording (true);
PBD::Signal0<void> TriggerBox::CueRecordingChanged;
void
TriggerBox::init ()
@ -2224,6 +2227,15 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
Config->ParameterChanged.connect_same_thread (*this, boost::bind (&TriggerBox::parameter_changed, this, _1));
}
void
TriggerBox::set_cue_recording (bool yn)
{
if (yn != _cue_recording) {
_cue_recording = yn;
CueRecordingChanged ();
}
}
void
TriggerBox::set_region (uint32_t slot, boost::shared_ptr<Region> region)
{
@ -2712,11 +2724,13 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
_sidechain->run (bufs, start_sample, end_sample, speed, nframes, true);
}
if (!_cue_recording) {
int32_t cue_bang = _session.first_cue_within (start_sample, end_sample);
if (cue_bang >= 0) {
std::cerr << " CUE BANG " << cue_bang << std::endl;
_active_scene = cue_bang;
}
}
/* STEP SIX: if at this point there is an active cue, make it trigger
* our corresponding slot