diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index a4675f4a21..89abd1d3cc 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -113,6 +113,16 @@ sigc::signal ARDOUR_UI::RapidScreenUpdate; sigc::signal ARDOUR_UI::SuperRapidScreenUpdate; sigc::signal ARDOUR_UI::Clock; +void gui_rt_cleanup (SessionEvent* ev) +{ + /* a little helper function that makes sure we delete queued SessionEvents in the correct thread */ + ENSURE_GUI_THREAD (bind (sigc::ptr_fun (&gui_rt_cleanup), ev)); + delete ev; +} + +/* wrap the above as a slot so that we can pass it to the session when queuing RT events */ +const sigc::slot gui_rt_cleanup_slot (sigc::ptr_fun (&gui_rt_cleanup)); + ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) : Gtkmm2ext::UI (X_("gui"), argcp, argvp), diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index 100c552be7..9cd5231d41 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -170,7 +170,9 @@ EditorRoutes::on_tv_rec_enable_toggled (Glib::ustring const & path_string) AudioTimeAxisView *atv = dynamic_cast (tv); if (atv != 0 && atv->is_audio_track()){ - atv->reversibly_apply_track_boolean ("rec-enable change", &Track::set_record_enable, !atv->track()->record_enabled(), this); + boost::shared_ptr rl (new RouteList); + rl->push_back (atv->route()); + _session->set_record_enable (rl, !atv->track()->record_enabled(), Session::rt_cleanup); } } @@ -184,7 +186,9 @@ EditorRoutes::on_tv_mute_enable_toggled (Glib::ustring const & path_string) AudioTimeAxisView *atv = dynamic_cast (tv); if (atv != 0) { - atv->reversibly_apply_route_boolean ("mute-enable change", &Route::set_mute, !atv->route()->muted(), this); + boost::shared_ptr rl (new RouteList); + rl->push_back (atv->route()); + _session->set_mute (rl, !atv->route()->muted(), Session::rt_cleanup); } } @@ -198,7 +202,9 @@ EditorRoutes::on_tv_solo_enable_toggled (Glib::ustring const & path_string) AudioTimeAxisView *atv = dynamic_cast (tv); if (atv != 0) { - atv->reversibly_apply_route_boolean ("solo-enable change", &Route::set_solo, !atv->route()->soloed(), this); + boost::shared_ptr rl (new RouteList); + rl->push_back (atv->route()); + _session->set_solo (rl, !atv->route()->soloed(), Session::rt_cleanup); } } diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc index 95e41b5ee6..4bd8aee03a 100644 --- a/gtk2_ardour/gain_meter.cc +++ b/gtk2_ardour/gain_meter.cc @@ -519,12 +519,7 @@ GainMeterBase::meter_press(GdkEventButton* ev) /* Primary+Tertiary-click applies change to all routes */ - _session.begin_reversible_command (_("meter point change")); - Session::GlobalMeteringStateCommand *cmd = new Session::GlobalMeteringStateCommand (_session, this); _session.foreach_route (this, &GainMeterBase::set_meter_point, next_meter_point (_route->meter_point())); - cmd->mark(); - _session.add_command (cmd); - _session.commit_reversible_command (); } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { @@ -534,12 +529,7 @@ GainMeterBase::meter_press(GdkEventButton* ev) */ if (ev->button == 1) { - _session.begin_reversible_command (_("meter point change")); - Session::GlobalMeteringStateCommand *cmd = new Session::GlobalMeteringStateCommand (_session, this); set_mix_group_meter_point (*_route, next_meter_point (_route->meter_point())); - cmd->mark(); - _session.add_command (cmd); - _session.commit_reversible_command (); } } else { diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index 821b82a0b6..f233314188 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -103,7 +103,8 @@ RouteUI::init () listen_mute_check = 0; main_mute_check = 0; ignore_toggle = false; - wait_for_release = false; + _solo_release = 0; + _mute_release = 0; route_active_menu_item = 0; polarity_menu_item = 0; denormal_menu_item = 0; @@ -229,12 +230,14 @@ RouteUI::set_route (boost::shared_ptr rp) } bool -RouteUI::mute_press(GdkEventButton* ev) +RouteUI::mute_press (GdkEventButton* ev) { if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) { return true; } + multiple_mute_change = false; + if (!ignore_toggle) { if (Keyboard::is_context_menu_event (ev)) { @@ -251,51 +254,50 @@ RouteUI::mute_press(GdkEventButton* ev) // Primary-button2 click is the midi binding click // button2-click is "momentary" - if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) { - wait_for_release = true; - } else { + if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) { return false; } + + _mute_release = new SoloMuteRelease (_route->muted ()); } if (ev->button == 1 || Keyboard::is_button2_event (ev)) { if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { -#if 0 - /* Primary-Tertiary-click applies change to all routes */ + if (_mute_release) { + _mute_release->routes = _session.get_routes (); + } - _session.begin_reversible_command (_("mute change")); - Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this); - _session.set_mute (!_route->muted()); - cmd->mark(); - _session.add_command(cmd); - _session.commit_reversible_command (); - multiple_mute_change = true; -#endif + _session.set_mute (_session.get_routes(), !_route->muted()); } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { -#if 0 - /* Primary-button1 applies change to the mix group. + /* Primary-button1 applies change to the mix group even if it is not active NOTE: Primary-button2 is MIDI learn. */ - if (ev->button == 1) { - set_route_group_mute (_route, !_route->muted()); + if (ev->button == 1 && _route->route_group()) { + if (_mute_release) { + _mute_release->routes = _session.get_routes (); + } + + _session.set_mute (_session.get_routes(), !_route->muted(), Session::rt_cleanup, true); } -#endif } else { -#if 0 /* plain click applies change to this route */ - if (wait_for_release) { - _route->set_mute (!_route->muted(), this); - } else { - reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this); + + boost::shared_ptr rl (new RouteList); + rl->push_back (_route); + + if (_mute_release) { + _mute_release->routes = rl; } -#endif + + _session.set_mute (rl, !_route->muted()); + } } } @@ -306,36 +308,19 @@ RouteUI::mute_press(GdkEventButton* ev) } bool -RouteUI::mute_release(GdkEventButton*) +RouteUI::mute_release (GdkEventButton*) { if (!ignore_toggle) { - if (wait_for_release){ - wait_for_release = false; - if (multiple_mute_change) { - multiple_mute_change = false; - // undo the last op - // because the press was the last undoable thing we did - _session.undo (1U); - } else { - _route->set_mute (!_route->muted(), this); - } + if (_mute_release){ + _session.set_mute (_mute_release->routes, _mute_release->active, Session::rt_cleanup, true); + delete _mute_release; + _mute_release = 0; } } + return true; } -void -RouteUI::post_solo_cleanup (SessionEvent* ev, bool was_not_latched) -{ - ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::post_solo_cleanup), ev, was_not_latched)); - - delete ev; - - if (was_not_latched) { - Config->set_solo_latched (false); - } -} - bool RouteUI::solo_press(GdkEventButton* ev) { @@ -344,106 +329,115 @@ RouteUI::solo_press(GdkEventButton* ev) if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) { return true; } + + multiple_solo_change = false; - if (Config->get_solo_control_is_listen_control()) { - - _route->set_listen (!_route->listening(), this); - - } else { - - multiple_solo_change = false; - if (!ignore_toggle) { - - if (Keyboard::is_context_menu_event (ev)) { - - if (solo_menu == 0) { - build_solo_menu (); + if (!ignore_toggle) { + + if (Keyboard::is_context_menu_event (ev)) { + + if (solo_menu == 0) { + build_solo_menu (); + } + + solo_menu->popup (1, ev->time); + + } else { + + if (Keyboard::is_button2_event (ev)) { + + // Primary-button2 click is the midi binding click + // button2-click is "momentary" + + if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) { + return false; } - solo_menu->popup (1, ev->time); + _solo_release = new SoloMuteRelease (_route->soloed()); + } + + if (ev->button == 1 || Keyboard::is_button2_event (ev)) { + + if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { + + /* Primary-Tertiary-click applies change to all routes */ - } else { - - if (Keyboard::is_button2_event (ev)) { - - // Primary-button2 click is the midi binding click - // button2-click is "momentary" - - if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) { - wait_for_release = true; - } else { - return false; + if (_solo_release) { + _solo_release->routes = _session.get_routes (); } - } - - if (ev->button == 1 || Keyboard::is_button2_event (ev)) { - - if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { - - /* Primary-Tertiary-click applies change to all routes */ - bool was_not_latched = false; - - if (!Config->get_solo_latched ()) { - was_not_latched = true; - /* - XXX it makes no sense to solo all tracks if we're - not in latched mode, but doing nothing feels like a bug, - so do it anyway - */ - Config->set_solo_latched (true); - } - - SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); - ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_solo), _session.get_routes(), !_route->soloed()); - ev->rt_return = bind (sigc::mem_fun (*this, &RouteUI::post_solo_cleanup), was_not_latched); - - _session.queue_event (ev); - - } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) { - - // Primary-Secondary-click: exclusively solo this track, not a toggle */ - - //boost::shared_ptr rl (new RouteList); - //rl->push_back (route()); - - //SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); - // ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_just_one_solo), rl, true); - //ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup); - - //_session.queue_event (ev); - - } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { - - // shift-click: toggle solo isolated status - - _route->set_solo_isolated (!_route->solo_isolated(), this); - wait_for_release = false; - - } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { - -#if 0 - /* Primary-button1: solo mix group. - NOTE: Primary-button2 is MIDI learn. - */ - - if (ev->button == 1) { - queue_route_group_op (RouteGroup::Solo, &Session::set_all_solo, !_route->soloed()); - } -#endif - - + + if (Config->get_solo_control_is_listen_control()) { + _session.set_listen (_session.get_routes(), !_route->listening(), Session::rt_cleanup, true); } else { + _session.set_solo (_session.get_routes(), !_route->soloed(), Session::rt_cleanup, true); + } + + } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) { + + // Primary-Secondary-click: exclusively solo this track - /* click: solo this route */ + if (_solo_release) { + _solo_release->exclusive = true; - boost::shared_ptr rl (new RouteList); - rl->push_back (route()); - - SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); - ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_solo), rl, !rec_enable_button->get_active()); - ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup); - - _session.queue_event (ev); + boost::shared_ptr routes = _session.get_routes(); + + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + if ((*i)->soloed ()) { + _solo_release->routes_on->push_back (*i); + } else { + _solo_release->routes_off->push_back (*i); + } + } + } + + if (Config->get_solo_control_is_listen_control()) { + /* ??? we need a just_one_listen() method */ + } else { + _session.set_just_one_solo (_route, true); + } + + } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { + + // shift-click: toggle solo isolated status + + _route->set_solo_isolated (!_route->solo_isolated(), this); + delete _solo_release; + _solo_release = 0; + + } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { + + /* Primary-button1: solo mix group. + NOTE: Primary-button2 is MIDI learn. + */ + + if (ev->button == 1 && _route->route_group()) { + + if (_solo_release) { + _solo_release->routes = _route->route_group()->route_list(); + } + + if (Config->get_solo_control_is_listen_control()) { + _session.set_listen (_route->route_group()->route_list(), !_route->listening(), Session::rt_cleanup, true); + } else { + _session.set_solo (_route->route_group()->route_list(), !_route->soloed(), Session::rt_cleanup, true); + } + } + + } else { + + /* click: solo this route */ + + boost::shared_ptr rl (new RouteList); + rl->push_back (route()); + + if (_solo_release) { + _solo_release->routes = rl; + } + + if (Config->get_solo_control_is_listen_control()) { + _session.set_listen (rl, !_route->listening()); + } else { + _session.set_solo (rl, !_route->soloed()); } } } @@ -454,73 +448,26 @@ RouteUI::solo_press(GdkEventButton* ev) } bool -RouteUI::solo_release(GdkEventButton*) +RouteUI::solo_release (GdkEventButton*) { if (!ignore_toggle) { - if (wait_for_release) { - wait_for_release = false; - if (multiple_solo_change) { - multiple_solo_change = false; - // undo the last op - // because the press was the last undoable thing we did - _session.undo (1U); + + if (_solo_release) { + + if (_solo_release->exclusive) { + } else { - // we don't use "undo the last op" - // here because its expensive for the GUI - _route->set_solo (!_route->soloed(), this); + _session.set_solo (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true); } + + delete _solo_release; + _solo_release = 0; } } return true; } -void -RouteUI::post_rtop_cleanup (SessionEvent* ev) -{ - ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::post_rtop_cleanup), ev)); - delete ev; -} - -void -RouteUI::post_group_rtop_cleanup (SessionEvent* ev, RouteGroup* rg, RouteGroup::Property prop) -{ - ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::post_group_rtop_cleanup), ev, rg, prop)); - delete ev; - rg->set_property (prop, false); -} - -void -RouteUI::queue_route_group_op (RouteGroup::Property prop, void (Session::*session_method)(boost::shared_ptr, bool), bool yn) -{ - RouteGroup* rg = _route->route_group(); - bool prop_was_active; - - if (rg) { - prop_was_active = rg->active_property (prop); - rg->set_property (prop, true); - } else { - prop_was_active = false; - } - - /* we will queue the op for just this route, but because its route group now has the relevant property marked active, - the operation will apply to the whole group (if there is a group) - */ - - boost::shared_ptr rl (new RouteList); - rl->push_back (route()); - - SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); - ev->rt_slot = bind (sigc::mem_fun (_session, session_method), rl, yn); - if (rg && !prop_was_active) { - ev->rt_return = bind (sigc::mem_fun (*this, &RouteUI::post_group_rtop_cleanup), rg, prop); - } else { - ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup); - } - - _session.queue_event (ev); -} - bool RouteUI::rec_enable_press(GdkEventButton* ev) { @@ -543,21 +490,16 @@ RouteUI::rec_enable_press(GdkEventButton* ev) } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { -#if 0 - _session.set_record_enable (_session.get_route(), !rec_enable_button->get_active(), sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup)); -#endif + _session.set_record_enable (_session.get_routes(), !rec_enable_button->get_active()); } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { /* Primary-button1 applies change to the route group (even if it is not active) NOTE: Primary-button2 is MIDI learn. */ -#if 0 if (ev->button == 1 && _route->route_group()) { - _session.set_record_enable (_route->route_group(), !rec_enable_button->get_active(), - queue_route_group_op (RouteGroup::RecEnable, &Session::set_record_enable, + _session.set_record_enable (_route->route_group()->route_list(), !rec_enable_button->get_active(), Session::rt_cleanup, true); } -#endif } else if (Keyboard::is_context_menu_event (ev)) { @@ -565,11 +507,9 @@ RouteUI::rec_enable_press(GdkEventButton* ev) } else { -#if 0 boost::shared_ptr rl (new RouteList); rl->push_back (route()); - _session.set_record_enable (rl, !rec_enable_button->get_active(), sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup)); -#endif + _session.set_record_enable (rl, !rec_enable_button->get_active()); } } @@ -1018,80 +958,6 @@ RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check) _route->set_solo_safe (check->get_active(), this); } -void -RouteUI::set_route_group_solo(boost::shared_ptr route, bool yn) -{ - RouteGroup* route_group; - - if((route_group = route->route_group()) != 0){ - _session.begin_reversible_command (_("mix group solo change")); - Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this); - route_group->apply(&Route::set_solo, yn, this); - cmd->mark(); - _session.add_command (cmd); - _session.commit_reversible_command (); - } else { - reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route->soloed(), this); - } -} - -void -RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg) -{ - _session.begin_reversible_command (name); - XMLNode &before = _route->get_state(); - bind(mem_fun(*_route, func), yn, arg)(); - XMLNode &after = _route->get_state(); - _session.add_command (new MementoCommand(*_route, &before, &after)); - _session.commit_reversible_command (); -} - -void -RouteUI::reversibly_apply_track_boolean (string name, void (Track::*func)(bool, void *), bool yn, void *arg) -{ - _session.begin_reversible_command (name); - XMLNode &before = track()->get_state(); - bind (mem_fun (*track(), func), yn, arg)(); - XMLNode &after = track()->get_state(); - _session.add_command (new MementoCommand(*track(), &before, &after)); - _session.commit_reversible_command (); -} - -void -RouteUI::set_route_group_mute(boost::shared_ptr route, bool yn) -{ - RouteGroup* route_group; - - if((route_group = route->route_group()) != 0){ - _session.begin_reversible_command (_("mix group mute change")); - Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (_session, this); - route_group->apply(&Route::set_mute, yn, this); - cmd->mark(); - _session.add_command(cmd); - _session.commit_reversible_command (); - } else { - reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route->muted(), this); - } -} - -void -RouteUI::set_route_group_rec_enable(boost::shared_ptr route, bool yn) -{ - RouteGroup* route_group; - - if((route_group = route->route_group()) != 0){ - _session.begin_reversible_command (_("mix group rec-enable change")); - Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this); - route_group->apply (&Route::set_record_enable, yn, this); - cmd->mark(); - _session.add_command(cmd); - _session.commit_reversible_command (); - } else { - reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route->record_enabled(), this); - } -} - - bool RouteUI::choose_color() { diff --git a/gtk2_ardour/route_ui.h b/gtk2_ardour/route_ui.h index 90bdaa7fb7..c38a4aac6d 100644 --- a/gtk2_ardour/route_ui.h +++ b/gtk2_ardour/route_ui.h @@ -155,10 +155,6 @@ class RouteUI : public virtual AxisView void build_mute_menu(void); void init_mute_menu(ARDOUR::MuteMaster::MutePoint, Gtk::CheckMenuItem*); - void set_route_group_solo (boost::shared_ptr, bool); - void set_route_group_mute (boost::shared_ptr, bool); - void set_route_group_rec_enable (boost::shared_ptr, bool); - int set_color_from_route (); void remove_this_route (); @@ -191,9 +187,6 @@ class RouteUI : public virtual AxisView virtual void map_frozen (); - void reversibly_apply_route_boolean (std::string name, void (ARDOUR::Route::*func)(bool, void*), bool, void *); - void reversibly_apply_track_boolean (std::string name, void (ARDOUR::Track::*func)(bool, void*), bool, void *); - void adjust_latency (); void save_as_template (); void open_remote_control_id_dialog (); @@ -215,9 +208,21 @@ class RouteUI : public virtual AxisView void parameter_changed (std::string const &); void relabel_solo_button (); - void post_rtop_cleanup (ARDOUR::SessionEvent* ev); - void post_group_rtop_cleanup (ARDOUR::SessionEvent* ev, ARDOUR::RouteGroup*, ARDOUR::RouteGroup::Property); - void post_solo_cleanup (ARDOUR::SessionEvent* ev, bool was_not_latched); + struct SoloMuteRelease { + SoloMuteRelease (bool was_active) + : active (was_active) + , exclusive (false) {} + + boost::shared_ptr routes; + boost::shared_ptr routes_on; + boost::shared_ptr routes_off; + boost::shared_ptr route; + bool active; + bool exclusive; + }; + + SoloMuteRelease* _solo_release; + SoloMuteRelease* _mute_release; }; #endif /* __ardour_route_ui__ */ diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 4e1721bae0..c4f101ce5c 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -122,12 +122,6 @@ extern void setup_enum_writer (); class Session : public PBD::StatefulDestructible, public SessionEventManager, public boost::noncopyable { - private: - typedef std::pair,bool> RouteBooleanState; - typedef std::vector GlobalRouteBooleanState; - typedef std::pair,MeterPoint> RouteMeterState; - typedef std::vector GlobalRouteMeterState; - public: enum RecordState { Disabled = 0, @@ -617,14 +611,17 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu bool soloing() const { return _non_soloed_outs_muted; } bool listening() const { return _listen_cnt > 0; } - void set_solo (boost::shared_ptr, bool); - void set_mute (boost::shared_ptr, bool); - void set_listen (boost::shared_ptr, bool); + static const sigc::slot rt_cleanup; + + void set_solo (boost::shared_ptr, bool, sigc::slot after = rt_cleanup, bool group_override = false); + void set_just_one_solo (boost::shared_ptr, bool, sigc::slot after = rt_cleanup); + void set_mute (boost::shared_ptr, bool, sigc::slot after = rt_cleanup, bool group_override = false); + void set_listen (boost::shared_ptr, bool, sigc::slot after = rt_cleanup, bool group_override = false); + void set_record_enable (boost::shared_ptr, bool, sigc::slot after = rt_cleanup, bool group_override = false); sigc::signal SoloActive; sigc::signal SoloChanged; - void set_record_enable (boost::shared_ptr, bool); /* control/master out */ @@ -735,73 +732,6 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu Command* memento_command_factory(XMLNode* n); void register_with_memento_command_factory(PBD::ID, PBD::StatefulThingWithGoingAway*); - Command* global_state_command_factory (const XMLNode& n); - - class GlobalRouteStateCommand : public Command - { - public: - GlobalRouteStateCommand (Session&, void*); - GlobalRouteStateCommand (Session&, const XMLNode& node); - int set_state (const XMLNode&, int version); - XMLNode& get_state (); - - protected: - GlobalRouteBooleanState before, after; - Session& sess; - void* src; - }; - - class GlobalSoloStateCommand : public GlobalRouteStateCommand - { - public: - GlobalSoloStateCommand (Session &, void *src); - GlobalSoloStateCommand (Session&, const XMLNode&); - void operator()(); //redo - void undo(); - XMLNode &get_state(); - void mark(); - }; - - class GlobalMuteStateCommand : public GlobalRouteStateCommand - { - public: - GlobalMuteStateCommand(Session &, void *src); - GlobalMuteStateCommand (Session&, const XMLNode&); - void operator()(); // redo - void undo(); - XMLNode &get_state(); - void mark(); - }; - - class GlobalRecordEnableStateCommand : public GlobalRouteStateCommand - { - public: - GlobalRecordEnableStateCommand(Session &, void *src); - GlobalRecordEnableStateCommand (Session&, const XMLNode&); - void operator()(); // redo - void undo(); - XMLNode &get_state(); - void mark(); - }; - - class GlobalMeteringStateCommand : public Command - { - public: - GlobalMeteringStateCommand(Session &, void *src); - GlobalMeteringStateCommand (Session&, const XMLNode&); - void operator()(); - void undo(); - XMLNode &get_state(); - int set_state (const XMLNode&, int version); - void mark(); - - protected: - Session& sess; - void* src; - GlobalRouteMeterState before; - GlobalRouteMeterState after; - }; - /* clicking */ boost::shared_ptr click_io() { return _click_io; } @@ -1443,16 +1373,6 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu UndoHistory _history; std::stack _current_trans; - GlobalRouteBooleanState get_global_route_boolean (bool (Route::*method)(void) const); - GlobalRouteMeterState get_global_route_metering (); - - void set_global_route_boolean (GlobalRouteBooleanState s, void (Route::*method)(bool, void*), void *arg); - void set_global_route_metering (GlobalRouteMeterState s, void *arg); - - void set_global_mute (GlobalRouteBooleanState s, void *src); - void set_global_solo (GlobalRouteBooleanState s, void *src); - void set_global_record_enable (GlobalRouteBooleanState s, void *src); - void jack_timebase_callback (jack_transport_state_t, nframes_t, jack_position_t*, int); int jack_sync_callback (jack_transport_state_t, jack_position_t*); void reset_jack_connection (jack_client_t* jack); @@ -1549,6 +1469,16 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu void update_have_rec_enabled_diskstream (); gint _have_rec_enabled_diskstream; + + /* realtime "apply to set of routes" operations */ + SessionEvent* get_rt_event (boost::shared_ptr rl, bool yn, sigc::slot after, bool group_override, + void (Session::*method) (boost::shared_ptr, bool, bool)); + + void rt_set_solo (boost::shared_ptr, bool yn, bool group_override); + void rt_set_just_one_solo (boost::shared_ptr, bool yn, bool /* ignored*/ ); + void rt_set_mute (boost::shared_ptr, bool yn, bool group_override); + void rt_set_listen (boost::shared_ptr, bool yn, bool group_override); + void rt_set_record_enable (boost::shared_ptr, bool yn, bool group_override); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/session_event.h b/libs/ardour/ardour/session_event.h index 7775f4b1ad..bb5b5f49ce 100644 --- a/libs/ardour/ardour/session_event.h +++ b/libs/ardour/ardour/session_event.h @@ -7,6 +7,7 @@ #include "pbd/pool.h" #include "pbd/ringbuffer.h" +#include "pbd/ui_callback.h" #include "ardour/types.h" @@ -68,6 +69,7 @@ struct SessionEvent { boost::shared_ptr routes; sigc::slot rt_slot; /* what to call in RT context */ sigc::slot rt_return; /* called after rt_slot, with this event as an argument */ + PBD::UICallback* ui; std::list audio_range; std::list music_range; @@ -81,7 +83,8 @@ struct SessionEvent { , target_frame (where) , speed (spd) , yes_or_no (yn) - , second_yes_or_no (yn2) {} + , second_yes_or_no (yn2) + , ui (0) {} void set_ptr (void* p) { ptr = p; diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 0bf161bb5d..24700b8893 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -117,6 +117,9 @@ sigc::signal Session::AutoBindingOn; sigc::signal Session::AutoBindingOff; sigc::signal Session::Exported; +static void clean_up_session_event (SessionEvent* ev) { delete ev; } +const sigc::slot Session::rt_cleanup (sigc::ptr_fun (&clean_up_session_event)); + Session::Session (AudioEngine &eng, const string& fullpath, const string& snapshot_name, @@ -3515,42 +3518,6 @@ Session::is_auditioning () const } } -void -Session::set_solo (boost::shared_ptr r, bool yn) -{ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { - (*i)->set_solo (yn, this); - } - } - - set_dirty(); -} - -void -Session::set_listen (boost::shared_ptr r, bool yn) -{ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { - (*i)->set_listen (yn, this); - } - } - - set_dirty(); -} - -void -Session::set_mute (boost::shared_ptr r, bool yn) -{ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { - (*i)->set_mute (yn, this); - } - } - - set_dirty(); -} - uint32_t Session::n_diskstreams () const { @@ -3596,32 +3563,6 @@ Session::graph_reordered () } } -void -Session::set_record_enable (boost::shared_ptr rl, bool yn) -{ - if (!writable()) { - return; - } - - for (RouteList::iterator i = rl->begin(); i != rl->end(); ) { - boost::shared_ptr t; - - if ((t = boost::dynamic_pointer_cast(*i)) != 0) { - t->set_record_enable (yn, this); - if (t->meter_point() == MeterCustom) { - /* don't change metering for this track */ - i = rl->erase (i); - } else { - ++i; - } - } else { - ++i; - } - } - - set_dirty (); -} - void Session::add_processor (Processor* processor) { diff --git a/libs/ardour/session_command.cc b/libs/ardour/session_command.cc index d1ce09cbf6..4d733264fb 100644 --- a/libs/ardour/session_command.cc +++ b/libs/ardour/session_command.cc @@ -124,428 +124,3 @@ Session::memento_command_factory(XMLNode *n) return 0 ; } -Command * -Session::global_state_command_factory (const XMLNode& node) -{ - const XMLProperty* prop; - Command* command = 0; - - if ((prop = node.property ("type")) == 0) { - error << _("GlobalRouteStateCommand has no \"type\" node, ignoring") << endmsg; - return 0; - } - - try { - - if (prop->value() == "solo") { - command = new GlobalSoloStateCommand (*this, node); - } else if (prop->value() == "mute") { - command = new GlobalMuteStateCommand (*this, node); - } else if (prop->value() == "rec-enable") { - command = new GlobalRecordEnableStateCommand (*this, node); - } else if (prop->value() == "metering") { - command = new GlobalMeteringStateCommand (*this, node); - } else { - error << string_compose (_("unknown type of GlobalRouteStateCommand (%1), ignored"), prop->value()) << endmsg; - } - } - - catch (failed_constructor& err) { - return 0; - } - - return command; -} - -Session::GlobalRouteStateCommand::GlobalRouteStateCommand (Session& s, void* p) - : sess (s), src (p) -{ -} - -Session::GlobalRouteStateCommand::GlobalRouteStateCommand (Session& s, const XMLNode& node) - : sess (s), src (this) -{ - if (set_state (node, Stateful::loading_state_version)) { - throw failed_constructor (); - } -} - -int -Session::GlobalRouteStateCommand::set_state (const XMLNode& node, int /*version*/) -{ - GlobalRouteBooleanState states; - XMLNodeList nlist; - const XMLProperty* prop; - XMLNode* child; - XMLNodeConstIterator niter; - int loop; - - before.clear (); - after.clear (); - - for (loop = 0; loop < 2; ++loop) { - - const char *str; - - if (loop) { - str = "after"; - } else { - str = "before"; - } - - if ((child = node.child (str)) == 0) { - warning << string_compose (_("global route state command has no \"%1\" node, ignoring entire command"), str) << endmsg; - return -1; - } - - nlist = child->children(); - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - - RouteBooleanState rbs; - boost::shared_ptr route; - ID id; - - prop = (*niter)->property ("id"); - id = prop->value (); - - if ((route = sess.route_by_id (id)) == 0) { - warning << string_compose (_("cannot find track/bus \"%1\" while rebuilding a global route state command, ignored"), id.to_s()) << endmsg; - continue; - } - - rbs.first = boost::weak_ptr (route); - - prop = (*niter)->property ("yn"); - rbs.second = (prop->value() == "1"); - - if (loop) { - after.push_back (rbs); - } else { - before.push_back (rbs); - } - } - } - - return 0; -} - -XMLNode& -Session::GlobalRouteStateCommand::get_state () -{ - XMLNode* node = new XMLNode (X_("GlobalRouteStateCommand")); - XMLNode* nbefore = new XMLNode (X_("before")); - XMLNode* nafter = new XMLNode (X_("after")); - - for (Session::GlobalRouteBooleanState::iterator x = before.begin(); x != before.end(); ++x) { - XMLNode* child = new XMLNode ("s"); - boost::shared_ptr r = x->first.lock(); - - if (r) { - child->add_property (X_("id"), r->id().to_s()); - child->add_property (X_("yn"), (x->second ? "1" : "0")); - nbefore->add_child_nocopy (*child); - } - } - - for (Session::GlobalRouteBooleanState::iterator x = after.begin(); x != after.end(); ++x) { - XMLNode* child = new XMLNode ("s"); - boost::shared_ptr r = x->first.lock(); - - if (r) { - child->add_property (X_("id"), r->id().to_s()); - child->add_property (X_("yn"), (x->second ? "1" : "0")); - nafter->add_child_nocopy (*child); - } - } - - node->add_child_nocopy (*nbefore); - node->add_child_nocopy (*nafter); - - return *node; -} - -// solo - -Session::GlobalSoloStateCommand::GlobalSoloStateCommand(Session &sess, void *src) - : GlobalRouteStateCommand (sess, src) -{ - after = before = sess.get_global_route_boolean(&Route::soloed); -} - -Session::GlobalSoloStateCommand::GlobalSoloStateCommand (Session& sess, const XMLNode& node) - : Session::GlobalRouteStateCommand (sess, node) -{ -} - -void -Session::GlobalSoloStateCommand::mark() -{ - after = sess.get_global_route_boolean(&Route::soloed); -} - -void -Session::GlobalSoloStateCommand::operator()() -{ - sess.set_global_solo(after, src); -} - -void -Session::GlobalSoloStateCommand::undo() -{ - sess.set_global_solo(before, src); -} - -XMLNode& -Session::GlobalSoloStateCommand::get_state() -{ - XMLNode& node = GlobalRouteStateCommand::get_state(); - node.add_property ("type", "solo"); - return node; -} - -// mute -Session::GlobalMuteStateCommand::GlobalMuteStateCommand(Session &sess, void *src) - : GlobalRouteStateCommand (sess, src) -{ - after = before = sess.get_global_route_boolean(&Route::muted); -} - -Session::GlobalMuteStateCommand::GlobalMuteStateCommand (Session& sess, const XMLNode& node) - : Session::GlobalRouteStateCommand (sess, node) -{ -} - -void -Session::GlobalMuteStateCommand::mark() -{ - after = sess.get_global_route_boolean(&Route::muted); -} - -void -Session::GlobalMuteStateCommand::operator()() -{ - sess.set_global_mute(after, src); -} - -void -Session::GlobalMuteStateCommand::undo() -{ - sess.set_global_mute(before, src); -} - -XMLNode& -Session::GlobalMuteStateCommand::get_state() -{ - XMLNode& node = GlobalRouteStateCommand::get_state(); - node.add_property ("type", "mute"); - return node; -} - -// record enable -Session::GlobalRecordEnableStateCommand::GlobalRecordEnableStateCommand(Session &sess, void *src) - : GlobalRouteStateCommand (sess, src) -{ - after = before = sess.get_global_route_boolean(&Route::record_enabled); -} - -Session::GlobalRecordEnableStateCommand::GlobalRecordEnableStateCommand (Session& sess, const XMLNode& node) - : Session::GlobalRouteStateCommand (sess, node) -{ -} - -void -Session::GlobalRecordEnableStateCommand::mark() -{ - after = sess.get_global_route_boolean(&Route::record_enabled); -} - -void -Session::GlobalRecordEnableStateCommand::operator()() -{ - sess.set_global_record_enable(after, src); -} - -void -Session::GlobalRecordEnableStateCommand::undo() -{ - sess.set_global_record_enable(before, src); -} - -XMLNode& -Session::GlobalRecordEnableStateCommand::get_state() -{ - XMLNode& node = GlobalRouteStateCommand::get_state(); - node.add_property ("type", "rec-enable"); - return node; -} - -// metering -Session::GlobalMeteringStateCommand::GlobalMeteringStateCommand(Session &s, void *p) - : sess (s), src (p) -{ - after = before = sess.get_global_route_metering(); -} - -Session::GlobalMeteringStateCommand::GlobalMeteringStateCommand (Session& s, const XMLNode& node) - : sess (s), src (this) -{ - if (set_state (node, Stateful::loading_state_version)) { - throw failed_constructor(); - } -} - -void -Session::GlobalMeteringStateCommand::mark() -{ - after = sess.get_global_route_metering(); -} - -void -Session::GlobalMeteringStateCommand::operator()() -{ - sess.set_global_route_metering(after, src); -} - -void -Session::GlobalMeteringStateCommand::undo() -{ - sess.set_global_route_metering(before, src); -} - -XMLNode& -Session::GlobalMeteringStateCommand::get_state() -{ - XMLNode* node = new XMLNode (X_("GlobalRouteStateCommand")); - XMLNode* nbefore = new XMLNode (X_("before")); - XMLNode* nafter = new XMLNode (X_("after")); - - for (Session::GlobalRouteMeterState::iterator x = before.begin(); x != before.end(); ++x) { - XMLNode* child = new XMLNode ("s"); - boost::shared_ptr r = x->first.lock(); - - if (r) { - child->add_property (X_("id"), r->id().to_s()); - - const char* meterstr = 0; - - switch (x->second) { - case MeterInput: - meterstr = X_("input"); - break; - case MeterPreFader: - meterstr = X_("pre"); - break; - case MeterPostFader: - meterstr = X_("post"); - break; - default: - fatal << string_compose (_("programming error: %1") , "no meter state in Session::GlobalMeteringStateCommand::get_state") << endmsg; - } - - child->add_property (X_("meter"), meterstr); - nbefore->add_child_nocopy (*child); - } - } - - for (Session::GlobalRouteMeterState::iterator x = after.begin(); x != after.end(); ++x) { - XMLNode* child = new XMLNode ("s"); - boost::shared_ptr r = x->first.lock(); - - if (r) { - child->add_property (X_("id"), r->id().to_s()); - - const char* meterstr; - - switch (x->second) { - case MeterInput: - meterstr = X_("input"); - break; - case MeterPreFader: - meterstr = X_("pre"); - break; - case MeterPostFader: - meterstr = X_("post"); - break; - default: meterstr = ""; - } - - child->add_property (X_("meter"), meterstr); - nafter->add_child_nocopy (*child); - } - } - - node->add_child_nocopy (*nbefore); - node->add_child_nocopy (*nafter); - - node->add_property ("type", "metering"); - - return *node; -} - -int -Session::GlobalMeteringStateCommand::set_state (const XMLNode& node, int /*version*/) -{ - GlobalRouteBooleanState states; - XMLNodeList nlist; - const XMLProperty* prop; - XMLNode* child; - XMLNodeConstIterator niter; - int loop; - - before.clear (); - after.clear (); - - for (loop = 0; loop < 2; ++loop) { - - const char *str; - - if (loop) { - str = "after"; - } else { - str = "before"; - } - - if ((child = node.child (str)) == 0) { - warning << string_compose (_("global route meter state command has no \"%1\" node, ignoring entire command"), str) << endmsg; - return -1; - } - - nlist = child->children(); - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - - RouteMeterState rms; - boost::shared_ptr route; - ID id; - - prop = (*niter)->property ("id"); - id = prop->value (); - - if ((route = sess.route_by_id (id)) == 0) { - warning << string_compose (_("cannot find track/bus \"%1\" while rebuilding a global route state command, ignored"), id.to_s()) << endmsg; - continue; - } - - rms.first = boost::weak_ptr (route); - - prop = (*niter)->property ("meter"); - - if (prop->value() == X_("pre")) { - rms.second = MeterPreFader; - } else if (prop->value() == X_("post")) { - rms.second = MeterPostFader; - } else { - rms.second = MeterInput; - } - - if (loop) { - after.push_back (rms); - } else { - before.push_back (rms); - } - } - } - - return 0; -} diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index 568017c064..9766a1b159 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -577,7 +577,7 @@ Session::follow_slave (nframes_t nframes) slave_speed)); } -#if 0 +#if 1 if (abs(average_slave_delta) > _slave->resolution()) { cerr << "average slave delta greater than slave resolution (" << _slave->resolution() << "), going to silent motion\n"; goto silent_motion; @@ -1141,9 +1141,3 @@ Session::process_event (SessionEvent* ev) } } -void -Session::process_rtop (SessionEvent* ev) -{ - ev->rt_slot (); - ev->rt_return (ev); -} diff --git a/libs/ardour/session_rtevents.cc b/libs/ardour/session_rtevents.cc new file mode 100644 index 0000000000..6d3e7ce78c --- /dev/null +++ b/libs/ardour/session_rtevents.cc @@ -0,0 +1,169 @@ +/* + Copyright (C) 1999-2009 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "pbd/error.h" +#include "pbd/compose.h" + +#include "ardour/session.h" +#include "ardour/route.h" +#include "ardour/track.h" + +#include "i18n.h" + +using namespace std; +using namespace PBD; +using namespace ARDOUR; +using namespace Glib; + +SessionEvent* +Session::get_rt_event (boost::shared_ptr rl, bool yn, sigc::slot after, bool group_override, + void (Session::*method) (boost::shared_ptr, bool, bool)) +{ + SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); + ev->rt_slot = bind (sigc::mem_fun (*this, method), rl, yn, group_override); + ev->rt_return = after; + ev->ui = UICallback::get_ui_for_thread (); + + return ev; +} + +void +Session::set_solo (boost::shared_ptr rl, bool yn, sigc::slot after, bool group_override) +{ + queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_solo)); +} + +void +Session::rt_set_solo (boost::shared_ptr rl, bool yn, bool group_override) +{ + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + if (!(*i)->is_hidden()) { + (*i)->set_solo (yn, this); + } + } + + set_dirty(); +} + +void +Session::set_just_one_solo (boost::shared_ptr r, bool yn, sigc::slot after) +{ + /* its a bit silly to have to do this, but it keeps the API for this public method sane (we're + only going to solo one route) and keeps our ability to use get_rt_event() for the internal + private method. + */ + + boost::shared_ptr rl (new RouteList); + rl->push_back (r); + + queue_event (get_rt_event (rl, yn, after, false, &Session::rt_set_just_one_solo)); +} + +void +Session::rt_set_just_one_solo (boost::shared_ptr just_one, bool yn, bool /*ignored*/) +{ + boost::shared_ptr rl = routes.reader (); + boost::shared_ptr r = just_one->front(); + + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + if (!(*i)->is_hidden() && r != *i) { + (*i)->set_solo (!yn, (*i)->route_group()); + } + } + + r->set_solo (yn, r->route_group()); + + set_dirty(); +} + +void +Session::set_listen (boost::shared_ptr rl, bool yn, sigc::slot after, bool group_override) +{ + queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_listen)); +} + +void +Session::rt_set_listen (boost::shared_ptr rl, bool yn, bool group_override) +{ + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + if (!(*i)->is_hidden()) { + (*i)->set_listen (yn, this); + } + } + + set_dirty(); +} + +void +Session::set_mute (boost::shared_ptr rl, bool yn, sigc::slot after, bool group_override) +{ + queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_mute)); +} + +void +Session::rt_set_mute (boost::shared_ptr rl, bool yn, bool group_override) +{ + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + if (!(*i)->is_hidden()) { + (*i)->set_mute (yn, this); + } + } + + set_dirty(); +} + +void +Session::set_record_enable (boost::shared_ptr rl, bool yn, sigc::slot after, bool group_override) +{ + if (!writable()) { + return; + } + + queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_record_enable)); +} + +void +Session::rt_set_record_enable (boost::shared_ptr rl, bool yn, bool group_override) +{ + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + if ((*i)->is_hidden()) { + continue; + } + + boost::shared_ptr t; + + if ((t = boost::dynamic_pointer_cast(*i)) != 0) { + t->set_record_enable (yn, (group_override ? (void*) t->route_group() : (void *) this)); + } + } + + set_dirty (); +} + +void +Session::process_rtop (SessionEvent* ev) +{ + ev->rt_slot (); + + if (ev->ui) { + ev->ui->call_slot (bind (ev->rt_return, ev)); + } else { + warning << string_compose ("programming error: %1", X_("Session RT event queued from thread without a UI - cleanup in RT thread!")) << endmsg; + ev->rt_return (ev); + } +} diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index db3d9e94ae..74578039a8 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -2176,92 +2176,6 @@ Session::commit_reversible_command(Command *cmd) _current_trans.pop(); } -Session::GlobalRouteBooleanState -Session::get_global_route_boolean (bool (Route::*method)(void) const) -{ - GlobalRouteBooleanState s; - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { - RouteBooleanState v; - - v.first =* i; - Route* r = (*i).get(); - v.second = (r->*method)(); - - s.push_back (v); - } - } - - return s; -} - -Session::GlobalRouteMeterState -Session::get_global_route_metering () -{ - GlobalRouteMeterState s; - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { - RouteMeterState v; - - v.first =* i; - v.second = (*i)->meter_point(); - - s.push_back (v); - } - } - - return s; -} - -void -Session::set_global_route_metering (GlobalRouteMeterState s, void* arg) -{ - for (GlobalRouteMeterState::iterator i = s.begin(); i != s.end(); ++i) { - - boost::shared_ptr r = (i->first.lock()); - - if (r) { - r->set_meter_point (i->second, arg); - } - } -} - -void -Session::set_global_route_boolean (GlobalRouteBooleanState s, void (Route::*method)(bool, void*), void* arg) -{ - for (GlobalRouteBooleanState::iterator i = s.begin(); i != s.end(); ++i) { - - boost::shared_ptr r = (i->first.lock()); - - if (r) { - Route* rp = r.get(); - (rp->*method) (i->second, arg); - } - } -} - -void -Session::set_global_mute (GlobalRouteBooleanState s, void* src) -{ - set_global_route_boolean (s, &Route::set_mute, src); -} - -void -Session::set_global_solo (GlobalRouteBooleanState s, void* src) -{ - set_global_route_boolean (s, &Route::set_solo, src); -} - -void -Session::set_global_record_enable (GlobalRouteBooleanState s, void* src) -{ - set_global_route_boolean (s, &Route::set_record_enable, src); -} - static bool accept_all_non_peak_files (const string& path, void */*arg*/) { @@ -2892,12 +2806,6 @@ Session::restore_history (string snapshot_name) ut->add_command(c); } - } else if (n->name() == X_("GlobalRouteStateCommand")) { - - if ((c = global_state_command_factory (*n))) { - ut->add_command (c); - } - } else if (n->name() == "DeltaCommand") { PBD::ID id(n->property("midi-source")->value()); boost::shared_ptr midi_source = diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 9f056d9223..0dcfea7056 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -168,6 +168,7 @@ libardour_sources = [ 'session_midi.cc', 'session_playlists.cc', 'session_process.cc', + 'session_rtevents.cc', 'session_state.cc', 'session_state_utils.cc', 'session_time.cc', diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc index 1f8e919af4..3d86c6d103 100644 --- a/libs/gtkmm2ext/gtk_ui.cc +++ b/libs/gtkmm2ext/gtk_ui.cc @@ -80,6 +80,12 @@ UI::UI (string namestr, int *argc, char ***argv) */ run_loop_thread = Thread::self(); + + /* store "this" as the UI-for-thread of this thread, same argument + as for previous line. + */ + + set_ui_for_thread (this); /* attach our request source to the default main context */ diff --git a/libs/pbd/base_ui.cc b/libs/pbd/base_ui.cc index 9b461fb01d..ea7e469c74 100644 --- a/libs/pbd/base_ui.cc +++ b/libs/pbd/base_ui.cc @@ -43,8 +43,6 @@ BaseUI::BaseUI (const string& str) : run_loop_thread (0) , _name (str) { - cerr << "New BUI called " << _name << " @ " << this << endl; - base_ui_instance = this; request_channel.ios()->connect (sigc::mem_fun (*this, &BaseUI::request_handler)); @@ -72,6 +70,7 @@ BaseUI::new_request_type () void BaseUI::main_thread () { + set_ui_for_thread (this); thread_init (); _main_loop->run (); } diff --git a/libs/pbd/pbd/base_ui.h b/libs/pbd/pbd/base_ui.h index e8de355b03..20534aa7fd 100644 --- a/libs/pbd/pbd/base_ui.h +++ b/libs/pbd/pbd/base_ui.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2007 Paul Davis + Copyright (C) 2000-2009 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,8 +30,10 @@ #include #include "pbd/crossthread.h" +#include "pbd/ui_callback.h" -class BaseUI : virtual public sigc::trackable { +class BaseUI : virtual public sigc::trackable, public PBD::UICallback +{ public: BaseUI (const std::string& name); virtual ~BaseUI(); @@ -45,7 +47,7 @@ class BaseUI : virtual public sigc::trackable { std::string name() const { return _name; } bool ok() const { return _ok; } - + enum RequestType { range_guarantee = ~0 }; @@ -62,8 +64,6 @@ class BaseUI : virtual public sigc::trackable { void run (); void quit (); - virtual void call_slot (sigc::slot theSlot) = 0; - protected: CrossThreadChannel request_channel; bool _ok; diff --git a/libs/pbd/pbd/ui_callback.h b/libs/pbd/pbd/ui_callback.h new file mode 100644 index 0000000000..d5f4c51f4e --- /dev/null +++ b/libs/pbd/pbd/ui_callback.h @@ -0,0 +1,47 @@ +/* + Copyright (C) 2009 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __pbd_ui_callback_h__ +#define __pbd_ui_callback_h__ + +#include +#include + +namespace PBD +{ + +class UICallback +{ + public: + UICallback() {} + virtual ~UICallback() {} + + virtual void call_slot (sigc::slot theSlot) = 0; + + static UICallback* get_ui_for_thread() { return thread_ui.get(); } + static void set_ui_for_thread (UICallback* ui) { return thread_ui.set (ui); } + + private: + static Glib::StaticPrivate thread_ui; + +}; + +} + +#endif /* __pbd_ui_callback_h__ */ diff --git a/libs/pbd/ui_callback.cc b/libs/pbd/ui_callback.cc new file mode 100644 index 0000000000..954d25f637 --- /dev/null +++ b/libs/pbd/ui_callback.cc @@ -0,0 +1,6 @@ +#include "pbd/ui_callback.h" + +using namespace PBD; + +Glib::StaticPrivate UICallback::thread_ui; + diff --git a/libs/pbd/wscript b/libs/pbd/wscript index e114cbe1ae..84c18cc899 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -81,6 +81,7 @@ def build(bld): strsplit.cc textreceiver.cc transmitter.cc + ui_callback.cc undo.cc uuid.cc version.cc