X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=19c7f5638f3bf9d76b139316b653c5611b0cc3dc;hb=4fc42bca44651b927d336110c23e789691f3bb48;hp=8a3eb6a6c68c164a5932ac5ab4f3eecf6269d308;hpb=3b0c5e35411fdb1a22eae54c8c574668b62f9c56;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 8a3eb6a6c6..19c7f5638f 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -44,6 +44,7 @@ #include "pbd/stacktrace.h" #include "pbd/file_utils.h" #include "pbd/convert.h" +#include "pbd/strsplit.h" #include "ardour/amp.h" #include "ardour/analyser.h" @@ -98,6 +99,8 @@ #include "ardour/tempo.h" #include "ardour/utils.h" #include "ardour/graph.h" +#include "ardour/speakers.h" +#include "ardour/operations.h" #include "midi++/port.h" #include "midi++/mmc.h" @@ -108,19 +111,17 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -using boost::shared_ptr; -using boost::weak_ptr; bool Session::_disable_all_loaded_plugins = false; PBD::Signal1 Session::Dialog; PBD::Signal0 Session::AskAboutPendingState; -PBD::Signal2 Session::AskAboutSampleRateMismatch; +PBD::Signal2 Session::AskAboutSampleRateMismatch; PBD::Signal0 Session::SendFeedback; +PBD::Signal3 Session::MissingFile; -PBD::Signal0 Session::TimecodeOffsetChanged; -PBD::Signal0 Session::StartTimeChanged; -PBD::Signal0 Session::EndTimeChanged; +PBD::Signal1 Session::StartTimeChanged; +PBD::Signal1 Session::EndTimeChanged; PBD::Signal0 Session::AutoBindingOn; PBD::Signal0 Session::AutoBindingOff; PBD::Signal2 Session::Exported; @@ -131,39 +132,39 @@ static void clean_up_session_event (SessionEvent* ev) { delete ev; } const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event); Session::Session (AudioEngine &eng, - const string& fullpath, - const string& snapshot_name, + const string& fullpath, + const string& snapshot_name, BusProfile* bus_profile, - string mix_template) - + string mix_template) : _engine (eng) - , _target_transport_speed (0.0) - , _requested_return_frame (-1) - , _session_dir (new SessionDirectory(fullpath)) - , state_tree (0) + , _target_transport_speed (0.0) + , _requested_return_frame (-1) + , _session_dir (new SessionDirectory(fullpath)) + , state_tree (0) , _state_of_the_state (Clean) - , _butler (new Butler (*this)) - , _post_transport_work (0) - , _send_timecode_update (false) - , _all_route_group (new RouteGroup (*this, "all")) - , route_graph (new Graph(*this)) - , routes (new RouteList) - , _total_free_4k_blocks (0) - , _bundles (new BundleList) - , _bundle_xml_node (0) - , _click_io ((IO*) 0) - , click_data (0) - , click_emphasis_data (0) - , main_outs (0) - , _metadata (new SessionMetadata()) - , _have_rec_enabled_track (false) - , _suspend_timecode_transmission (0) + , _butler (new Butler (*this)) + , _post_transport_work (0) + , _send_timecode_update (false) + , _all_route_group (new RouteGroup (*this, "all")) + , route_graph (new Graph(*this)) + , routes (new RouteList) + , _total_free_4k_blocks (0) + , _bundles (new BundleList) + , _bundle_xml_node (0) + , _current_trans (0) + , _click_io ((IO*) 0) + , click_data (0) + , click_emphasis_data (0) + , main_outs (0) + , _metadata (new SessionMetadata()) + , _have_rec_enabled_track (false) + , _suspend_timecode_transmission (0) { _locations = new Locations (*this); playlists.reset (new SessionPlaylists); - _all_route_group->set_active (true, this); + _all_route_group->set_active (true, this); interpolation.add_channel_to (0, 0); @@ -176,14 +177,14 @@ Session::Session (AudioEngine &eng, first_stage_init (fullpath, snapshot_name); - _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); if (_is_new) { if (create (mix_template, bus_profile)) { destroy (); throw failed_constructor (); } - } + } if (second_stage_init ()) { destroy (); @@ -203,7 +204,10 @@ Session::Session (AudioEngine &eng, DirtyChanged (); /* EMIT SIGNAL */ } - _is_new = false; + StartTimeChanged.connect_same_thread (*this, boost::bind (&Session::start_time_changed, this, _1)); + EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1)); + + _is_new = false; } Session::~Session () @@ -234,10 +238,6 @@ Session::destroy () delete state_tree; - /* remove all stubfiles that might still be lurking */ - - cleanup_stubfiles (); - /* reset dynamic state version back to default */ Stateful::loading_state_version = 0; @@ -245,7 +245,7 @@ Session::destroy () _butler->drop_references (); delete _butler; delete midi_control_ui; - delete _all_route_group; + delete _all_route_group; if (click_data != default_click) { delete [] click_data; @@ -297,8 +297,6 @@ Session::destroy () } routes.flush (); - boost::shared_ptr r = routes.reader (); - DEBUG_TRACE (DEBUG::Destruction, "delete sources\n"); for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) { DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for source %1 ; pre-ref = %2\n", i->second->path(), i->second.use_count())); @@ -318,29 +316,13 @@ Session::destroy () /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */ playlists.reset (); - boost_debug_list_ptrs (); - delete _locations; DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); -} - -void -Session::set_worst_io_latencies () -{ - _worst_output_latency = 0; - _worst_input_latency = 0; - - if (!_engine.connected()) { - return; - } - - boost::shared_ptr r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - _worst_output_latency = max (_worst_output_latency, (*i)->output()->latency()); - _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency()); - } +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + boost_debug_list_ptrs (); +#endif } void @@ -429,8 +411,6 @@ Session::when_engine_running () BootMessage (_("Compute I/O Latencies")); - set_worst_io_latencies (); - if (_clicking) { // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO? } @@ -458,7 +438,7 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); - shared_ptr c (new Bundle (buf, true)); + boost::shared_ptr c (new Bundle (buf, true)); c->add_channel (_("mono"), DataType::AUDIO); c->set_port (0, outputs[DataType::AUDIO][np]); @@ -471,7 +451,7 @@ Session::when_engine_running () if (np + 1 < outputs[DataType::AUDIO].size()) { char buf[32]; snprintf (buf, sizeof(buf), _("out %" PRIu32 "+%" PRIu32), np + 1, np + 2); - shared_ptr c (new Bundle (buf, true)); + boost::shared_ptr c (new Bundle (buf, true)); c->add_channel (_("L"), DataType::AUDIO); c->set_port (0, outputs[DataType::AUDIO][np]); c->add_channel (_("R"), DataType::AUDIO); @@ -487,7 +467,7 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); - shared_ptr c (new Bundle (buf, false)); + boost::shared_ptr c (new Bundle (buf, false)); c->add_channel (_("mono"), DataType::AUDIO); c->set_port (0, inputs[DataType::AUDIO][np]); @@ -501,7 +481,7 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof(buf), _("in %" PRIu32 "+%" PRIu32), np + 1, np + 2); - shared_ptr c (new Bundle (buf, false)); + boost::shared_ptr c (new Bundle (buf, false)); c->add_channel (_("L"), DataType::AUDIO); c->set_port (0, inputs[DataType::AUDIO][np]); c->add_channel (_("R"), DataType::AUDIO); @@ -517,7 +497,7 @@ Session::when_engine_running () string n = inputs[DataType::MIDI][np]; boost::erase_first (n, X_("alsa_pcm:")); - shared_ptr c (new Bundle (n, false)); + boost::shared_ptr c (new Bundle (n, false)); c->add_channel ("", DataType::MIDI); c->set_port (0, inputs[DataType::MIDI][np]); add_bundle (c); @@ -529,7 +509,7 @@ Session::when_engine_running () string n = outputs[DataType::MIDI][np]; boost::erase_first (n, X_("alsa_pcm:")); - shared_ptr c (new Bundle (n, true)); + boost::shared_ptr c (new Bundle (n, true)); c->add_channel ("", DataType::MIDI); c->set_port (0, outputs[DataType::MIDI][np]); add_bundle (c); @@ -541,7 +521,9 @@ Session::when_engine_running () if (_is_new && !no_auto_connect()) { - /* don't connect the master bus outputs if there is a monitor bus */ + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock()); + + /* don't connect the master bus outputs if there is a monitor bus */ if (_master_out && Config->get_auto_connect_standard_busses() && !_monitor_out) { @@ -613,7 +595,7 @@ Session::when_engine_running () } } else { - + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { uint32_t mod = n_physical_outputs.get (*t); uint32_t limit = _monitor_out->n_outputs().get(*t); @@ -642,15 +624,14 @@ Session::when_engine_running () } } - /* catch up on send+insert cnts */ - _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); /* hook us up to the engine */ BootMessage (_("Connect to engine")); - _engine.set_session (this); + + update_latency_compensation (true); } void @@ -662,7 +643,6 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting); - if (!auditioner) { /* we delay creating the auditioner till now because @@ -670,13 +650,12 @@ Session::hookup_io () */ try { - Auditioner* a = new Auditioner (*this); - if (a->init()) { - delete a; - throw failed_constructor(); - } - a->use_new_diskstream (); - auditioner.reset (a); + boost::shared_ptr a (new Auditioner (*this)); + if (a->init()) { + throw failed_constructor (); + } + a->use_new_diskstream (); + auditioner = a; } catch (failed_constructor& err) { @@ -699,32 +678,29 @@ Session::hookup_io () Delivery::reset_panners (); - /* Connect tracks to monitor/listen bus if there is one. - Note that in an existing session, the internal sends will - already exist, but we want the routes to notice that - they connect to the control out specifically. - */ + /* Connect tracks to monitor/listen bus if there is one. Note that in an + existing session, the internal sends will already exist, but we want the + routes to notice that they connect to the control out specifically. + */ - if (_monitor_out) { + if (_monitor_out) { boost::shared_ptr r = routes.reader (); - for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { + for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { - if ((*x)->is_monitor()) { - - /* relax */ + if ((*x)->is_monitor()) { - } else if ((*x)->is_master()) { + /* relax */ - /* relax */ + } else if ((*x)->is_master()) { - } else { + /* relax */ - (*x)->listen_via (_monitor_out, - (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), - false, false); - } - } - } + } else { + + (*x)->listen_via_monitor (); + } + } + } /* Anyone who cares about input state, wake up and do something */ @@ -747,12 +723,6 @@ Session::hookup_io () update_route_solo_state (); } -void -Session::playlist_length_changed () -{ - update_session_range_location_marker (); -} - void Session::track_playlist_changed (boost::weak_ptr wp) { @@ -764,10 +734,10 @@ Session::track_playlist_changed (boost::weak_ptr wp) boost::shared_ptr playlist; if ((playlist = track->playlist()) != 0) { - playlist->LengthChanged.connect_same_thread (*this, boost::bind (&Session::playlist_length_changed, this)); + playlist->RegionAdded.connect_same_thread (*this, boost::bind (&Session::playlist_region_added, this, _1)); + playlist->RangesMoved.connect_same_thread (*this, boost::bind (&Session::playlist_ranges_moved, this, _1)); + playlist->RegionsExtended.connect_same_thread (*this, boost::bind (&Session::playlist_regions_extended, this, _1)); } - - update_session_range_location_marker (); } bool @@ -784,30 +754,26 @@ Session::record_enabling_legal () const return true; } +void +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 ()) { + //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; + tr->monitor_input (yn); + } + } +} + void Session::reset_input_monitor_state () { if (transport_rolling()) { - - 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 ()) { - //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; - tr->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input()); - } - } - + set_track_monitor_input_status (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input()); } else { - - 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 ()) { - //cerr << "switching to input = " << !Config->get_auto_input() << __FILE__ << __LINE__ << endl << endl; - tr->monitor_input (Config->get_monitoring_model() == HardwareMonitoring); - } - } + set_track_monitor_input_status (Config->get_monitoring_model() == HardwareMonitoring); } } @@ -825,7 +791,7 @@ Session::auto_punch_start_changed (Location* location) void Session::auto_punch_end_changed (Location* location) { - nframes_t when_to_stop = location->end(); + framepos_t when_to_stop = location->end(); // when_to_stop += _worst_output_latency + _worst_input_latency; replace_event (SessionEvent::PunchOut, when_to_stop); } @@ -833,7 +799,7 @@ Session::auto_punch_end_changed (Location* location) void Session::auto_punch_changed (Location* location) { - nframes_t when_to_stop = location->end(); + framepos_t when_to_stop = location->end(); replace_event (SessionEvent::PunchIn, location->start()); //when_to_stop += _worst_output_latency + _worst_input_latency; @@ -1005,33 +971,26 @@ Session::handle_locations_changed (Locations::LocationList& locations) void Session::enable_record () { - while (1) { - RecordState rs = (RecordState) g_atomic_int_get (&_record_status); + while (1) { + RecordState rs = (RecordState) g_atomic_int_get (&_record_status); - if (rs == Recording) { - break; - } + if (rs == Recording) { + break; + } - if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) { + if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) { - _last_record_location = _transport_frame; + _last_record_location = _transport_frame; MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe)); - if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { - - 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 ()) { - tr->monitor_input (true); - } - } - } + if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { + set_track_monitor_input_status (true); + } - RecordStateChanged (); - break; - } - } + RecordStateChanged (); + break; + } + } } void @@ -1051,14 +1010,7 @@ Session::disable_record (bool rt_context, bool force) } if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { - - 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 ()) { - tr->monitor_input (false); - } - } + set_track_monitor_input_status (false); } RecordStateChanged (); /* emit signal */ @@ -1075,14 +1027,7 @@ Session::step_back_from_record () if (g_atomic_int_compare_and_exchange (&_record_status, Recording, Enabled)) { if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { - 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 ()) { - //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl; - tr->monitor_input (false); - } - } + set_track_monitor_input_status (false); } } } @@ -1090,9 +1035,9 @@ Session::step_back_from_record () void Session::maybe_enable_record () { - if (_step_editors > 0) { - return; - } + if (_step_editors > 0) { + return; + } g_atomic_int_set (&_record_status, Enabled); @@ -1121,7 +1066,7 @@ Session::audible_frame () const { framepos_t ret; framepos_t tf; - nframes_t offset; + framecnt_t offset; /* the first of these two possible settings for "offset" mean that the audible frame is stationary until @@ -1133,7 +1078,7 @@ Session::audible_frame () const in the absence of any plugin latency compensation */ - offset = _worst_output_latency; + offset = worst_playback_latency (); if (offset > current_block_size) { offset -= current_block_size; @@ -1196,9 +1141,9 @@ Session::audible_frame () const } void -Session::set_frame_rate (nframes_t frames_per_second) +Session::set_frame_rate (framecnt_t frames_per_second) { - /** \fn void Session::set_frame_size(nframes_t) + /** \fn void Session::set_frame_size(framecnt_t) the AudioEngine object that calls this guarantees that it will not be called while we are also in ::process(). Its fine to do things that block @@ -1209,7 +1154,7 @@ Session::set_frame_rate (nframes_t frames_per_second) sync_time_vars(); - Automatable::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval()))); + Automatable::set_automation_interval (ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval()))); clear_clicks (); @@ -1222,7 +1167,7 @@ Session::set_frame_rate (nframes_t frames_per_second) } void -Session::set_block_size (nframes_t nframes) +Session::set_block_size (pframes_t nframes) { /* the AudioEngine guarantees that it will not be called while we are also in @@ -1253,48 +1198,14 @@ Session::set_block_size (nframes_t nframes) } } -void -Session::set_default_fade (float /*steepness*/, float /*fade_msecs*/) -{ -#if 0 - nframes_t fade_frames; - - /* Don't allow fade of less 1 frame */ - - if (fade_msecs < (1000.0 * (1.0/_current_frame_rate))) { - - fade_msecs = 0; - fade_frames = 0; - - } else { - - fade_frames = (nframes_t) floor (fade_msecs * _current_frame_rate * 0.001); - - } - - default_fade_msecs = fade_msecs; - default_fade_steepness = steepness; - - { - // jlc, WTF is this! - Glib::RWLock::ReaderLock lm (route_lock); - AudioRegion::set_default_fade (steepness, fade_frames); - } - - set_dirty(); - - /* XXX have to do this at some point */ - /* foreach region using default fade, reset, then - refill_all_diskstream_buffers (); - */ -#endif -} - struct RouteSorter { + /** @return true to run r1 before r2, otherwise false */ bool operator() (boost::shared_ptr r1, boost::shared_ptr r2) { if (r2->feeds (r1)) { + /* r1 fed by r2; run r2 early */ return false; } else if (r1->feeds (r2)) { + /* r2 fed by r1; run r1 early */ return true; } else { if (r1->not_fed ()) { @@ -1306,16 +1217,22 @@ struct RouteSorter { return true; } } else { - return r1->order_key(N_("signal")) < r2->order_key(N_("signal")); + if (r2->not_fed()) { + /* r1 has connections, r2 does not; run r2 early */ + return false; + } else { + /* both r1 and r2 have connections, but not to each other. just use signal order */ + return r1->order_key(N_("signal")) < r2->order_key(N_("signal")); + } } } } }; static void -trace_terminal (shared_ptr r1, shared_ptr rbase) +trace_terminal (boost::shared_ptr r1, boost::shared_ptr rbase) { - shared_ptr r2; + boost::shared_ptr r2; if (r1->feeds (rbase) && rbase->feeds (r1)) { info << string_compose(_("feedback loop setup between %1 and %2"), r1->name(), rbase->name()) << endmsg; @@ -1324,7 +1241,7 @@ trace_terminal (shared_ptr r1, shared_ptr rbase) /* make a copy of the existing list of routes that feed r1 */ - Route::FedBy existing (r1->fed_by()); + Route::FedBy existing (r1->fed_by()); /* for each route that feeds r1, recurse, marking it as feeding rbase as well. @@ -1375,7 +1292,7 @@ Session::resort_routes () { RCUWriter writer (routes); - shared_ptr r = writer.get_copy (); + boost::shared_ptr r = writer.get_copy (); resort_routes_using (r); /* writer goes out of scope and forces update */ } @@ -1383,24 +1300,24 @@ Session::resort_routes () //route_graph->dump(1); #ifndef NDEBUG - boost::shared_ptr rl = routes.reader (); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name())); + boost::shared_ptr rl = routes.reader (); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name())); - const Route::FedBy& fb ((*i)->fed_by()); - - for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) { - boost::shared_ptr sf = f->r.lock(); - if (sf) { - DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only)); - } - } - } + const Route::FedBy& fb ((*i)->fed_by()); + + for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) { + boost::shared_ptr sf = f->r.lock(); + if (sf) { + DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only)); + } + } + } #endif } void -Session::resort_routes_using (shared_ptr r) +Session::resort_routes_using (boost::shared_ptr r) { RouteList::iterator i, j; @@ -1420,7 +1337,7 @@ Session::resort_routes_using (shared_ptr r) continue; } - bool via_sends_only; + bool via_sends_only; if ((*j)->direct_feeds (*i, &via_sends_only)) { (*i)->add_fed_by (*j, via_sends_only); @@ -1438,29 +1355,38 @@ Session::resort_routes_using (shared_ptr r) route_graph->rechain (r); #ifndef NDEBUG - DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n"); + 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 ("signal"))); + (*i)->name(), (*i)->order_key ("signal"))); } #endif } -/** Find the route name starting with \a base with the lowest \a id. +/** Find a route name starting with \a base, maybe followed by the + * lowest \a id. \a id will always be added if \a definitely_add_number + * is true on entry; otherwise it will only be added if required + * to make the name unique. * - * Names are constructed like e.g. "Audio 3" for base="Audio" and id=3. - * The available route name with the lowest ID will be used, and \a id - * will be set to the ID. + * Names are constructed like e.g. "Audio 3" for base="Audio" and id=3. + * The available route name with the lowest ID will be used, and \a id + * will be set to the ID. * - * \return false if a route name could not be found, and \a track_name - * and \a id do not reflect a free route name. + * \return false if a route name could not be found, and \a track_name + * and \a id do not reflect a free route name. */ bool -Session::find_route_name (const char* base, uint32_t& id, char* name, size_t name_len) +Session::find_route_name (string const & base, uint32_t& id, char* name, size_t name_len, bool definitely_add_number) { + if (!definitely_add_number && route_by_name (base) == 0) { + /* juse use the base */ + snprintf (name, name_len, "%s", base.c_str()); + return true; + } + do { - snprintf (name, name_len, "%s %" PRIu32, base, id); + snprintf (name, name_len, "%s %" PRIu32, base.c_str(), id); if (route_by_name (name) == 0) { return true; @@ -1473,70 +1399,74 @@ Session::find_route_name (const char* base, uint32_t& id, char* name, size_t nam return false; } -/** Count the total ins and outs of all non-hidden routes in the session and return them in in and out */ +/** Count the total ins and outs of all non-hidden tracks in the session and return them in in and out */ void -Session::count_existing_route_channels (ChanCount& in, ChanCount& out) +Session::count_existing_track_channels (ChanCount& in, ChanCount& out) { in = ChanCount::ZERO; out = ChanCount::ZERO; - shared_ptr r = routes.reader (); + + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { - in += (*i)->n_inputs(); - out += (*i)->n_outputs(); + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr && !tr->is_hidden()) { + cerr << "Using track i/o counts for " << tr->name() << endl; + in += tr->n_inputs(); + out += tr->n_outputs(); } } } +/** Caller must not hold process lock + * @param name_template string to use for the start of the name, or "" to use "Midi". + */ list > -Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many) +Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) { char track_name[32]; uint32_t track_id = 0; - ChanCount existing_inputs; - ChanCount existing_outputs; string port; RouteList new_routes; list > ret; uint32_t control_id; - count_existing_route_channels (existing_inputs, existing_outputs); - control_id = ntracks() + nbusses(); + bool const use_number = (how_many != 1); + while (how_many) { - if (!find_route_name ("Midi", ++track_id, track_name, sizeof(track_name))) { + if (!find_route_name (name_template.empty() ? _("Midi") : name_template, ++track_id, track_name, sizeof(track_name), use_number)) { error << "cannot find name for new midi track" << endmsg; goto failed; } - shared_ptr track; + boost::shared_ptr track; try { - MidiTrack* mt = new MidiTrack (*this, track_name, Route::Flag (0), mode); - - if (mt->init ()) { - delete mt; - goto failed; - } - - mt->use_new_diskstream(); + track.reset (new MidiTrack (*this, track_name, Route::Flag (0), mode)); - boost_debug_shared_ptr_mark_interesting (mt, "Track"); - track = boost::shared_ptr(mt); - - if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { - error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + if (track->init ()) { goto failed; } + track->use_new_diskstream(); - if (track->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { - error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; - goto failed; - } +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); +#endif + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { + error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + goto failed; + } - auto_connect_route (track.get(), existing_inputs, existing_outputs); + if (track->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { + error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + goto failed; + } + } track->non_realtime_input_change(); @@ -1567,7 +1497,7 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m failed: if (!new_routes.empty()) { - add_routes (new_routes, false); + add_routes (new_routes, true, false); save_state (_current_snapshot_name); } @@ -1579,10 +1509,20 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m * @param output_start As \a input_start, but for outputs. */ void -Session::auto_connect_route ( - Route* route, ChanCount& existing_inputs, ChanCount& existing_outputs, bool connect_inputs, ChanCount input_start, ChanCount output_start - ) +Session::auto_connect_route (Route* route, ChanCount& existing_inputs, ChanCount& existing_outputs, + bool with_lock, bool connect_inputs, ChanCount input_start, ChanCount output_start) { + if (!IO::connecting_legal) { + cerr << "Auto-connect ignored because connecting it not legal\n"; + return; + } + + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock (), Glib::NOT_LOCK); + + if (with_lock) { + lm.acquire (); + } + /* If both inputs and outputs are auto-connected to physical ports, use the max of input and output offsets to ensure auto-connected port numbers always match up (e.g. the first audio input and the @@ -1591,14 +1531,18 @@ Session::auto_connect_route ( offset possible. */ + DEBUG_TRACE (DEBUG::Graph, + string_compose("Auto-connect: existing in = %1 out = %2\n", + existing_inputs, existing_outputs)); + const bool in_out_physical = - (Config->get_input_auto_connect() & AutoConnectPhysical) + (Config->get_input_auto_connect() & AutoConnectPhysical) && (Config->get_output_auto_connect() & AutoConnectPhysical) && connect_inputs; const ChanCount in_offset = in_out_physical ? ChanCount::max(existing_inputs, existing_outputs) - : existing_inputs; + : existing_inputs; const ChanCount out_offset = in_out_physical ? ChanCount::max(existing_inputs, existing_outputs) @@ -1613,15 +1557,28 @@ Session::auto_connect_route ( if (!physinputs.empty() && connect_inputs) { uint32_t nphysical_in = physinputs.size(); + + DEBUG_TRACE (DEBUG::Graph, + string_compose("There are %1 physical inputs of type %2\n", + nphysical_in, *t)); + for (uint32_t i = input_start.get(*t); i < route->n_inputs().get(*t) && i < nphysical_in; ++i) { string port; if (Config->get_input_auto_connect() & AutoConnectPhysical) { + DEBUG_TRACE (DEBUG::Graph, + string_compose("Get index %1 + %2 % %3 = %4\n", + in_offset.get(*t), i, nphysical_in, + (in_offset.get(*t) + i) % nphysical_in)); port = physinputs[(in_offset.get(*t) + i) % nphysical_in]; } + DEBUG_TRACE (DEBUG::Graph, + string_compose("Connect route %1 IN to %2\n", + route->name(), port)); + if (!port.empty() && route->input()->connect ( - route->input()->ports().port(*t, i), port, this)) { + route->input()->ports().port(*t, i), port, this)) { break; } } @@ -1641,6 +1598,10 @@ Session::auto_connect_route ( } } + DEBUG_TRACE (DEBUG::Graph, + string_compose("Connect route %1 OUT to %2\n", + route->name(), port)); + if (!port.empty() && route->output()->connect ( route->output()->ports().port(*t, i), port, this)) { break; @@ -1648,65 +1609,66 @@ Session::auto_connect_route ( } } } - - existing_inputs += route->n_inputs(); - existing_outputs += route->n_outputs(); } +/** 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) +Session::new_audio_track ( + int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template + ) { char track_name[32]; uint32_t track_id = 0; - ChanCount existing_inputs; - ChanCount existing_outputs; string port; RouteList new_routes; list > ret; uint32_t control_id; - count_existing_route_channels (existing_inputs, existing_outputs); - control_id = ntracks() + nbusses() + 1; + bool const use_number = (how_many != 1); + while (how_many) { - if (!find_route_name ("Audio", ++track_id, track_name, sizeof(track_name))) { + if (!find_route_name (name_template.empty() ? _("Audio") : name_template, ++track_id, track_name, sizeof(track_name), use_number)) { error << "cannot find name for new audio track" << endmsg; goto failed; } - shared_ptr track; - + boost::shared_ptr track; + try { - AudioTrack* at = new AudioTrack (*this, track_name, Route::Flag (0), mode); - - if (at->init ()) { - delete at; - goto failed; - } - - at->use_new_diskstream(); + track.reset (new AudioTrack (*this, track_name, Route::Flag (0), mode)); - boost_debug_shared_ptr_mark_interesting (at, "Track"); - track = boost::shared_ptr(at); - - if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { - error << string_compose ( - _("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; + if (track->init ()) { goto failed; } - if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { - error << string_compose ( - _("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; - goto failed; - } + track->use_new_diskstream(); - auto_connect_route (track.get(), existing_inputs, existing_outputs); +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); +#endif + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { + error << string_compose ( + _("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failed; + } + + if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { + error << string_compose ( + _("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failed; + } + } if (route_group) { route_group->add (track); @@ -1738,7 +1700,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod failed: if (!new_routes.empty()) { - add_routes (new_routes, true); + add_routes (new_routes, true, true); } return ret; @@ -1750,7 +1712,7 @@ Session::set_remote_control_ids () RemoteModel m = Config->get_remote_model(); bool emit_signal = false; - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (MixerOrdered == m) { @@ -1771,65 +1733,63 @@ Session::set_remote_control_ids () } } - +/** Caller must not hold process lock. + * @param name_template string to use for the start of the name, or "" to use "Bus". + */ RouteList -Session::new_audio_route (bool aux, int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many) +Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template) { char bus_name[32]; uint32_t bus_id = 0; - ChanCount existing_inputs; - ChanCount existing_outputs; string port; RouteList ret; uint32_t control_id; - count_existing_route_channels (existing_inputs, existing_outputs); - control_id = ntracks() + nbusses() + 1; + bool const use_number = (how_many != 1); while (how_many) { - if (!find_route_name ("Bus", ++bus_id, bus_name, sizeof(bus_name))) { + if (!find_route_name (name_template.empty () ? _("Bus") : name_template, ++bus_id, bus_name, sizeof(bus_name), use_number)) { error << "cannot find name for new audio bus" << endmsg; goto failure; } try { - Route* rt = new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO); + boost::shared_ptr bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); - if (rt->init ()) { - delete rt; - goto failure; - } - - boost_debug_shared_ptr_mark_interesting (rt, "Route"); - shared_ptr bus (rt); - - if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { - error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; + if (bus->init ()) { goto failure; } - - if (bus->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { - error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; - goto failure; +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + boost_debug_shared_ptr_mark_interesting (bus.get(), "Route"); +#endif + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failure; + } + + + if (bus->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failure; + } } - auto_connect_route (bus.get(), existing_inputs, existing_outputs, false); - if (route_group) { route_group->add (bus); } bus->set_remote_control_id (control_id); ++control_id; - if (aux) { - bus->add_internal_return (); - } + bus->add_internal_return (); ret.push_back (bus); } @@ -1851,7 +1811,7 @@ Session::new_audio_route (bool aux, int input_channels, int output_channels, Rou failure: if (!ret.empty()) { - add_routes (ret, true); + add_routes (ret, true, true); } return ret; @@ -1882,7 +1842,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template std::string node_name = IO::name_from_state (*node_copy.children().front()); /* generate a new name by adding a number to the end of the template name */ - if (!find_route_name (node_name.c_str(), ++number, name, sizeof(name))) { + if (!find_route_name (node_name.c_str(), ++number, name, sizeof(name), true)) { fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; /*NOTREACHED*/ } @@ -1898,7 +1858,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template Track::zero_diskstream_id_in_xml (node_copy); try { - shared_ptr route (XMLRouteFactory (node_copy, 3000)); + boost::shared_ptr route (XMLRouteFactory (node_copy, 3000)); if (route == 0) { error << _("Session: cannot create track/bus from template description") << endmsg; @@ -1910,6 +1870,9 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template picks up the configuration of the route. During session loading this normally happens in a different way. */ + + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + IOChange change (IOChange::Type (IOChange::ConfigurationChanged | IOChange::ConnectionsChanged)); change.after = route->input()->n_ports(); route->input()->changed (change, this); @@ -1938,18 +1901,23 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template out: if (!ret.empty()) { - add_routes (ret, true); + add_routes (ret, true, true); } return ret; } void -Session::add_routes (RouteList& new_routes, bool save) +Session::add_routes (RouteList& new_routes, bool auto_connect, bool save) { + ChanCount existing_inputs; + ChanCount existing_outputs; + + count_existing_track_channels (existing_inputs, existing_outputs); + { RCUWriter writer (routes); - shared_ptr r = writer.get_copy (); + boost::shared_ptr r = writer.get_copy (); r->insert (r->end(), new_routes.begin(), new_routes.end()); @@ -1991,10 +1959,15 @@ Session::add_routes (RouteList& new_routes, bool save) track_playlist_changed (boost::weak_ptr (tr)); tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_track, this)); - boost::shared_ptr mt = boost::dynamic_pointer_cast (tr); - if (mt) { - mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1)); - } + boost::shared_ptr mt = boost::dynamic_pointer_cast (tr); + if (mt) { + mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1)); + } + } + + if (auto_connect) { + + auto_connect_route (r, existing_inputs, existing_outputs, true); } } @@ -2002,14 +1975,12 @@ Session::add_routes (RouteList& new_routes, bool save) for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { if ((*x)->is_monitor()) { - /* relax */ - } else if ((*x)->is_master()) { - /* relax */ + /* relax */ + } else if ((*x)->is_master()) { + /* relax */ } else { - (*x)->listen_via (_monitor_out, - (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), - false, false); - } + (*x)->listen_via_monitor (); + } } resort_routes (); @@ -2031,13 +2002,9 @@ Session::globally_set_send_gains_to_zero (boost::shared_ptr dest) boost::shared_ptr r = routes.reader (); boost::shared_ptr s; - /* only tracks */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i)) { - if ((s = (*i)->internal_send_for (dest)) != 0) { - s->amp()->gain_control()->set_value (0.0); - } + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value (0.0); } } } @@ -2048,13 +2015,9 @@ Session::globally_set_send_gains_to_unity (boost::shared_ptr dest) boost::shared_ptr r = routes.reader (); boost::shared_ptr s; - /* only tracks */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i)) { - if ((s = (*i)->internal_send_for (dest)) != 0) { - s->amp()->gain_control()->set_value (1.0); - } + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value (1.0); } } } @@ -2065,27 +2028,22 @@ Session::globally_set_send_gains_from_track(boost::shared_ptr dest) boost::shared_ptr r = routes.reader (); boost::shared_ptr s; - /* only tracks */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i)) { - if ((s = (*i)->internal_send_for (dest)) != 0) { - s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value()); - } + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value()); } } } +/** @param include_buses true to add sends to buses and tracks, false for just tracks */ void -Session::globally_add_internal_sends (boost::shared_ptr dest, Placement p) +Session::globally_add_internal_sends (boost::shared_ptr dest, Placement p, bool include_buses) { boost::shared_ptr r = routes.reader (); boost::shared_ptr t (new RouteList); - /* only send tracks */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i)) { + if (include_buses || boost::dynamic_pointer_cast(*i)) { t->push_back (*i); } } @@ -2110,24 +2068,24 @@ Session::add_internal_sends (boost::shared_ptr dest, Placement p, boost:: continue; } - (*i)->listen_via (dest, p, true, true); + (*i)->listen_via (dest, p); } graph_reordered (); } void -Session::remove_route (shared_ptr route) +Session::remove_route (boost::shared_ptr route) { - if (((route == _master_out) || (route == _monitor_out)) && !Config->get_allow_special_bus_removal()) { - return; - } + if (((route == _master_out) || (route == _monitor_out)) && !Config->get_allow_special_bus_removal()) { + return; + } - route->set_solo (false, this); + route->set_solo (false, this); { RCUWriter writer (routes); - shared_ptr rs = writer.get_copy (); + boost::shared_ptr rs = writer.get_copy (); rs->remove (route); @@ -2137,7 +2095,7 @@ Session::remove_route (shared_ptr route) */ if (route == _master_out) { - _master_out = shared_ptr (); + _master_out = boost::shared_ptr (); } if (route == _monitor_out) { @@ -2154,8 +2112,7 @@ Session::remove_route (shared_ptr route) /* writer goes out of scope, forces route list update */ } - update_route_solo_state (); - update_session_range_location_marker (); + update_route_solo_state (); // We need to disconnect the route's inputs and outputs @@ -2174,22 +2131,22 @@ Session::remove_route (shared_ptr route) } } - boost::shared_ptr mt = boost::dynamic_pointer_cast (route); - if (mt && mt->step_editing()) { - if (_step_editors > 0) { - _step_editors--; - } - } + boost::shared_ptr mt = boost::dynamic_pointer_cast (route); + if (mt && mt->step_editing()) { + if (_step_editors > 0) { + _step_editors--; + } + } - update_latency_compensation (false, false); + update_latency_compensation (); set_dirty(); - /* Re-sort routes to remove the graph's current references to the one that is + /* Re-sort routes to remove the graph's current references to the one that is * going away, then flush old references out of the graph. - */ + */ resort_routes (); - route_graph->clear_other_chain (); + route_graph->clear_other_chain (); /* get rid of it from the dead wood collection in the route list manager */ @@ -2227,18 +2184,18 @@ Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) return; } - if (route->listening()) { + if (route->listening_via_monitor ()) { - if (Config->get_exclusive_solo()) { - /* new listen: disable all other listen */ - shared_ptr r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { - continue; - } - (*i)->set_listen (false, this); - } - } + if (Config->get_exclusive_solo()) { + /* new listen: disable all other listen */ + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { + continue; + } + (*i)->set_listen (false, this); + } + } _listen_cnt++; @@ -2246,6 +2203,8 @@ Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) _listen_cnt--; } + + update_route_solo_state (); } void Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr wpr) @@ -2258,32 +2217,32 @@ Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr wpr) return; } - bool send_changed = false; - - if (route->solo_isolated()) { - if (_solo_isolated_cnt == 0) { - send_changed = true; - } - _solo_isolated_cnt++; - } else if (_solo_isolated_cnt > 0) { - _solo_isolated_cnt--; - if (_solo_isolated_cnt == 0) { - send_changed = true; - } - } + bool send_changed = false; - if (send_changed) { - IsolatedChanged (); /* EMIT SIGNAL */ - } + if (route->solo_isolated()) { + if (_solo_isolated_cnt == 0) { + send_changed = true; + } + _solo_isolated_cnt++; + } else if (_solo_isolated_cnt > 0) { + _solo_isolated_cnt--; + if (_solo_isolated_cnt == 0) { + send_changed = true; + } + } + + if (send_changed) { + IsolatedChanged (); /* EMIT SIGNAL */ + } } void Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_ptr wpr) { - if (!self_solo_change) { - // session doesn't care about changes to soloed-by-others - return; - } + if (!self_solo_change) { + // session doesn't care about changes to soloed-by-others + return; + } if (solo_update_disabled) { // We know already @@ -2298,7 +2257,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p return; } - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); int32_t delta; if (route->self_soloed()) { @@ -2307,59 +2266,59 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p delta = -1; } - if (delta == 1 && Config->get_exclusive_solo()) { - /* new solo: disable all other solos */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { - continue; - } - (*i)->set_solo (false, this); - } - } + if (delta == 1 && Config->get_exclusive_solo()) { + /* new solo: disable all other solos */ + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { + continue; + } + (*i)->set_solo (false, this); + } + } solo_update_disabled = true; - RouteList uninvolved; + RouteList uninvolved; for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { bool via_sends_only; - bool in_signal_flow; + bool in_signal_flow; if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { continue; } - in_signal_flow = false; + in_signal_flow = false; - if ((*i)->feeds (route, &via_sends_only)) { + if ((*i)->feeds (route, &via_sends_only)) { if (!via_sends_only) { - if (!route->soloed_by_others_upstream()) { - (*i)->mod_solo_by_others_downstream (delta); - } - in_signal_flow = true; + if (!route->soloed_by_others_upstream()) { + (*i)->mod_solo_by_others_downstream (delta); + } + in_signal_flow = true; } } - if (route->feeds (*i, &via_sends_only)) { - (*i)->mod_solo_by_others_upstream (delta); - in_signal_flow = true; - } + if (route->feeds (*i, &via_sends_only)) { + (*i)->mod_solo_by_others_upstream (delta); + in_signal_flow = true; + } - if (!in_signal_flow) { - uninvolved.push_back (*i); - } + if (!in_signal_flow) { + uninvolved.push_back (*i); + } } solo_update_disabled = false; update_route_solo_state (r); - /* now notify that the mute state of the routes not involved in the signal - pathway of the just-solo-changed route may have altered. - */ + /* now notify that the mute state of the routes not involved in the signal + pathway of the just-solo-changed route may have altered. + */ - for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { - (*i)->mute_changed (this); - } + for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { + (*i)->mute_changed (this); + } SoloChanged (); /* EMIT SIGNAL */ set_dirty(); @@ -2371,8 +2330,8 @@ Session::update_route_solo_state (boost::shared_ptr r) /* now figure out if anything that matters is soloed (or is "listening")*/ bool something_soloed = false; - uint32_t listeners = 0; - uint32_t isolated = 0; + uint32_t listeners = 0; + uint32_t isolated = 0; if (!r) { r = routes.reader(); @@ -2383,36 +2342,36 @@ Session::update_route_solo_state (boost::shared_ptr r) something_soloed = true; } - if (!(*i)->is_hidden() && (*i)->listening()) { - if (Config->get_solo_control_is_listen_control()) { - listeners++; - } else { - (*i)->set_listen (false, this); - } - } + if (!(*i)->is_hidden() && (*i)->listening_via_monitor()) { + if (Config->get_solo_control_is_listen_control()) { + listeners++; + } else { + (*i)->set_listen (false, this); + } + } - if ((*i)->solo_isolated()) { - isolated++; - } + if ((*i)->solo_isolated()) { + isolated++; + } } - if (something_soloed != _non_soloed_outs_muted) { - _non_soloed_outs_muted = something_soloed; - SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */ - } + if (something_soloed != _non_soloed_outs_muted) { + _non_soloed_outs_muted = something_soloed; + SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */ + } - _listen_cnt = listeners; + _listen_cnt = listeners; - if (isolated != _solo_isolated_cnt) { - _solo_isolated_cnt = isolated; - IsolatedChanged (); /* EMIT SIGNAL */ - } + if (isolated != _solo_isolated_cnt) { + _solo_isolated_cnt = isolated; + IsolatedChanged (); /* EMIT SIGNAL */ + } } boost::shared_ptr Session::get_routes_with_internal_returns() const { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); boost::shared_ptr rl (new RouteList); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { @@ -2426,25 +2385,25 @@ Session::get_routes_with_internal_returns() const bool Session::io_name_is_legal (const std::string& name) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i)->name() == name) { - return false; - } + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i)->name() == name) { + return false; + } - if ((*i)->has_io_processor_named (name)) { - return false; - } - } + if ((*i)->has_io_processor_named (name)) { + return false; + } + } - return true; + return true; } -shared_ptr +boost::shared_ptr Session::route_by_name (string name) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == name) { @@ -2452,13 +2411,13 @@ Session::route_by_name (string name) } } - return shared_ptr ((Route*) 0); + return boost::shared_ptr ((Route*) 0); } -shared_ptr +boost::shared_ptr Session::route_by_id (PBD::ID id) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->id() == id) { @@ -2466,13 +2425,13 @@ Session::route_by_id (PBD::ID id) } } - return shared_ptr ((Route*) 0); + return boost::shared_ptr ((Route*) 0); } -shared_ptr +boost::shared_ptr Session::route_by_remote_id (uint32_t id) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->remote_control_id() == id) { @@ -2480,67 +2439,88 @@ Session::route_by_remote_id (uint32_t id) } } - return shared_ptr ((Route*) 0); + return boost::shared_ptr ((Route*) 0); } -/** If either end of the session range location marker lies inside the current - * session extent, move it to the corresponding session extent. +void +Session::playlist_region_added (boost::weak_ptr w) +{ + boost::shared_ptr r = w.lock (); + if (!r) { + return; + } + + /* These are the operations that are currently in progress... */ + list curr = _current_trans_quarks; + curr.sort (); + + /* ...and these are the operations during which we want to update + the session range location markers. + */ + list ops; + ops.push_back (Operations::capture); + ops.push_back (Operations::paste); + ops.push_back (Operations::duplicate_region); + ops.push_back (Operations::insert_file); + ops.push_back (Operations::insert_region); + ops.push_back (Operations::drag_region_brush); + ops.push_back (Operations::region_drag); + ops.push_back (Operations::selection_grab); + ops.push_back (Operations::region_fill); + ops.push_back (Operations::fill_selection); + ops.push_back (Operations::create_region); + ops.sort (); + + /* See if any of the current operations match the ones that we want */ + list in; + set_intersection (_current_trans_quarks.begin(), _current_trans_quarks.end(), ops.begin(), ops.end(), back_inserter (in)); + + /* If so, update the session range markers */ + if (!in.empty ()) { + maybe_update_session_range (r->position (), r->last_frame ()); + } +} + +/** Update the session range markers if a is before the current start or + * b is after the current end. */ void -Session::update_session_range_location_marker () +Session::maybe_update_session_range (framepos_t a, framepos_t b) { if (_state_of_the_state & Loading) { return; } - pair const ext = get_extent (); - if (_session_range_location == 0) { - /* we don't have a session range yet; use this one (provided it is valid) */ - if (ext.first != max_framepos) { - add_session_range_location (ext.first, ext.second); - } + + add_session_range_location (a, b); + } else { - /* update the existing session range */ - if (ext.first < _session_range_location->start()) { - _session_range_location->set_start (ext.first); - set_dirty (); - } - if (ext.second > _session_range_location->end()) { - _session_range_location->set_end (ext.second); - set_dirty (); + if (a < _session_range_location->start()) { + _session_range_location->set_start (a); } + if (b > _session_range_location->end()) { + _session_range_location->set_end (b); + } } } -/** @return Extent of the session's contents; if the session is empty, the first value of - * the pair will equal max_framepos. - */ -pair -Session::get_extent () const +void +Session::playlist_ranges_moved (list > const & ranges) { - pair ext (max_framepos, 0); - - 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->destructive()) { - // ignore tape tracks when getting extents - continue; - } - - pair e = tr->playlist()->get_extent (); - if (e.first < ext.first) { - ext.first = e.first; - } - if (e.second > ext.second) { - ext.second = e.second; - } + for (list >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) { + maybe_update_session_range (i->to, i->to + i->length); } +} - return ext; +void +Session::playlist_regions_extended (list > const & ranges) +{ + for (list >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) { + maybe_update_session_range (i->from, i->to); + } } /* Region management */ @@ -2548,7 +2528,7 @@ Session::get_extent () const boost::shared_ptr Session::find_whole_file_parent (boost::shared_ptr child) const { - const RegionFactory::RegionMap& regions (RegionFactory::regions()); + const RegionFactory::RegionMap& regions (RegionFactory::regions()); RegionFactory::RegionMap::const_iterator i; boost::shared_ptr region; @@ -2572,48 +2552,48 @@ Session::find_whole_file_parent (boost::shared_ptr child) const int Session::destroy_sources (list > srcs) { - set > relevant_regions; + set > relevant_regions; for (list >::iterator s = srcs.begin(); s != srcs.end(); ++s) { - RegionFactory::get_regions_using_source (*s, relevant_regions); + RegionFactory::get_regions_using_source (*s, relevant_regions); } - cerr << "There are " << relevant_regions.size() << " using " << srcs.size() << " sources" << endl; + cerr << "There are " << relevant_regions.size() << " using " << srcs.size() << " sources" << endl; - for (set >::iterator r = relevant_regions.begin(); r != relevant_regions.end(); ) { - set >::iterator tmp; + for (set >::iterator r = relevant_regions.begin(); r != relevant_regions.end(); ) { + set >::iterator tmp; - tmp = r; - ++tmp; + tmp = r; + ++tmp; - cerr << "Cleanup " << (*r)->name() << " UC = " << (*r).use_count() << endl; + cerr << "Cleanup " << (*r)->name() << " UC = " << (*r).use_count() << endl; - playlists->destroy_region (*r); - RegionFactory::map_remove (*r); + playlists->destroy_region (*r); + RegionFactory::map_remove (*r); - (*r)->drop_sources (); - (*r)->drop_references (); + (*r)->drop_sources (); + (*r)->drop_references (); - cerr << "\tdone UC = " << (*r).use_count() << endl; + cerr << "\tdone UC = " << (*r).use_count() << endl; - relevant_regions.erase (r); + relevant_regions.erase (r); - r = tmp; - } + r = tmp; + } for (list >::iterator s = srcs.begin(); s != srcs.end(); ) { - { - Glib::Mutex::Lock ls (source_lock); - /* remove from the main source list */ - sources.erase ((*s)->id()); - } + { + Glib::Mutex::Lock ls (source_lock); + /* remove from the main source list */ + sources.erase ((*s)->id()); + } - (*s)->mark_for_remove (); - (*s)->drop_references (); + (*s)->mark_for_remove (); + (*s)->drop_references (); - s = srcs.erase (s); - } + s = srcs.erase (s); + } return 0; } @@ -2663,23 +2643,29 @@ Session::add_source (boost::shared_ptr source) if (result.second) { - /* yay, new source */ + /* yay, new source */ set_dirty(); - boost::shared_ptr afs; + boost::shared_ptr afs; - if ((afs = boost::dynamic_pointer_cast(source)) != 0) { - if (Config->get_auto_analyse_audio()) { - Analyser::queue_source_for_analysis (source, false); - } - } - } + if ((afs = boost::dynamic_pointer_cast(source)) != 0) { + if (Config->get_auto_analyse_audio()) { + Analyser::queue_source_for_analysis (source, false); + } + } + + source->DropReferences.connect_same_thread (*this, boost::bind (&Session::remove_source, this, boost::weak_ptr (source))); + } } void Session::remove_source (boost::weak_ptr src) { + if (_state_of_the_state & Deletion) { + return; + } + SourceMap::iterator i; boost::shared_ptr source = src.lock(); @@ -2691,7 +2677,6 @@ Session::remove_source (boost::weak_ptr src) Glib::Mutex::Lock lm (source_lock); if ((i = sources.find (source->id())) != sources.end()) { - cerr << "Removing source " << source->name() << endl; sources.erase (i); } } @@ -2736,6 +2721,24 @@ Session::source_by_path_and_channel (const string& path, uint16_t chn) return boost::shared_ptr(); } +uint32_t +Session::count_sources_by_origin (const string& path) +{ + uint32_t cnt = 0; + Glib::Mutex::Lock lm (source_lock); + + for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) { + boost::shared_ptr fs + = boost::dynamic_pointer_cast(i->second); + + if (fs && fs->origin() == path) { + ++cnt; + } + } + + return cnt; +} + string Session::change_source_path_by_name (string path, string oldname, string newname, bool destructive) @@ -2760,7 +2763,7 @@ Session::change_source_path_by_name (string path, string oldname, string newname string::size_type dash; dir = Glib::path_get_dirname (path); - path = Glib::path_get_basename (path); + path = Glib::path_get_basename (path); /* '-' is not a legal character for the NAME part of the path */ @@ -2774,8 +2777,7 @@ Session::change_source_path_by_name (string path, string oldname, string newname path += '-'; path += new_legalized; path += native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); - - path = Glib::build_filename (dir, path); + path = Glib::build_filename (dir, path); } else { @@ -2792,7 +2794,7 @@ Session::change_source_path_by_name (string path, string oldname, string newname string::size_type postfix; dir = Glib::path_get_dirname (path); - path = Glib::path_get_basename (path); + path = Glib::path_get_basename (path); /* '-' is not a legal character for the NAME part of the path */ @@ -2824,8 +2826,8 @@ Session::change_source_path_by_name (string path, string oldname, string newname snprintf (buf, sizeof(buf), "%s-%u%s", newname.c_str(), cnt, suffix.c_str()); - if (!matching_unsuffixed_filename_exists_in (dir, buf)) { - path = Glib::build_filename (dir, buf); + if (!matching_unsuffixed_filename_exists_in (dir, buf)) { + path = Glib::build_filename (dir, buf); break; } @@ -2834,8 +2836,8 @@ Session::change_source_path_by_name (string path, string oldname, string newname if (path.empty()) { fatal << string_compose (_("FATAL ERROR! Could not find a suitable version of %1 for a rename"), - newname) << endl; - /*NOTREACHED*/ + newname) << endl; + /*NOTREACHED*/ } } @@ -2847,7 +2849,7 @@ Session::change_source_path_by_name (string path, string oldname, string newname * (e.g. as returned by new_*_source_name) */ string -Session::new_source_path_from_name (DataType type, const string& name, bool as_stub) +Session::new_source_path_from_name (DataType type, const string& name) { assert(name.find("/") == string::npos); @@ -2855,9 +2857,9 @@ Session::new_source_path_from_name (DataType type, const string& name, bool as_s sys::path p; if (type == DataType::AUDIO) { - p = (as_stub ? sdir.sound_stub_path() : sdir.sound_path()); + p = sdir.sound_path(); } else if (type == DataType::MIDI) { - p = (as_stub ? sdir.midi_stub_path() : sdir.midi_path()); + p = sdir.midi_path(); } else { error << "Unknown source type, unable to create file path" << endmsg; return ""; @@ -2871,7 +2873,7 @@ string Session::peak_path (string base) const { sys::path peakfile_path(_session_dir->peak_path()); - peakfile_path /= basename_nosuffix (base) + peakfile_suffix; + peakfile_path /= base + peakfile_suffix; return peakfile_path.to_string(); } @@ -2883,7 +2885,7 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha char buf[PATH_MAX+1]; const uint32_t limit = 10000; string legalized; - string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); + string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); buf[0] = '\0'; legalized = legalize_for_path (base); @@ -2900,21 +2902,21 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha if (nchan < 2) { snprintf (buf, sizeof(buf), "T%04d-%s%s", - cnt, legalized.c_str(), ext.c_str()); + cnt, legalized.c_str(), ext.c_str()); } else if (nchan == 2) { if (chan == 0) { snprintf (buf, sizeof(buf), "T%04d-%s%%L%s", - cnt, legalized.c_str(), ext.c_str()); + cnt, legalized.c_str(), ext.c_str()); } else { snprintf (buf, sizeof(buf), "T%04d-%s%%R%s", - cnt, legalized.c_str(), ext.c_str()); + cnt, legalized.c_str(), ext.c_str()); } } else if (nchan < 26) { snprintf (buf, sizeof(buf), "T%04d-%s%%%c%s", - cnt, legalized.c_str(), 'a' + chan, ext.c_str()); + cnt, legalized.c_str(), 'a' + chan, ext.c_str()); } else { snprintf (buf, sizeof(buf), "T%04d-%s%s", - cnt, legalized.c_str(), ext.c_str()); + cnt, legalized.c_str(), ext.c_str()); } } else { @@ -2937,19 +2939,17 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha SessionDirectory sdir((*i).path); string spath = sdir.sound_path().to_string(); - string spath_stubs = sdir.sound_stub_path().to_string(); - /* note that we search *without* the extension so that - we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf" - in the event that this new name is required for - a file format change. - */ + /* note that we search *without* the extension so that + we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf" + in the event that this new name is required for + a file format change. + */ - if (matching_unsuffixed_filename_exists_in (spath, buf) || - matching_unsuffixed_filename_exists_in (spath_stubs, buf)) { - existing++; - break; - } + if (matching_unsuffixed_filename_exists_in (spath, buf)) { + existing++; + break; + } } if (existing == 0) { @@ -2970,13 +2970,13 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha /** Create a new within-session audio source */ boost::shared_ptr -Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive, bool as_stub) +Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive) { const string name = new_audio_source_name (n, n_chans, chan, destructive); - const string path = new_source_path_from_name(DataType::AUDIO, name, as_stub); + const string path = new_source_path_from_name(DataType::AUDIO, name); return boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, path, string(), destructive, frame_rate())); } /** Return a unique name based on \a base for a new internal MIDI source */ @@ -3030,30 +3030,30 @@ Session::new_midi_source_name (const string& base) /** Create a new within-session MIDI source */ boost::shared_ptr -Session::create_midi_source_for_session (Track* track, string const & n, bool as_stub) +Session::create_midi_source_for_session (Track* track, string const & n) { - /* try to use the existing write source for the track, to keep numbering sane - */ + /* try to use the existing write source for the track, to keep numbering sane + */ - if (track) { - /*MidiTrack* mt = dynamic_cast (track); - assert (mt); - */ + if (track) { + /*MidiTrack* mt = dynamic_cast (track); + assert (mt); + */ - list > l = track->steal_write_sources (); + list > l = track->steal_write_sources (); - if (!l.empty()) { - assert (boost::dynamic_pointer_cast (l.front())); - return boost::dynamic_pointer_cast (l.front()); - } - } + if (!l.empty()) { + assert (boost::dynamic_pointer_cast (l.front())); + return boost::dynamic_pointer_cast (l.front()); + } + } const string name = new_midi_source_name (n); - const string path = new_source_path_from_name (DataType::MIDI, name, as_stub); + const string path = new_source_path_from_name (DataType::MIDI, name); return boost::dynamic_pointer_cast ( - SourceFactory::createWritable ( - DataType::MIDI, *this, path, false, frame_rate())); + SourceFactory::createWritable ( + DataType::MIDI, *this, path, string(), false, frame_rate())); } @@ -3139,12 +3139,12 @@ Session::cancel_audition () bool Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost::shared_ptr b) { - if (a->is_monitor()) { - return true; - } - if (b->is_monitor()) { - return false; - } + if (a->is_monitor()) { + return true; + } + if (b->is_monitor()) { + return false; + } return a->order_key(N_("signal")) < b->order_key(N_("signal")); } @@ -3227,7 +3227,7 @@ Session::available_capture_duration () } void -Session::add_bundle (shared_ptr bundle) +Session::add_bundle (boost::shared_ptr bundle) { { RCUWriter writer (_bundles); @@ -3241,7 +3241,7 @@ Session::add_bundle (shared_ptr bundle) } void -Session::remove_bundle (shared_ptr bundle) +Session::remove_bundle (boost::shared_ptr bundle) { bool removed = false; @@ -3263,7 +3263,7 @@ Session::remove_bundle (shared_ptr bundle) set_dirty(); } -shared_ptr +boost::shared_ptr Session::bundle_by_name (string name) const { boost::shared_ptr b = _bundles.reader (); @@ -3303,7 +3303,7 @@ Session::update_locations_after_tempo_map_change (Locations::LocationList& loc) void Session::ensure_buffers (ChanCount howmany) { - BufferManager::ensure_buffers (howmany); + BufferManager::ensure_buffers (howmany); } void @@ -3414,24 +3414,24 @@ void Session::unmark_send_id (uint32_t id) { if (id < send_bitset.size()) { - send_bitset[id] = false; - } + send_bitset[id] = false; + } } void Session::unmark_return_id (uint32_t id) { if (id < return_bitset.size()) { - return_bitset[id] = false; - } + return_bitset[id] = false; + } } void Session::unmark_insert_id (uint32_t id) { if (id < insert_bitset.size()) { - insert_bitset[id] = false; - } + insert_bitset[id] = false; + } } @@ -3491,12 +3491,12 @@ Session::reset_native_file_format () for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); if (tr) { - /* don't save state as we do this, there's no point - */ + /* don't save state as we do this, there's no point + */ - _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup); + _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup); tr->reset_write_sources (false); - _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup); + _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup); } } } @@ -3504,7 +3504,7 @@ Session::reset_native_file_format () bool Session::route_name_unique (string n) const { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == n) { @@ -3532,7 +3532,7 @@ Session::route_name_internal (string n) const int Session::freeze_all (InterThreadInfo& itt) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { @@ -3559,7 +3559,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, boost::shared_ptr fsource; uint32_t x; char buf[PATH_MAX+1]; - ChanCount nchans(track.n_channels()); + ChanCount diskstream_channels (track.n_channels()); framepos_t position; framecnt_t this_chunk; framepos_t to_do; @@ -3567,8 +3567,9 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, SessionDirectory sdir(get_best_session_directory_for_new_source ()); const string sound_dir = sdir.sound_path().to_string(); framepos_t len = end - start; - bool need_block_size_reset = false; - string ext; + bool need_block_size_reset = false; + string ext; + ChanCount const max_proc = track.max_processor_streams (); if (end <= start) { error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"), @@ -3594,9 +3595,9 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, goto out; } - ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); + ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); - for (uint32_t chan_n=0; chan_n < nchans.n_audio(); ++chan_n) { + for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) { for (x = 0; x < 99999; ++x) { snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 "%s", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1, ext.c_str()); @@ -3612,7 +3613,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, try { fsource = boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, buf, string(), false, frame_rate())); } catch (failed_constructor& err) { @@ -3623,10 +3624,10 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, srcs.push_back (fsource); } - /* tell redirects that care that we are about to use a much larger blocksize */ + /* tell redirects that care that we are about to use a much larger blocksize */ - need_block_size_reset = true; - track.set_block_size (chunk_size); + need_block_size_reset = true; + track.set_block_size (chunk_size); /* XXX need to flush all redirects */ @@ -3634,10 +3635,10 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, to_do = len; /* create a set of reasonably-sized buffers */ - buffers.ensure_buffers(DataType::AUDIO, nchans.n_audio(), chunk_size); - buffers.set_count(nchans); + buffers.ensure_buffers (DataType::AUDIO, max_proc.n_audio(), chunk_size); + buffers.set_count (max_proc); - for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { + for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); if (afs) afs->prepare_for_peakfile_writes (); @@ -3719,9 +3720,9 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, } - if (need_block_size_reset) { - track.set_block_size (get_block_size()); - } + if (need_block_size_reset) { + track.set_block_size (get_block_size()); + } unblock_processing (); @@ -3731,65 +3732,38 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, gain_t* Session::gain_automation_buffer() const { - return ProcessThread::gain_automation_buffer (); + return ProcessThread::gain_automation_buffer (); } pan_t** Session::pan_automation_buffer() const { - return ProcessThread::pan_automation_buffer (); + return ProcessThread::pan_automation_buffer (); } BufferSet& Session::get_silent_buffers (ChanCount count) { - return ProcessThread::get_silent_buffers (count); -#if 0 - assert(_silent_buffers->available() >= count); - _silent_buffers->set_count(count); - - for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - for (size_t i= 0; i < count.get(*t); ++i) { - _silent_buffers->get(*t, i).clear(); - } - } - - return *_silent_buffers; -#endif + return ProcessThread::get_silent_buffers (count); } BufferSet& Session::get_scratch_buffers (ChanCount count) { - return ProcessThread::get_scratch_buffers (count); -#if 0 - if (count != ChanCount::ZERO) { - assert(_scratch_buffers->available() >= count); - _scratch_buffers->set_count(count); - } else { - _scratch_buffers->set_count (_scratch_buffers->available()); - } - - return *_scratch_buffers; -#endif + return ProcessThread::get_scratch_buffers (count); } BufferSet& Session::get_mix_buffers (ChanCount count) { - return ProcessThread::get_mix_buffers (count); -#if 0 - assert(_mix_buffers->available() >= count); - _mix_buffers->set_count(count); - return *_mix_buffers; -#endif + return ProcessThread::get_mix_buffers (count); } uint32_t Session::ntracks () const { uint32_t n = 0; - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { @@ -3804,7 +3778,7 @@ uint32_t Session::nbusses () const { uint32_t n = 0; - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { if (boost::dynamic_pointer_cast(*i) == 0) { @@ -3881,22 +3855,10 @@ Session::update_have_rec_enabled_track () void Session::listen_position_changed () { - Placement p; - - switch (Config->get_listen_position()) { - case AfterFaderListen: - p = PostFader; - break; - - case PreFaderListen: - p = PreFader; - break; - } - boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->put_monitor_send_at (p); + (*i)->listen_position_changed (); } } @@ -3905,11 +3867,11 @@ 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()) { + set_solo (get_routes(), false); + } else if (listening()) { + set_listen (get_routes(), false); + } } /** Called when anything about any of our route groups changes (membership, state etc.) */ @@ -3934,8 +3896,8 @@ Session::get_available_sync_options () const boost::shared_ptr Session::get_routes_with_regions_at (framepos_t const p) const { - shared_ptr r = routes.reader (); - shared_ptr rl (new RouteList); + boost::shared_ptr r = routes.reader (); + boost::shared_ptr rl (new RouteList); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); @@ -3989,7 +3951,7 @@ Session::current_end_frame () const } void -Session::add_session_range_location (nframes_t start, nframes_t end) +Session::add_session_range_location (framepos_t start, framepos_t end) { _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange); _locations->add (_session_range_location); @@ -4005,26 +3967,360 @@ Session::route_order_key_changed () void Session::step_edit_status_change (bool yn) { - bool send = false; + bool send = false; - bool val = false; - if (yn) { - send = (_step_editors == 0); - val = true; + bool val = false; + if (yn) { + send = (_step_editors == 0); + val = true; - _step_editors++; - } else { - send = (_step_editors == 1); - val = false; + _step_editors++; + } else { + send = (_step_editors == 1); + val = false; - if (_step_editors > 0) { - _step_editors--; - } - } + if (_step_editors > 0) { + _step_editors--; + } + } + + if (send) { + StepEditStatusChange (val); + } +} + + +void +Session::start_time_changed (framepos_t old) +{ + /* Update the auto loop range to match the session range + (unless the auto loop range has been changed by the user) + */ + + Location* s = _locations->session_range_location (); + if (s == 0) { + return; + } + + Location* l = _locations->auto_loop_location (); + + if (l->start() == old) { + l->set_start (s->start(), true); + } +} + +void +Session::end_time_changed (framepos_t old) +{ + /* Update the auto loop range to match the session range + (unless the auto loop range has been changed by the user) + */ + + Location* s = _locations->session_range_location (); + if (s == 0) { + return; + } + + Location* l = _locations->auto_loop_location (); + + if (l->end() == old) { + l->set_end (s->end(), true); + } +} + +string +Session::source_search_path (DataType type) const +{ + string search_path; + + if (session_dirs.size() == 1) { + switch (type) { + case DataType::AUDIO: + search_path = _session_dir->sound_path().to_string(); + break; + case DataType::MIDI: + search_path = _session_dir->midi_path().to_string(); + break; + } + } else { + for (vector::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) { + SessionDirectory sdir (i->path); + if (!search_path.empty()) { + search_path += ':'; + } + switch (type) { + case DataType::AUDIO: + search_path += sdir.sound_path().to_string(); + break; + case DataType::MIDI: + search_path += sdir.midi_path().to_string(); + break; + } + } + } + + /* now add user-specified locations + */ + + vector dirs; + + switch (type) { + case DataType::AUDIO: + split (config.get_audio_search_path (), dirs, ':'); + break; + case DataType::MIDI: + split (config.get_midi_search_path (), dirs, ':'); + break; + } - if (send) { - StepEditStatusChange (val); + for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { + search_path += ':'; + search_path += *i; + + } + + return search_path; +} + +void +Session::ensure_search_path_includes (const string& path, DataType type) +{ + string search_path; + vector dirs; + + if (path == ".") { + return; + } + + switch (type) { + case DataType::AUDIO: + search_path = config.get_audio_search_path (); + break; + case DataType::MIDI: + search_path = config.get_midi_search_path (); + break; + } + + split (search_path, dirs, ':'); + + for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { + if (*i == path) { + return; + } + } + + if (!search_path.empty()) { + search_path += ':'; + } + + search_path += path; + + switch (type) { + case DataType::AUDIO: + config.set_audio_search_path (search_path); + break; + case DataType::MIDI: + config.set_midi_search_path (search_path); + break; + } +} + +boost::shared_ptr +Session::get_speakers() +{ + return _speakers; +} + +list +Session::unknown_processors () const +{ + list p; + + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + list t = (*i)->unknown_processors (); + copy (t.begin(), t.end(), back_inserter (p)); + } + + p.sort (); + p.unique (); + + return p; +} + +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)) { + return; + } + + boost::shared_ptr r = routes.reader (); + framecnt_t max_latency = 0; + + if (playback) { + /* reverse the list so that we work backwards from the last route to run to the first */ + reverse (r->begin(), r->end()); + } + + /* compute actual latency values for the given direction and store them all in per-port + structures. this will also publish the same values (to JACK) so that computation of latency + for routes can consistently use public latency values. + */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + max_latency = max (max_latency, (*i)->set_private_port_latencies (playback)); + } + + /* because we latency compensate playback, our published playback latencies should + be the same for all output ports - all material played back by ardour has + the same latency, whether its caused by plugins or by latency compensation. since + these may differ from the values computed above, reset all playback port latencies + to the same value. + */ + + DEBUG_TRACE (DEBUG::Latency, string_compose ("Set public port latencies to %1\n", max_latency)); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->set_public_port_latencies (max_latency, playback); } + + if (playback) { + + post_playback_latency (); + + } else { + + post_capture_latency (); + } + + DEBUG_TRACE (DEBUG::Latency, "JACK latency callback: DONE\n"); +} + +void +Session::post_playback_latency () +{ + set_worst_playback_latency (); + + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (!(*i)->is_hidden() && ((*i)->active())) { + _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ()); + } + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->set_latency_compensation (_worst_track_latency); + } + } } +void +Session::post_capture_latency () +{ + set_worst_capture_latency (); + + /* reflect any changes in capture latencies into capture offsets + */ + 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_capture_offset (); + } + } +} + +void +Session::set_worst_io_latencies () +{ + set_worst_playback_latency (); + set_worst_capture_latency (); +} + +void +Session::set_worst_playback_latency () +{ + if (_state_of_the_state & (InitialConnecting|Deletion)) { + return; + } + + _worst_output_latency = 0; + + if (!_engine.connected()) { + return; + } + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + _worst_output_latency = max (_worst_output_latency, (*i)->output()->latency()); + } + + DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst output latency: %1\n", _worst_output_latency)); +} + +void +Session::set_worst_capture_latency () +{ + if (_state_of_the_state & (InitialConnecting|Deletion)) { + return; + } + + _worst_input_latency = 0; + + if (!_engine.connected()) { + return; + } + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency()); + } + + DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency)); +} + +void +Session::update_latency_compensation (bool force_whole_graph) +{ + bool some_track_latency_changed = false; + + if (_state_of_the_state & (InitialConnecting|Deletion)) { + return; + } + + DEBUG_TRACE(DEBUG::Latency, "---------------------------- update latency compensation\n\n"); + + _worst_track_latency = 0; + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (!(*i)->is_hidden() && ((*i)->active())) { + framecnt_t tl; + if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency ())) { + some_track_latency_changed = true; + } + _worst_track_latency = max (tl, _worst_track_latency); + } + } + + DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_track_latency, + (some_track_latency_changed ? "yes" : "no"))); + + if (force_whole_graph || some_track_latency_changed) { + /* trigger a full recompute of latency numbers for the graph. + everything else that we need to do will be done in the latency + callback. + */ + _engine.update_total_latencies (); + return; // everything else will be done in the latency callback + } + + DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n") +} +