X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=19c7f5638f3bf9d76b139316b653c5611b0cc3dc;hb=4fc42bca44651b927d336110c23e789691f3bb48;hp=28822ef6f9a830821f868dcb539022aba5cf5e78;hpb=1c3f709bf75d86462b4441470d5108cdc049301b;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 28822ef6f9..19c7f5638f 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -132,11 +132,11 @@ 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) - : _engine (eng) + string mix_template) + : _engine (eng) , _target_transport_speed (0.0) , _requested_return_frame (-1) , _session_dir (new SessionDirectory(fullpath)) @@ -207,7 +207,7 @@ Session::Session (AudioEngine &eng, 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; + _is_new = false; } Session::~Session () @@ -238,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; @@ -249,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; @@ -301,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())); @@ -323,7 +317,6 @@ Session::destroy () playlists.reset (); delete _locations; - delete _speakers; DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); @@ -332,24 +325,6 @@ Session::destroy () #endif } -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()); - } -} - void Session::when_engine_running () { @@ -436,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? } @@ -465,7 +438,7 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); - boost::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]); @@ -494,7 +467,7 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); - boost::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]); @@ -508,7 +481,7 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof(buf), _("in %" PRIu32 "+%" PRIu32), np + 1, np + 2); - boost::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); @@ -524,7 +497,7 @@ Session::when_engine_running () string n = inputs[DataType::MIDI][np]; boost::erase_first (n, X_("alsa_pcm:")); - boost::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); @@ -536,7 +509,7 @@ Session::when_engine_running () string n = outputs[DataType::MIDI][np]; boost::erase_first (n, X_("alsa_pcm:")); - boost::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); @@ -548,9 +521,9 @@ Session::when_engine_running () if (_is_new && !no_auto_connect()) { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock()); + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock()); - /* don't connect the master bus outputs if there is a monitor bus */ + /* don't connect the master bus outputs if there is a monitor bus */ if (_master_out && Config->get_auto_connect_standard_busses() && !_monitor_out) { @@ -622,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); @@ -651,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 @@ -671,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 @@ -679,13 +650,12 @@ Session::hookup_io () */ try { - Auditioner* a = new Auditioner (*this); + boost::shared_ptr a (new Auditioner (*this)); if (a->init()) { - delete a; - throw failed_constructor(); + throw failed_constructor (); } a->use_new_diskstream (); - auditioner.reset (a); + auditioner = a; } catch (failed_constructor& err) { @@ -708,11 +678,10 @@ 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) { boost::shared_ptr r = routes.reader (); @@ -727,10 +696,8 @@ Session::hookup_io () /* relax */ } else { - - (*x)->listen_via (_monitor_out, - (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), - false, false); + + (*x)->listen_via_monitor (); } } } @@ -769,6 +736,7 @@ Session::track_playlist_changed (boost::weak_ptr wp) if ((playlist = track->playlist()) != 0) { 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)); } } @@ -786,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); } } @@ -1020,14 +984,7 @@ Session::enable_record () 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); - } - } + set_track_monitor_input_status (true); } RecordStateChanged (); @@ -1053,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 */ @@ -1077,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); } } } @@ -1135,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; @@ -1255,43 +1198,6 @@ Session::set_block_size (pframes_t nframes) } } -void -Session::set_default_fade (float /*steepness*/, float /*fade_msecs*/) -{ -#if 0 - framecnt_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 = (framecnt_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) { @@ -1326,7 +1232,7 @@ struct RouteSorter { static void trace_terminal (boost::shared_ptr r1, boost::shared_ptr rbase) { - boost::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; @@ -1386,7 +1292,7 @@ Session::resort_routes () { RCUWriter writer (routes); - boost::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 */ } @@ -1458,20 +1364,29 @@ Session::resort_routes_using (boost::shared_ptr r) } -/** 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; @@ -1484,61 +1399,62 @@ 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; - boost::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 */ +/** 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; } - boost::shared_ptr track; + boost::shared_ptr track; try { - MidiTrack* mt = new MidiTrack (*this, track_name, Route::Flag (0), mode); + track.reset (new MidiTrack (*this, track_name, Route::Flag (0), mode)); - if (mt->init ()) { - delete mt; + if (track->init ()) { goto failed; } - mt->use_new_diskstream(); + track->use_new_diskstream(); #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_shared_ptr_mark_interesting (mt, "Track"); + boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); #endif - track = boost::shared_ptr(mt); - { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { @@ -1552,8 +1468,6 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m } } - auto_connect_route (track.get(), existing_inputs, existing_outputs); - track->non_realtime_input_change(); if (route_group) { @@ -1583,23 +1497,32 @@ 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); } return ret; } -/** Caller must hold process lock. - * @param connect_inputs true to connect inputs as well as outputs, false to connect just outputs. +/** @param connect_inputs true to connect inputs as well as outputs, false to connect just outputs. * @param input_start Where to start from when auto-connecting inputs; e.g. if this is 0, auto-connect starting from input 0. * @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 @@ -1608,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) @@ -1630,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; } } @@ -1658,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; @@ -1665,51 +1609,47 @@ Session::auto_connect_route ( } } } - - existing_inputs += route->n_inputs(); - existing_outputs += route->n_outputs(); } -/** Caller must not hold process lock */ +/** 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; } - boost::shared_ptr track; + boost::shared_ptr track; try { - AudioTrack* at = new AudioTrack (*this, track_name, Route::Flag (0), mode); + track.reset (new AudioTrack (*this, track_name, Route::Flag (0), mode)); - if (at->init ()) { - delete at; + if (track->init ()) { goto failed; } - at->use_new_diskstream(); + track->use_new_diskstream(); #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_shared_ptr_mark_interesting (at, "Track"); + boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); #endif - track = boost::shared_ptr(at); - { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); @@ -1728,8 +1668,6 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod << endmsg; goto failed; } - - auto_connect_route (track.get(), existing_inputs, existing_outputs); } if (route_group) { @@ -1762,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; @@ -1774,7 +1712,7 @@ Session::set_remote_control_ids () RemoteModel m = Config->get_remote_model(); bool emit_signal = false; - boost::shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (MixerOrdered == m) { @@ -1795,41 +1733,37 @@ Session::set_remote_control_ids () } } -/** Caller must not hold process lock */ +/** 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 (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; + if (bus->init ()) { goto failure; } #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_shared_ptr_mark_interesting (rt, "Route"); + boost_debug_shared_ptr_mark_interesting (bus.get(), "Route"); #endif - boost::shared_ptr bus (rt); - { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); @@ -1847,8 +1781,6 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r << endmsg; goto failure; } - - auto_connect_route (bus.get(), existing_inputs, existing_outputs, false); } if (route_group) { @@ -1879,7 +1811,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r failure: if (!ret.empty()) { - add_routes (ret, true); + add_routes (ret, true, true); } return ret; @@ -1910,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*/ } @@ -1926,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 { - boost::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; @@ -1969,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); - boost::shared_ptr r = writer.get_copy (); + boost::shared_ptr r = writer.get_copy (); r->insert (r->end(), new_routes.begin(), new_routes.end()); @@ -2027,6 +1964,11 @@ Session::add_routes (RouteList& new_routes, bool save) 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); + } } if (_monitor_out && IO::connecting_legal) { @@ -2037,9 +1979,7 @@ Session::add_routes (RouteList& new_routes, bool save) } 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 (); } } @@ -2128,7 +2068,7 @@ 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 (); @@ -2145,7 +2085,7 @@ Session::remove_route (boost::shared_ptr route) { RCUWriter writer (routes); - boost::shared_ptr rs = writer.get_copy (); + boost::shared_ptr rs = writer.get_copy (); rs->remove (route); @@ -2198,7 +2138,7 @@ Session::remove_route (boost::shared_ptr route) } } - 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 @@ -2244,7 +2184,7 @@ 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 */ @@ -2263,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) @@ -2275,23 +2217,23 @@ 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 @@ -2400,7 +2342,7 @@ Session::update_route_solo_state (boost::shared_ptr r) something_soloed = true; } - if (!(*i)->is_hidden() && (*i)->listening()) { + if (!(*i)->is_hidden() && (*i)->listening_via_monitor()) { if (Config->get_solo_control_is_listen_control()) { listeners++; } else { @@ -2573,6 +2515,14 @@ Session::playlist_ranges_moved (list > const & ran } } +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 */ boost::shared_ptr @@ -2704,12 +2654,18 @@ Session::add_source (boost::shared_ptr source) 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(); @@ -2721,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); } } @@ -2894,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); @@ -2902,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 ""; @@ -2984,7 +2939,6 @@ 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" @@ -2992,8 +2946,7 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha a file format change. */ - if (matching_unsuffixed_filename_exists_in (spath, buf) || - matching_unsuffixed_filename_exists_in (spath_stubs, buf)) { + if (matching_unsuffixed_filename_exists_in (spath, buf)) { existing++; break; } @@ -3017,10 +2970,10 @@ 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, string(), destructive, frame_rate())); @@ -3077,7 +3030,7 @@ 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 */ @@ -3096,7 +3049,7 @@ Session::create_midi_source_for_session (Track* track, string const & n, bool as } 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 ( @@ -3792,45 +3745,18 @@ 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 } 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 } 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 } uint32_t @@ -3929,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 (); } } @@ -4211,10 +4125,10 @@ Session::ensure_search_path_includes (const string& path, DataType type) } } -Speakers& +boost::shared_ptr Session::get_speakers() { - return *_speakers; + return _speakers; } list @@ -4234,10 +4148,179 @@ Session::unknown_processors () const return p; } -#ifdef HAVE_JACK_NEW_LATENCY void Session::update_latency (bool playback) { - DEBUG_TRACE (DEBUG::Latency, "JACK latency callback\n"); + 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); + } + } } -#endif + +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") +} +