X-Git-Url: https://main.carlh.net/gitweb/?p=ardour.git;a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=61bfce14b6bfc995f0203b44e42bdf17652e60d8;hp=ef5ace53e8aa53384ab3ca17df698e9b6ecd9007;hb=c8c6bca6587450ff64303dbc994a4cd28d6ce7aa;hpb=59f7df38a20c38ee255b0c89e3c693175165652b diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ef5ace53e8..61bfce14b6 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,13 +106,15 @@ #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" #include "LuaBridge/LuaBridge.h" -#include "i18n.h" +#include "pbd/i18n.h" #include @@ -175,6 +178,7 @@ Session::Session (AudioEngine &eng, , _record_status (Disabled) , _transport_frame (0) , _session_range_location (0) + , _session_range_end_is_free (true) , _slave (0) , _silent (false) , _transport_speed (0) @@ -209,7 +213,9 @@ Session::Session (AudioEngine &eng, , post_export_position (0) , _exporting (false) , _export_rolling (false) + , _realtime_export (false) , _export_preroll (0) + , _export_latency (0) , _pre_export_mmc_enabled (false) , _name (snapshot_name) , _is_new (true) @@ -234,7 +240,7 @@ Session::Session (AudioEngine &eng, , pending_locate_flush (false) , pending_abort (false) , pending_auto_loop (false) - , _mempool ("Session", 1048576) + , _mempool ("Session", 2097152) , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool)) , _n_lua_scripts (0) , _butler (new Butler (*this)) @@ -246,6 +252,7 @@ Session::Session (AudioEngine &eng, , _rt_thread_active (false) , _rt_emit_pending (false) , _ac_thread_active (false) + , _latency_recompute_pending (0) , step_speed (0) , outbound_mtc_timecode_frame (0) , next_quarter_frame_to_send (-1) @@ -301,19 +308,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 +330,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 +391,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 +571,7 @@ Session::immediately_post_engine () } try { + LocaleGuard lg; BootMessage (_("Set up LTC")); setup_ltc (); BootMessage (_("Set up Click")); @@ -692,6 +704,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 */ @@ -745,11 +762,45 @@ Session::destroy () delete midi_clock; delete _tempo_map; + /* clear event queue, the session is gone, nobody is interested in + * those anymore, but they do leak memory if not removed + */ + while (!immediate_events.empty ()) { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + SessionEvent *ev = immediate_events.front (); + DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("Drop event: %1\n", enum_2_string (ev->type))); + immediate_events.pop_front (); + bool remove = true; + bool del = true; + switch (ev->type) { + case SessionEvent::AutoLoop: + case SessionEvent::AutoLoopDeclick: + case SessionEvent::Skip: + case SessionEvent::PunchIn: + case SessionEvent::PunchOut: + case SessionEvent::StopOnce: + case SessionEvent::RangeStop: + case SessionEvent::RangeLocate: + remove = false; + del = false; + break; + case SessionEvent::RealTimeOperation: + process_rtop (ev); + del = false; + default: + break; + } + if (remove) { + del = del && !_remove_event (ev); + } + if (del) { + delete ev; + } + } + DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_list_ptrs (); -#endif + BOOST_SHOW_POINTERS (); } void @@ -1103,15 +1154,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); @@ -1122,7 +1172,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); @@ -1268,8 +1318,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); @@ -1464,7 +1518,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); } @@ -1899,15 +1953,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) { @@ -1982,59 +2030,55 @@ framepos_t Session::audible_frame () const { framepos_t ret; - framepos_t tf; - framecnt_t offset; - offset = worst_playback_latency (); + frameoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start (); + offset *= transport_speed (); if (synced_to_engine()) { /* Note: this is basically just sync-to-JACK */ - tf = _engine.transport_frame(); + ret = _engine.transport_frame(); } else { - tf = _transport_frame; + ret = _transport_frame; } - ret = tf; - - if (!non_realtime_work_pending()) { - - /* MOVING */ + if (transport_rolling()) { + ret -= offset; /* Check to see if we have passed the first guaranteed - audible frame past our last start position. if not, - return that last start point because in terms - of audible frames, we have not moved yet. - - `Start position' in this context means the time we last - either started, located, or changed transport direction. - */ + * audible frame past our last start position. if not, + * return that last start point because in terms + * of audible frames, we have not moved yet. + * + * `Start position' in this context means the time we last + * either started, located, or changed transport direction. + */ if (_transport_speed > 0.0f) { if (!play_loop || !have_looped) { - if (tf < _last_roll_or_reversal_location + offset) { + if (ret < _last_roll_or_reversal_location) { return _last_roll_or_reversal_location; } + } else { + // latent loops + Location *location = _locations->auto_loop_location(); + frameoffset_t lo = location->start() - ret; + if (lo > 0) { + ret = location->end () - lo; + } } - - /* forwards */ - ret -= offset; - } else if (_transport_speed < 0.0f) { /* XXX wot? no backward looping? */ - if (tf > _last_roll_or_reversal_location - offset) { + if (ret > _last_roll_or_reversal_location) { return _last_roll_or_reversal_location; - } else { - /* backwards */ - ret += offset; } } } - return ret; + return std::max ((framepos_t)0, ret); } void @@ -2268,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 @@ -2387,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; @@ -2408,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; @@ -2420,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)) { @@ -2444,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) { @@ -2472,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); @@ -2491,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; @@ -2507,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; @@ -2517,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 ()); @@ -2538,13 +2579,9 @@ 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()); - } + bus->add_internal_return (); ret.push_back (bus); - RouteAddedOrRemoved (true); /* EMIT SIGNAL */ - ARDOUR::GUIIdle (); } catch (failed_constructor &err) { @@ -2564,11 +2601,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); } @@ -2888,12 +2928,40 @@ 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. + */ + StripableList sl; + get_stripables (sl); + + for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) { + boost::shared_ptr s (*si); + + if (s->is_monitor() || s->is_auditioner()) { + continue; + } + + if (s->presentation_info().order () >= first_new_order) { + s->set_presentation_order (s->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; @@ -2914,7 +2982,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; @@ -2924,7 +2992,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: @@ -2935,15 +3002,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 ()); @@ -2971,14 +3037,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) { @@ -2999,9 +3060,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); } } @@ -3012,7 +3073,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; @@ -3028,7 +3090,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; @@ -3038,9 +3100,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 ()); @@ -3063,20 +3124,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; @@ -3095,9 +3147,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 } } @@ -3121,7 +3173,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 @@ -3130,8 +3181,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 @@ -3173,6 +3222,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; @@ -3185,13 +3235,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; @@ -3217,6 +3267,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")); @@ -3251,9 +3305,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))) { @@ -3270,8 +3321,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) { @@ -3291,9 +3340,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 (); } @@ -3302,11 +3351,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; @@ -3323,25 +3372,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); @@ -3349,6 +3391,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 @@ -3361,17 +3404,21 @@ 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)); + r->processor_latency_changed.connect_same_thread (*this, boost::bind (&Session::queue_latency_recompute, this)); if (r->is_master()) { _master_out = r; @@ -3385,7 +3432,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) { @@ -3394,28 +3441,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 (n_routes + added, false); + 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())); } } +#if !defined(__APPLE__) && !defined(__FreeBSD__) + /* 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 (); } @@ -3432,6 +3496,8 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool } } } + + reassign_track_numbers (); } void @@ -3520,7 +3586,6 @@ Session::add_internal_send (boost::shared_ptr dest, boost::shared_ptr routes_to_remove) { @@ -3536,7 +3601,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); @@ -3590,7 +3655,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(); @@ -3627,7 +3691,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 */ @@ -3635,7 +3699,6 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) save_history (_current_snapshot_name); } - reassign_track_numbers(); update_route_record_state (); } @@ -3656,18 +3719,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 (); @@ -3677,7 +3742,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; } @@ -3690,7 +3755,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); } } @@ -3703,20 +3768,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; } @@ -3734,27 +3798,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)); - if (!self_solo_change) { - // session doesn't care about changes to soloed-by-others + boost::shared_ptr route (wpr.lock()); + + 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 @@ -3785,7 +3864,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; } @@ -3799,7 +3878,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); } } @@ -3818,8 +3897,10 @@ 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 */ + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 excluded from solo because iso = %2 can_solo = %3\n", (*i)->name(), (*i)->solo_isolate_control()->solo_isolated(), + (*i)->can_solo())); continue; } @@ -3840,7 +3921,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"); } @@ -3861,15 +3942,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())); } @@ -3894,7 +3975,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 */ @@ -3916,20 +3997,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++; } } @@ -3955,6 +4037,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 { @@ -3970,7 +4062,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 (); @@ -4079,7 +4171,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 (); @@ -4093,7 +4185,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 (); @@ -4122,7 +4214,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 (); @@ -4137,22 +4229,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_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().hidden()) { + /* if the caller didn't explicitly ask for hidden + stripables, ignore hidden ones. This matches + the semantics of the pre-PresentationOrder + "get by RID" logic of Ardour 4.x and earlier. + + XXX at some point we should likely reverse + the logic of the flags, because asking for "the + hidden stripables" is not going to be common, + whereas asking for visible ones is normal. + */ + + if (! (flags & PresentationInfo::Hidden)) { + continue; + } + } + + if ((*s)->presentation_info().flag_match (flags)) { + if (match_cnt++ == n) { + return *s; + } } } - return boost::shared_ptr ((Route*) 0); + /* 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 (); @@ -4163,6 +4285,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 () @@ -4170,7 +4305,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); @@ -4197,6 +4332,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 @@ -4262,12 +4407,18 @@ Session::maybe_update_session_range (framepos_t a, framepos_t b) _session_range_location->set_start (a); } - if (b > _session_range_location->end()) { + if (_session_range_end_is_free && (b > _session_range_location->end())) { _session_range_location->set_end (b); } } } +void +Session::set_end_is_free (bool yn) +{ + _session_range_end_is_free = yn; +} + void Session::playlist_ranges_moved (list > const & ranges) { @@ -5046,6 +5197,7 @@ Session::try_run_lua (pframes_t nframes) Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK); if (tm.locked ()) { try { (*_lua_run)(nframes); } catch (luabridge::LuaException const& e) { } + lua.collect_garbage_step (); } } @@ -5055,6 +5207,7 @@ Session::setup_lua () #ifndef NDEBUG lua.Print.connect (&_lua_print); #endif + lua.tweak_rt_gc (); lua.do_command ( "function ArdourSession ()" " local self = { scripts = {}, instances = {} }" @@ -5072,7 +5225,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" @@ -5246,7 +5399,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 @@ -5401,6 +5554,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) { @@ -5883,7 +6046,6 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end, } unblock_processing (); - itt.done = true; return result; } @@ -6001,7 +6163,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; } @@ -6018,7 +6180,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; } } @@ -6046,12 +6208,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); } } @@ -6367,9 +6532,10 @@ Session::unknown_processors () const void Session::update_latency (bool playback) { + DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE"))); - if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress) { + if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) { return; } @@ -6573,75 +6739,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 { @@ -6749,6 +6865,16 @@ Session::auto_connect_route (boost::shared_ptr route, bool connect_inputs } } +void +Session::queue_latency_recompute () +{ + g_atomic_int_inc (&_latency_recompute_pending); + if (pthread_mutex_trylock (&_auto_connect_mutex) == 0) { + pthread_cond_signal (&_auto_connect_cond); + pthread_mutex_unlock (&_auto_connect_mutex); + } +} + void Session::auto_connect (const AutoConnectRequest& ar) { @@ -6914,7 +7040,32 @@ Session::auto_connect_thread_run () } } + if (!actively_recording ()) { // might not be needed, + /* this is only used for updating plugin latencies, the + * graph does not change. so it's safe in general. + * BUT.. + * .. update_latency_compensation () entails set_capture_offset() + * which calls Diskstream::set_capture_offset () which + * modifies the capture offset... which can be a proplem + * in "prepare_to_stop" + */ + while (g_atomic_int_and (&_latency_recompute_pending, 0)) { + update_latency_compensation (); + } + } + pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex); } pthread_mutex_unlock (&_auto_connect_mutex); } + +void +Session::cancel_all_solo () +{ + StripableList sl; + + get_stripables (sl); + + set_controls (stripable_list_to_control_list (sl, &Stripable::solo_control), 0.0, Controllable::NoGroup); + clear_all_solo_state (routes.reader()); +}