X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=00055844031fa5c60e1cda4142029ff3e0e73853;hb=00caabf73583fa029601b7d4e9ccdb9b2da7925c;hp=718b68279dd8af54322525fd462e7324fea0e8d9;hpb=4c40fe8d042a7987ea17763210dbdad8de64fb2a;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 718b68279d..0005584403 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -36,7 +36,6 @@ #include #include "pbd/basename.h" -#include "pbd/boost_debug.h" #include "pbd/convert.h" #include "pbd/convert.h" #include "pbd/error.h" @@ -59,6 +58,7 @@ #include "ardour/audioengine.h" #include "ardour/audiofilesource.h" #include "ardour/auditioner.h" +#include "ardour/boost_debug.h" #include "ardour/buffer_manager.h" #include "ardour/buffer_set.h" #include "ardour/bundle.h" @@ -90,14 +90,15 @@ #include "ardour/recent_sessions.h" #include "ardour/region.h" #include "ardour/region_factory.h" +#include "ardour/revision.h" #include "ardour/route_graph.h" #include "ardour/route_group.h" -#include "ardour/route_sorters.h" #include "ardour/send.h" #include "ardour/session.h" #include "ardour/session_directory.h" #include "ardour/session_playlists.h" #include "ardour/smf_source.h" +#include "ardour/solo_isolate_control.h" #include "ardour/source_factory.h" #include "ardour/speakers.h" #include "ardour/tempo.h" @@ -105,6 +106,8 @@ #include "ardour/track.h" #include "ardour/user_bundle.h" #include "ardour/utils.h" +#include "ardour/vca_manager.h" +#include "ardour/vca.h" #include "midi++/port.h" #include "midi++/mmc.h" @@ -301,19 +304,21 @@ Session::Session (AudioEngine &eng, , first_file_header_format_reset (true) , have_looped (false) , _have_rec_enabled_track (false) - , _have_rec_disabled_track (true) + , _have_rec_disabled_track (true) , _step_editors (0) , _suspend_timecode_transmission (0) , _speakers (new Speakers) - , _order_hint (-1) , ignore_route_processor_changes (false) , midi_clock (0) , _scene_changer (0) , _midi_ports (0) , _mmc (0) + , _vca_manager (new VCAManager (*this)) { uint32_t sr = 0; + created_with = string_compose ("%1 %2", PROGRAM_NAME, revision); + pthread_mutex_init (&_rt_emit_mutex, 0); pthread_cond_init (&_rt_emit_cond, 0); @@ -321,6 +326,7 @@ Session::Session (AudioEngine &eng, pthread_cond_init (&_auto_connect_cond, 0); init_name_id_counter (1); // reset for new sessions, start at 1 + VCA::set_next_vca_number (1); // reset for new sessions, start at 1 pre_engine_init (fullpath); @@ -381,8 +387,9 @@ Session::Session (AudioEngine &eng, */ if (state_tree) { - const XMLProperty* prop; - if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) { + XMLProperty const * prop; + XMLNode const * root (state_tree->root()); + if ((prop = root->property (X_("sample-rate"))) != 0) { sr = atoi (prop->value()); } } @@ -560,6 +567,7 @@ Session::immediately_post_engine () } try { + LocaleGuard lg; BootMessage (_("Set up LTC")); setup_ltc (); BootMessage (_("Set up Click")); @@ -692,6 +700,11 @@ Session::destroy () DEBUG_TRACE (DEBUG::Destruction, "delete regions\n"); RegionFactory::delete_all_regions (); + /* Do this early so that VCAs no longer hold references to routes */ + + DEBUG_TRACE (DEBUG::Destruction, "delete vcas\n"); + delete _vca_manager; + DEBUG_TRACE (DEBUG::Destruction, "delete routes\n"); /* reset these three references to special routes before we do the usual route delete thing */ @@ -783,9 +796,7 @@ Session::destroy () DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_list_ptrs (); -#endif + BOOST_SHOW_POINTERS (); } void @@ -1139,15 +1150,14 @@ Session::add_monitor_section () return; } - boost::shared_ptr r (new Route (*this, _("Monitor"), Route::MonitorOut, DataType::AUDIO)); + boost::shared_ptr r (new Route (*this, _("Monitor"), PresentationInfo::MonitorOut, DataType::AUDIO)); if (r->init ()) { return; } -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - // boost_debug_shared_ptr_mark_interesting (r.get(), "Route"); -#endif + BOOST_MARK_ROUTE(r); + try { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); r->input()->ensure_io (_master_out->output()->n_ports(), false, this); @@ -1158,7 +1168,7 @@ Session::add_monitor_section () } rl.push_back (r); - add_routes (rl, false, false, false); + add_routes (rl, false, false, false, 0); assert (_monitor_out); @@ -1304,8 +1314,12 @@ Session::reset_monitor_section () _master_out->output()->disconnect (this); _monitor_out->output()->disconnect (this); - _monitor_out->input()->ensure_io (_master_out->output()->n_ports(), false, this); - _monitor_out->output()->ensure_io (_master_out->output()->n_ports(), false, this); + // monitor section follow master bus - except midi + ChanCount mon_chn (_master_out->output()->n_ports()); + mon_chn.set_midi (0); + + _monitor_out->input()->ensure_io (mon_chn, false, this); + _monitor_out->output()->ensure_io (mon_chn, false, this); for (uint32_t n = 0; n < limit; ++n) { boost::shared_ptr p = _monitor_out->input()->ports().nth_audio_port (n); @@ -1500,7 +1514,7 @@ Session::set_track_monitor_input_status (bool yn) boost::shared_ptr rl = routes.reader (); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && tr->record_enabled ()) { + if (tr && tr->rec_enable_control()->get_value()) { //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; tr->request_input_monitoring (yn); } @@ -1935,15 +1949,9 @@ void Session::set_all_tracks_record_enabled (bool enable ) { boost::shared_ptr rl = routes.reader(); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr) { - tr->set_record_enabled (enable, Controllable::NoGroup); - } - } + set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), enable, Controllable::NoGroup); } - void Session::disable_record (bool rt_context, bool force) { @@ -2304,8 +2312,7 @@ Session::resort_routes_using (boost::shared_ptr r) #ifndef NDEBUG DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n"); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n", - (*i)->name(), (*i)->order_key ())); + DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 presentation order %2\n", (*i)->name(), (*i)->presentation_info().order())); } #endif @@ -2423,8 +2430,10 @@ Session::default_track_name_pattern (DataType t) * @param instrument plugin info for the instrument to insert pre-fader, if any */ list > -Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost::shared_ptr instrument, - TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) +Session::new_midi_track (const ChanCount& input, const ChanCount& output, + boost::shared_ptr instrument, Plugin::PresetRecord* pset, + RouteGroup* route_group, uint32_t how_many, string name_template, PresentationInfo::order_t order, + TrackMode mode) { string track_name; uint32_t track_id = 0; @@ -2444,7 +2453,7 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost: boost::shared_ptr track; try { - track.reset (new MidiTrack (*this, track_name, Route::Flag (0), mode)); + track.reset (new MidiTrack (*this, track_name, mode)); if (track->init ()) { goto failed; @@ -2456,9 +2465,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost: track->use_new_diskstream(); -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - // boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); -#endif + BOOST_MARK_TRACK (track); + { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (track->input()->ensure_io (input, false, this)) { @@ -2480,14 +2488,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost: track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); - if (Config->get_remote_model() == UserOrdered) { - track->set_remote_control_id (next_control_id()); - } - new_routes.push_back (track); ret.push_back (track); - - RouteAddedOrRemoved (true); /* EMIT SIGNAL */ } catch (failed_constructor &err) { @@ -2508,14 +2510,17 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost: if (!new_routes.empty()) { StateProtector sp (this); if (Profile->get_trx()) { - add_routes (new_routes, false, false, false); + add_routes (new_routes, false, false, false, order); } else { - add_routes (new_routes, true, true, false); + add_routes (new_routes, true, true, false, order); } if (instrument) { for (RouteList::iterator r = new_routes.begin(); r != new_routes.end(); ++r) { PluginPtr plugin = instrument->load (*this); + if (pset) { + plugin->load_preset (*pset); + } boost::shared_ptr p (new PluginInsert (*this, plugin)); (*r)->add_processor (p, PreFader); @@ -2527,7 +2532,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost: } RouteList -Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr instrument) +Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr instrument, Plugin::PresetRecord* pset, + PresentationInfo::Flag flag, PresentationInfo::order_t order) { string bus_name; uint32_t bus_id = 0; @@ -2543,7 +2549,7 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name } try { - boost::shared_ptr bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); // XXX Editor::add_routes is not ready for ARDOUR::DataType::MIDI + boost::shared_ptr bus (new Route (*this, bus_name, flag, DataType::AUDIO)); // XXX Editor::add_routes is not ready for ARDOUR::DataType::MIDI if (bus->init ()) { goto failure; @@ -2553,9 +2559,8 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name bus->set_strict_io (true); } -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route"); -#endif + BOOST_MARK_ROUTE(bus); + { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); @@ -2574,13 +2579,8 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name if (route_group) { route_group->add (bus); } - if (Config->get_remote_model() == UserOrdered) { - bus->set_remote_control_id (next_control_id()); - } ret.push_back (bus); - RouteAddedOrRemoved (true); /* EMIT SIGNAL */ - ARDOUR::GUIIdle (); } catch (failed_constructor &err) { @@ -2600,11 +2600,14 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name failure: if (!ret.empty()) { StateProtector sp (this); - add_routes (ret, false, false, false); + add_routes (ret, false, false, false, order); if (instrument) { for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) { PluginPtr plugin = instrument->load (*this); + if (pset) { + plugin->load_preset (*pset); + } boost::shared_ptr p (new PluginInsert (*this, plugin)); (*r)->add_processor (p, PreFader); } @@ -2924,12 +2927,39 @@ Session::reconnect_mmc_ports(bool inputs) #endif +void +Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new_order, uint32_t how_many) +{ + if (first_new_order == PresentationInfo::max_order) { + /* adding at end, no worries */ + return; + } + + /* create a gap in the presentation info to accomodate @param how_many + * new objects. + */ + boost::shared_ptr rd = routes.reader(); + + for (RouteList::iterator ri = rd->begin(); ri != rd->end(); ++ri) { + boost::shared_ptr rt (*ri); + + if (rt->presentation_info().special()) { + continue; + } + + if (rt->presentation_info().order () >= first_new_order) { + rt->set_presentation_order (rt->presentation_info().order () + how_many); + } + } +} + /** Caller must not hold process lock * @param name_template string to use for the start of the name, or "" to use "Audio". */ list< boost::shared_ptr > -Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, - uint32_t how_many, string name_template) +Session::new_audio_track (int input_channels, int output_channels, RouteGroup* route_group, + uint32_t how_many, string name_template, PresentationInfo::order_t order, + TrackMode mode) { string track_name; uint32_t track_id = 0; @@ -2950,7 +2980,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod boost::shared_ptr track; try { - track.reset (new AudioTrack (*this, track_name, Route::Flag (0), mode)); + track.reset (new AudioTrack (*this, track_name, mode)); if (track->init ()) { goto failed; @@ -2960,7 +2990,6 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod track->set_strict_io (true); } - if (ARDOUR::Profile->get_trx ()) { // TRACKS considers it's not a USE CASE, it's // a piece of behavior of the session model: @@ -2971,15 +3000,14 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod // 0 for Stereo Out mode // 0 Multi Out mode if (Config->get_output_auto_connect() & AutoConnectMaster) { - track->set_gain (dB_to_coefficient (0), Controllable::NoGroup); + track->gain_control()->set_value (dB_to_coefficient (0), Controllable::NoGroup); } } track->use_new_diskstream(); -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - // boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); -#endif + BOOST_MARK_TRACK (track); + { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); @@ -3007,14 +3035,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod track->non_realtime_input_change(); track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); - if (Config->get_remote_model() == UserOrdered) { - track->set_remote_control_id (next_control_id()); - } new_routes.push_back (track); ret.push_back (track); - - RouteAddedOrRemoved (true); /* EMIT SIGNAL */ } catch (failed_constructor &err) { @@ -3035,9 +3058,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod if (!new_routes.empty()) { StateProtector sp (this); if (Profile->get_trx()) { - add_routes (new_routes, false, false, false); + add_routes (new_routes, false, false, false, order); } else { - add_routes (new_routes, true, true, false); + add_routes (new_routes, true, true, false, order); } } @@ -3048,7 +3071,8 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod * @param name_template string to use for the start of the name, or "" to use "Bus". */ RouteList -Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template) +Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template, + PresentationInfo::Flag flags, PresentationInfo::order_t order) { string bus_name; uint32_t bus_id = 0; @@ -3064,7 +3088,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r } try { - boost::shared_ptr bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); + boost::shared_ptr bus (new Route (*this, bus_name, flags, DataType::AUDIO)); if (bus->init ()) { goto failure; @@ -3074,9 +3098,8 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r bus->set_strict_io (true); } -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route"); -#endif + BOOST_MARK_ROUTE(bus); + { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); @@ -3099,20 +3122,11 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r if (route_group) { route_group->add (bus); } - if (Config->get_remote_model() == UserOrdered) { - bus->set_remote_control_id (next_control_id()); - } bus->add_internal_return (); - ret.push_back (bus); - - RouteAddedOrRemoved (true); /* EMIT SIGNAL */ - - ARDOUR::GUIIdle (); } - catch (failed_constructor &err) { error << _("Session: could not create new audio route.") << endmsg; goto failure; @@ -3131,9 +3145,9 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r if (!ret.empty()) { StateProtector sp (this); if (Profile->get_trx()) { - add_routes (ret, false, false, false); + add_routes (ret, false, false, false, order); } else { - add_routes (ret, false, true, true); // autoconnect // outputs only + add_routes (ret, false, true, true, order); // autoconnect // outputs only } } @@ -3157,7 +3171,6 @@ RouteList Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::string& name_base, PlaylistDisposition pd) { RouteList ret; - uint32_t control_id; uint32_t number = 0; const uint32_t being_added = how_many; /* This will prevent the use of any existing XML-provided PBD::ID @@ -3166,8 +3179,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s Stateful::ForceIDRegeneration force_ids; IO::disable_connecting (); - control_id = next_control_id (); - while (how_many) { /* We're going to modify the node contents a bit so take a @@ -3209,6 +3220,7 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s case NewPlaylist: rename_playlist = true; break; + default: case CopyPlaylist: case SharePlaylist: rename_playlist = false; @@ -3221,13 +3233,13 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s for (XMLNodeList::iterator i = children.begin(); i != children.end(); ++i) { if ((*i)->name() == X_("Processor")) { /* ForceIDRegeneration does not catch the following */ - XMLProperty* role = (*i)->property (X_("role")); - XMLProperty* type = (*i)->property (X_("type")); + XMLProperty const * role = (*i)->property (X_("role")); + XMLProperty const * type = (*i)->property (X_("type")); if (role && role->value() == X_("Aux")) { /* check if the target bus exists. * we should not save aux-sends in templates. */ - XMLProperty* target = (*i)->property (X_("target")); + XMLProperty const * target = (*i)->property (X_("target")); if (!target) { (*i)->add_property ("type", "dangling-aux-send"); continue; @@ -3253,6 +3265,10 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s (*i)->add_property ("bitslot", buf); (*i)->add_property ("name", name); } + else if (type && type->value() == X_("intreturn")) { + (*i)->remove_property (X_("bitslot")); + (*i)->add_property ("ignore-bitslot", "1"); + } else if (type && type->value() == X_("return")) { // Return::set_state() generates a new one (*i)->remove_property (X_("bitslot")); @@ -3287,9 +3303,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s route->output()->changed (change, this); } - route->set_remote_control_id (control_id); - ++control_id; - boost::shared_ptr track; if ((track = boost::dynamic_pointer_cast (route))) { @@ -3306,8 +3319,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s }; ret.push_back (route); - - RouteAddedOrRemoved (true); /* EMIT SIGNAL */ } catch (failed_constructor &err) { @@ -3327,9 +3338,9 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s if (!ret.empty()) { StateProtector sp (this); if (Profile->get_trx()) { - add_routes (ret, false, false, false); + add_routes (ret, false, false, false, PresentationInfo::max_order); } else { - add_routes (ret, true, true, false); + add_routes (ret, true, true, false, PresentationInfo::max_order); } IO::enable_connecting (); } @@ -3338,11 +3349,11 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s } void -Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, bool save) +Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, bool save, PresentationInfo::order_t order) { try { PBD::Unwinder aip (_adding_routes_in_progress, true); - add_routes_inner (new_routes, input_auto_connect, output_auto_connect); + add_routes_inner (new_routes, input_auto_connect, output_auto_connect, order); } catch (...) { error << _("Adding new tracks/busses failed") << endmsg; @@ -3359,25 +3370,18 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output save_state (_current_snapshot_name); } - reassign_track_numbers(); - update_route_record_state (); RouteAdded (new_routes); /* EMIT SIGNAL */ } void -Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect) +Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, PresentationInfo::order_t order) { ChanCount existing_inputs; ChanCount existing_outputs; - uint32_t order = next_control_id(); - - - if (_order_hint > -1) { - order = _order_hint; - _order_hint = -1; - } + uint32_t n_routes; + uint32_t added = 0; count_existing_track_channels (existing_inputs, existing_outputs); @@ -3385,6 +3389,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool RCUWriter writer (routes); boost::shared_ptr r = writer.get_copy (); r->insert (r->end(), new_routes.begin(), new_routes.end()); + n_routes = r->size(); /* if there is no control out and we're not in the middle of loading, * resort the graph here. if there is a control out, we will resort @@ -3397,15 +3402,18 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool } } - for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("ensure order gap starting at %1 for %2\n", order, new_routes.size())); + ensure_route_presentation_info_gap (order, new_routes.size()); + + for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) { boost::weak_ptr wpr (*x); boost::shared_ptr r (*x); - r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _1, wpr)); - r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr)); - r->solo_isolated_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr)); - r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this)); + r->solo_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2,wpr)); + r->solo_isolate_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr)); + r->mute_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this)); + r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2)); r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1)); @@ -3421,7 +3429,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool if (tr) { tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr (tr))); track_playlist_changed (boost::weak_ptr (tr)); - tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this)); + tr->rec_enable_control()->Changed.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this)); boost::shared_ptr mt = boost::dynamic_pointer_cast (tr); if (mt) { @@ -3430,28 +3438,45 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool } } - if (input_auto_connect || output_auto_connect) { - auto_connect_route (r, input_auto_connect, ChanCount (), ChanCount (), existing_inputs, existing_outputs); - existing_inputs += r->n_inputs(); - existing_outputs += r->n_outputs(); - } + if (!r->presentation_info().special()) { - /* order keys are a GUI responsibility but we need to set up - reasonable defaults because they also affect the remote control - ID in most situations. - */ + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name())); + + /* presentation info order may already have been set from XML */ - if (!r->has_order_key ()) { - if (r->is_auditioner()) { - /* use an arbitrarily high value */ - r->set_order_key (UINT_MAX); + if (!r->presentation_info().order_set()) { + + if (order == PresentationInfo::max_order) { + /* just add to the end */ + r->set_presentation_order_explicit (n_routes + added); + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to NR %1 + %2 = %3\n", n_routes, added, n_routes + added)); + } else { + r->set_presentation_order (order + added); + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to %1 + %2 = %3\n", order, added, order + added)); + } } else { - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("while adding, set %1 to order key %2\n", r->name(), order)); - r->set_order_key (order); - order++; + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order already set to %1\n", r->presentation_info().order())); } } +#ifndef __APPLE__ + /* clang complains: 'operator<<' should be declared prior to the call site or in an associated namespace of one of its + * arguments std::ostream& operator<<(std::ostream& o, ARDOUR::PresentationInfo const& rid)" + */ + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("added route %1, group order %2 type %3 (summary: %4)\n", + r->name(), + r->presentation_info().order(), + enum_2_string (r->presentation_info().flags()), + r->presentation_info())); +#endif + + + if (input_auto_connect || output_auto_connect) { + auto_connect_route (r, input_auto_connect, ChanCount (), ChanCount (), existing_inputs, existing_outputs); + existing_inputs += r->n_inputs(); + existing_outputs += r->n_outputs(); + } + ARDOUR::GUIIdle (); } @@ -3468,6 +3493,8 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool } } } + + reassign_track_numbers (); } void @@ -3556,7 +3583,6 @@ Session::add_internal_send (boost::shared_ptr dest, boost::shared_ptr routes_to_remove) { @@ -3572,7 +3598,7 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) continue; } - (*iter)->set_solo (false, Controllable::NoGroup); + (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup); rs->remove (*iter); @@ -3626,7 +3652,6 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) } // end of RCU Writer scope update_route_solo_state (); - RouteAddedOrRemoved (false); /* EMIT SIGNAL */ update_latency_compensation (); set_dirty(); @@ -3663,7 +3688,7 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) return; } - Route::RemoteControlIDChange(); /* EMIT SIGNAL */ + PresentationInfo::Change(); /* EMIT SIGNAL */ /* save the new state of the world */ @@ -3671,7 +3696,6 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) save_history (_current_snapshot_name); } - reassign_track_numbers(); update_route_record_state (); } @@ -3692,18 +3716,20 @@ Session::route_mute_changed () void Session::route_listen_changed (Controllable::GroupControlDisposition group_override, boost::weak_ptr wpr) { - boost::shared_ptr route = wpr.lock(); + boost::shared_ptr route (wpr.lock()); + if (!route) { - error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_listen_changed")) << endmsg; return; } - if (route->listening_via_monitor ()) { + assert (Config->get_solo_control_is_listen_control()); + + if (route->solo_control()->soloed_by_self_or_masters()) { if (Config->get_exclusive_solo()) { RouteGroup* rg = route->route_group (); - const bool group_already_accounted_for = route->use_group (group_override, &RouteGroup::is_solo); + const bool group_already_accounted_for = (group_override == Controllable::ForGroup); boost::shared_ptr r = routes.reader (); @@ -3713,7 +3739,7 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr continue; } - if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ continue; } @@ -3726,7 +3752,7 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr */ continue; } - (*i)->set_listen (false, Controllable::NoGroup); + (*i)->solo_control()->set_value (0.0, Controllable::NoGroup); } } @@ -3739,20 +3765,19 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr update_route_solo_state (); } + void Session::route_solo_isolated_changed (boost::weak_ptr wpr) { - boost::shared_ptr route = wpr.lock (); + boost::shared_ptr route (wpr.lock()); if (!route) { - /* should not happen */ - error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_isolated_changed")) << endmsg; return; } bool send_changed = false; - if (route->solo_isolated()) { + if (route->solo_isolate_control()->solo_isolated()) { if (_solo_isolated_cnt == 0) { send_changed = true; } @@ -3770,27 +3795,42 @@ Session::route_solo_isolated_changed (boost::weak_ptr wpr) } void -Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDisposition group_override, boost::weak_ptr wpr) +Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlDisposition group_override, boost::weak_ptr wpr) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_change)); + DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_changed)); + + boost::shared_ptr route (wpr.lock()); - if (!self_solo_change) { - // session doesn't care about changes to soloed-by-others + if (!route) { return; } - boost::shared_ptr route = wpr.lock (); - assert (route); + if (Config->get_solo_control_is_listen_control()) { + route_listen_changed (group_override, wpr); + return; + } - boost::shared_ptr r = routes.reader (); - int32_t delta; + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: self %2 masters %3 transition %4\n", route->name(), route->self_soloed(), route->solo_control()->get_masters_value(), route->solo_control()->transitioned_into_solo())); - if (route->self_soloed()) { - delta = 1; - } else { - delta = -1; + if (route->solo_control()->transitioned_into_solo() == 0) { + /* route solo changed by upstream/downstream; not interesting + to Session. + */ + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 not self-soloed nor soloed by master (%2), ignoring\n", route->name(), route->solo_control()->get_masters_value())); + return; + } + + if (route->solo_control()->transitioned_into_solo() == 0) { + /* reason for being soloed changed (e.g. master went away, we + * took over the master state), but actual status did + * not. nothing to do. + */ + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: solo change was change in reason, not status\n", route->name())); } + boost::shared_ptr r = routes.reader (); + int32_t delta = route->solo_control()->transitioned_into_solo (); + /* the route may be a member of a group that has shared-solo * semantics. If so, then all members of that group should follow the * solo of the changed route. But ... this is optional, controlled by a @@ -3821,7 +3861,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi continue; } - if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ continue; } @@ -3835,7 +3875,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi continue; } - (*i)->set_solo (false, group_override); + (*i)->solo_control()->set_value (0.0, group_override); } } @@ -3854,7 +3894,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi continue; } - if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ continue; } @@ -3876,7 +3916,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name())); if (!via_sends_only) { if (!route->soloed_by_others_upstream()) { - (*i)->mod_solo_by_others_downstream (delta); + (*i)->solo_control()->mod_solo_by_others_downstream (delta); } else { DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others upstream\n"); } @@ -3897,15 +3937,15 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi sends are involved. */ DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 feeds %2 via sends only %3 sboD %4 sboU %5\n", - route->name(), - (*i)->name(), - via_sends_only, - route->soloed_by_others_downstream(), - route->soloed_by_others_upstream())); + route->name(), + (*i)->name(), + via_sends_only, + route->soloed_by_others_downstream(), + route->soloed_by_others_upstream())); if (!via_sends_only) { //NB. Triggers Invert Push, which handles soloed by downstream DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta)); - (*i)->mod_solo_by_others_upstream (delta); + (*i)->solo_control()->mod_solo_by_others_upstream (delta); } else { DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name())); } @@ -3930,7 +3970,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1, which neither feeds or is fed by %2\n", (*i)->name(), route->name())); (*i)->act_on_mute (); - (*i)->mute_changed (); + (*i)->mute_control()->Changed (false, Controllable::NoGroup); } SoloChanged (); /* EMIT SIGNAL */ @@ -3952,20 +3992,21 @@ Session::update_route_solo_state (boost::shared_ptr r) } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner() && (*i)->self_soloed()) { - something_soloed = true; - } - - if (!(*i)->is_auditioner() && (*i)->listening_via_monitor()) { + if ((*i)->can_solo()) { if (Config->get_solo_control_is_listen_control()) { - listeners++; - something_listening = true; + if ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value()) { + listeners++; + something_listening = true; + } } else { - (*i)->set_listen (false, Controllable::NoGroup); + (*i)->set_listen (false); + if ((*i)->can_solo() && ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value())) { + something_soloed = true; + } } } - if ((*i)->solo_isolated()) { + if ((*i)->solo_isolate_control()->solo_isolated()) { isolated++; } } @@ -3991,6 +4032,16 @@ Session::update_route_solo_state (boost::shared_ptr r) something_soloed, listeners, isolated)); } +void +Session::get_stripables (StripableList& sl) const +{ + boost::shared_ptr r = routes.reader (); + sl.insert (sl.end(), r->begin(), r->end()); + + VCAList v = _vca_manager->vcas (); + sl.insert (sl.end(), v.begin(), v.end()); +} + boost::shared_ptr Session::get_routes_with_internal_returns() const { @@ -4006,7 +4057,7 @@ Session::get_routes_with_internal_returns() const } bool -Session::io_name_is_legal (const std::string& name) +Session::io_name_is_legal (const std::string& name) const { boost::shared_ptr r = routes.reader (); @@ -4115,7 +4166,7 @@ Session::routes_using_input_from (const string& str, RouteList& rl) } boost::shared_ptr -Session::route_by_name (string name) +Session::route_by_name (string name) const { boost::shared_ptr r = routes.reader (); @@ -4129,7 +4180,7 @@ Session::route_by_name (string name) } boost::shared_ptr -Session::route_by_id (PBD::ID id) +Session::route_by_id (PBD::ID id) const { boost::shared_ptr r = routes.reader (); @@ -4158,7 +4209,7 @@ Session::processor_by_id (PBD::ID id) const } boost::shared_ptr -Session::track_by_diskstream_id (PBD::ID id) +Session::track_by_diskstream_id (PBD::ID id) const { boost::shared_ptr r = routes.reader (); @@ -4173,22 +4224,52 @@ Session::track_by_diskstream_id (PBD::ID id) } boost::shared_ptr -Session::route_by_remote_id (uint32_t id) +Session::get_remote_nth_route (PresentationInfo::order_t n) const { - boost::shared_ptr r = routes.reader (); + return boost::dynamic_pointer_cast (get_remote_nth_stripable (n, PresentationInfo::Route)); +} - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i)->remote_control_id() == id) { - return *i; +boost::shared_ptr +Session::get_nth_stripable (PresentationInfo::order_t n) const +{ + StripableList sl; + + get_stripables (sl); + + for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) { + if ((*s)->presentation_info().order() == n) { + return *s; } } - return boost::shared_ptr ((Route*) 0); + /* there is no nth stripable */ + + return boost::shared_ptr(); } +boost::shared_ptr +Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo::Flag flags) const +{ + StripableList sl; + PresentationInfo::order_t match_cnt = 0; + + get_stripables (sl); + sl.sort (Stripable::PresentationOrderSorter()); + + for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) { + if ((*s)->presentation_info().flag_match (flags)) { + if (match_cnt++ == n) { + return *s; + } + } + } + + /* there is no nth stripable that matches the given flags */ + return boost::shared_ptr(); +} boost::shared_ptr -Session::route_by_selected_count (uint32_t id) +Session::route_by_selected_count (uint32_t id) const { boost::shared_ptr r = routes.reader (); @@ -4199,6 +4280,19 @@ Session::route_by_selected_count (uint32_t id) return boost::shared_ptr ((Route*) 0); } +struct PresentationOrderSorter { + bool operator() (boost::shared_ptr a, boost::shared_ptr b) { + if (a->presentation_info().special() && !b->presentation_info().special()) { + /* a is not ordered, b is; b comes before a */ + return false; + } else if (!b->presentation_info().order_set() && a->presentation_info().order_set()) { + /* b is not ordered, a is; a comes before b */ + return true; + } else { + return a->presentation_info().order() < b->presentation_info().order(); + } + } +}; void Session::reassign_track_numbers () @@ -4206,7 +4300,7 @@ Session::reassign_track_numbers () int64_t tn = 0; int64_t bn = 0; RouteList r (*(routes.reader ())); - SignalOrderRouteSorter sorter; + PresentationOrderSorter sorter; r.sort (sorter); StateProtector sp (this); @@ -4233,6 +4327,16 @@ Session::reassign_track_numbers () // trigger GUI re-layout config.ParameterChanged("track-name-number"); } + +#ifndef NDEBUG + if (DEBUG_ENABLED(DEBUG::OrderKeys)) { + boost::shared_ptr rl = routes.reader (); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 numbered %2\n", (*i)->name(), (*i)->track_number())); + } + } +#endif /* NDEBUG */ + } void @@ -5108,7 +5212,7 @@ Session::setup_lua () " assert(self.scripts[n] == nil, 'Callback \"'.. n ..'\" already exists.')" " self.scripts[n] = { ['f'] = f, ['a'] = a }" " local env = _ENV; env.f = nil env.io = nil env.os = nil env.loadfile = nil env.require = nil env.dofile = nil env.package = nil env.debug = nil" - " local env = { print = print, Session = Session, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall }" + " local env = { print = print, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall, Session = Session, PBD = PBD, Timecode = Timecode, Evoral = Evoral, C = C, ARDOUR = ARDOUR }" " self.instances[n] = load (string.dump(f, true), nil, nil, env)(a)" " Session:scripts_changed()" // call back " end" @@ -5282,7 +5386,7 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost:: if (b->is_monitor()) { return false; } - return a->order_key () < b->order_key (); + return a->presentation_info().order() < b->presentation_info().order(); } bool @@ -5437,6 +5541,16 @@ Session::tempo_map_changed (const PropertyChange&) set_dirty (); } +void +Session::gui_tempo_map_changed () +{ + clear_clicks (); + + playlists->update_after_tempo_map_change (); + + _locations->apply (*this, &Session::update_locations_after_tempo_map_change); +} + void Session::update_locations_after_tempo_map_change (const Locations::LocationList& loc) { @@ -5919,7 +6033,6 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end, } unblock_processing (); - itt.done = true; return result; } @@ -6037,7 +6150,7 @@ Session::update_route_record_state () while (i != rl->end ()) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && tr->record_enabled ()) { + if (tr && tr->rec_enable_control()->get_value()) { break; } @@ -6054,7 +6167,7 @@ Session::update_route_record_state () for (i = rl->begin(); i != rl->end (); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && !tr->record_enabled ()) { + if (tr && !tr->rec_enable_control()->get_value()) { break; } } @@ -6082,12 +6195,15 @@ Session::listen_position_changed () void Session::solo_control_mode_changed () { - /* cancel all solo or all listen when solo control mode changes */ - - if (soloing()) { - set_solo (get_routes(), false); - } else if (listening()) { - set_listen (get_routes(), false); + if (soloing() || listening()) { + /* We can't use ::clear_all_solo_state() here because during + session loading at program startup, that will queue a call + to rt_clear_all_solo_state() that will not execute until + AFTER solo states have been established (thus throwing away + the session's saved solo state). So just explicitly turn + them all off. + */ + set_controls (route_list_to_control_list (get_routes(), &Stripable::solo_control), 0.0, Controllable::NoGroup); } } @@ -6610,75 +6726,25 @@ Session::session_name_is_legal (const string& path) return 0; } -uint32_t -Session::next_control_id () const -{ - int subtract = 0; - - /* the monitor bus remote ID is in a different - * "namespace" than regular routes. its existence doesn't - * affect normal (low) numbered routes. - */ - - if (_monitor_out) { - subtract++; - } - - /* the same about masterbus in Waves Tracks */ - - if (Profile->get_trx() && _master_out) { - subtract++; - } - - return nroutes() - subtract; -} - void -Session::notify_remote_id_change () +Session::notify_presentation_info_change () { if (deletion_in_progress()) { return; } - switch (Config->get_remote_model()) { - case MixerOrdered: - Route::RemoteControlIDChange (); /* EMIT SIGNAL */ - break; - default: - break; - } + PresentationInfo::Change (); /* EMIT SIGNAL */ + reassign_track_numbers(); #ifdef USE_TRACKS_CODE_FEATURES - /* Waves Tracks: for Waves Tracks session it's required to reconnect their IOs - * if track order has been changed by user - */ - reconnect_existing_routes(true, true); + /* Waves Tracks: for Waves Tracks session it's required to reconnect their IOs + * if track order has been changed by user + */ + reconnect_existing_routes(true, true); #endif } -void -Session::sync_order_keys () -{ - if (deletion_in_progress()) { - return; - } - - /* tell everyone that something has happened to the sort keys - and let them sync up with the change(s) - this will give objects that manage the sort order keys the - opportunity to keep them in sync if they wish to. - */ - - DEBUG_TRACE (DEBUG::OrderKeys, "Sync Order Keys.\n"); - - reassign_track_numbers(); - - Route::SyncOrderKeys (); /* EMIT SIGNAL */ - - DEBUG_TRACE (DEBUG::OrderKeys, "\tsync done\n"); -} - bool Session::operation_in_progress (GQuark op) const {