X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=4ac6f9ebbc5ed965a3dad9149987db5d5ab6c20e;hb=581376e0ed7d8934b384766ee4500dd6abeb755d;hp=f7dfd2111d72b409dcb922cf8ac76d5013854d36;hpb=51693a3a5835a7c2deb16242d14d73fbb7881382;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index f7dfd2111d..4ac6f9ebbc 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 1999-2004 Paul Davis + Copyright (C) 1999-2010 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,21 +28,18 @@ #include #include -#include -#include - #include #include #include #include "pbd/error.h" -#include #include "pbd/boost_debug.h" #include "pbd/pathscanner.h" #include "pbd/stl_delete.h" #include "pbd/basename.h" #include "pbd/stacktrace.h" #include "pbd/file_utils.h" +#include "pbd/convert.h" #include "ardour/amp.h" #include "ardour/analyser.h" @@ -55,6 +52,7 @@ #include "ardour/audioplaylist.h" #include "ardour/audioregion.h" #include "ardour/auditioner.h" +#include "ardour/buffer_manager.h" #include "ardour/buffer_set.h" #include "ardour/bundle.h" #include "ardour/butler.h" @@ -71,7 +69,9 @@ #include "ardour/midi_playlist.h" #include "ardour/midi_region.h" #include "ardour/midi_track.h" +#include "ardour/midi_ui.h" #include "ardour/named_selection.h" +#include "ardour/process_thread.h" #include "ardour/playlist.h" #include "ardour/plugin_insert.h" #include "ardour/port_insert.h" @@ -86,6 +86,7 @@ #include "ardour/session_directory.h" #include "ardour/session_directory.h" #include "ardour/session_metadata.h" +#include "ardour/session_playlists.h" #include "ardour/slave.h" #include "ardour/smf_source.h" #include "ardour/source_factory.h" @@ -93,6 +94,8 @@ #include "ardour/tempo.h" #include "ardour/utils.h" +#include "midi++/jack.h" + #include "i18n.h" using namespace std; @@ -103,43 +106,41 @@ using boost::weak_ptr; bool Session::_disable_all_loaded_plugins = false; -sigc::signal Session::Dialog; -sigc::signal Session::AskAboutPendingState; -sigc::signal Session::AskAboutSampleRateMismatch; -sigc::signal Session::SendFeedback; +PBD::Signal1 Session::Dialog; +PBD::Signal0 Session::AskAboutPendingState; +PBD::Signal2 Session::AskAboutSampleRateMismatch; +PBD::Signal0 Session::SendFeedback; + +PBD::Signal0 Session::TimecodeOffsetChanged; +PBD::Signal0 Session::StartTimeChanged; +PBD::Signal0 Session::EndTimeChanged; +PBD::Signal0 Session::AutoBindingOn; +PBD::Signal0 Session::AutoBindingOff; +PBD::Signal2 Session::Exported; +PBD::Signal1 > Session::AskAboutPlaylistDeletion; -sigc::signal Session::TimecodeOffsetChanged; -sigc::signal Session::StartTimeChanged; -sigc::signal Session::EndTimeChanged; -sigc::signal Session::AutoBindingOn; -sigc::signal Session::AutoBindingOff; -sigc::signal Session::Exported; +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, + BusProfile* bus_profile, string mix_template) : _engine (eng), _target_transport_speed (0.0), _requested_return_frame (-1), - _scratch_buffers(new BufferSet()), - _silent_buffers(new BufferSet()), - _mix_buffers(new BufferSet()), mmc (0), _mmc_port (default_mmc_port), _mtc_port (default_mtc_port), _midi_port (default_midi_port), _midi_clock_port (default_midi_clock_port), _session_dir (new SessionDirectory(fullpath)), - pending_events (2048), state_tree (0), - _butler (new Butler (this)), + _butler (new Butler (*this)), _post_transport_work (0), _send_timecode_update (false), - midi_thread (pthread_t (0)), - midi_requests (128), // the size of this should match the midi request pool size - diskstreams (new DiskstreamList), routes (new RouteList), _total_free_4k_blocks (0), _bundles (new BundleList), @@ -149,34 +150,32 @@ Session::Session (AudioEngine &eng, click_emphasis_data (0), main_outs (0), _metadata (new SessionMetadata()), - _have_rec_enabled_diskstream (false) + _have_rec_enabled_track (false) { - bool new_session; - + playlists.reset (new SessionPlaylists); + interpolation.add_channel_to (0, 0); if (!eng.connected()) { throw failed_constructor(); } - info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl; - n_physical_outputs = _engine.n_physical_outputs(DataType::AUDIO); n_physical_inputs = _engine.n_physical_inputs(DataType::AUDIO); first_stage_init (fullpath, snapshot_name); - new_session = !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 (new_session) { - if (create (new_session, mix_template, compute_initial_length())) { + if (_is_new) { + if (create (mix_template, compute_initial_length(), bus_profile)) { destroy (); throw failed_constructor (); } - } + } - if (second_stage_init (new_session)) { + if (second_stage_init ()) { destroy (); throw failed_constructor (); } @@ -187,141 +186,14 @@ Session::Session (AudioEngine &eng, _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); - Config->ParameterChanged.connect (bind (mem_fun (*this, &Session::config_changed), false)); - config.ParameterChanged.connect (bind (mem_fun (*this, &Session::config_changed), true)); + Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, false)); + config.ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, true)); if (was_dirty) { DirtyChanged (); /* EMIT SIGNAL */ } -} - -Session::Session (AudioEngine &eng, - string fullpath, - string snapshot_name, - AutoConnectOption input_ac, - AutoConnectOption output_ac, - uint32_t control_out_channels, - uint32_t master_out_channels, - uint32_t requested_physical_in, - uint32_t requested_physical_out, - nframes_t initial_length) - - : _engine (eng), - _target_transport_speed (0.0), - _requested_return_frame (-1), - _scratch_buffers(new BufferSet()), - _silent_buffers(new BufferSet()), - _mix_buffers(new BufferSet()), - mmc (0), - _mmc_port (default_mmc_port), - _mtc_port (default_mtc_port), - _midi_port (default_midi_port), - _midi_clock_port (default_midi_clock_port), - _session_dir ( new SessionDirectory(fullpath)), - pending_events (2048), - state_tree (0), - _butler (new Butler (this)), - _post_transport_work (0), - _send_timecode_update (false), - midi_thread (pthread_t (0)), - midi_requests (16), - diskstreams (new DiskstreamList), - 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_diskstream (false) -{ - bool new_session; - - interpolation.add_channel_to (0, 0); - - if (!eng.connected()) { - throw failed_constructor(); - } - - info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; - - n_physical_outputs = _engine.n_physical_outputs (DataType::AUDIO); - n_physical_inputs = _engine.n_physical_inputs (DataType::AUDIO); - - if (n_physical_inputs) { - n_physical_inputs = max (requested_physical_in, n_physical_inputs); - } - - if (n_physical_outputs) { - n_physical_outputs = max (requested_physical_out, n_physical_outputs); - } - - first_stage_init (fullpath, snapshot_name); - - new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); - - if (new_session) { - if (create (new_session, string(), initial_length)) { - destroy (); - throw failed_constructor (); - } - } - - { - /* set up Master Out and Control Out if necessary */ - - RouteList rl; - int control_id = 1; - - if (master_out_channels) { - ChanCount count(DataType::AUDIO, master_out_channels); - shared_ptr r (new Route (*this, _("master"), Route::MasterOut, DataType::AUDIO)); - r->input()->ensure_io (count, false, this); - r->output()->ensure_io (count, false, this); - r->set_remote_control_id (control_id); - - rl.push_back (r); - } else { - /* prohibit auto-connect to master, because there isn't one */ - output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster); - } - - if (control_out_channels) { - ChanCount count(DataType::AUDIO, control_out_channels); - shared_ptr r (new Route (*this, _("monitor"), Route::ControlOut, DataType::AUDIO)); - r->input()->ensure_io (count, false, this); - r->output()->ensure_io (count, false, this); - r->set_remote_control_id (control_id++); - - rl.push_back (r); - } - - if (!rl.empty()) { - add_routes (rl, false); - } - - } - - if (no_auto_connect()) { - input_ac = AutoConnectOption (0); - output_ac = AutoConnectOption (0); - } - - Config->set_input_auto_connect (input_ac); - Config->set_output_auto_connect (output_ac); - - if (second_stage_init (new_session)) { - destroy (); - throw failed_constructor (); - } - - store_recent_sessions (_name, _path); - - _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); - Config->ParameterChanged.connect (bind (mem_fun (*this, &Session::config_changed), false)); + _is_new = false; } Session::~Session () @@ -344,12 +216,6 @@ Session::destroy () _engine.remove_session (); - GoingAway (); /* EMIT SIGNAL */ - - /* do this */ - - notify_callbacks (); - /* clear history so that no references to objects are held any more */ _history.clear (); @@ -362,8 +228,9 @@ Session::destroy () Stateful::loading_state_version = 0; - _butler->terminate_thread (); - //terminate_midi_thread (); + _butler->drop_references (); + delete _butler; + delete midi_control_ui; if (click_data != default_click) { delete [] click_data; @@ -375,75 +242,23 @@ Session::destroy () clear_clicks (); - delete _scratch_buffers; - delete _silent_buffers; - delete _mix_buffers; - /* clear out any pending dead wood from RCU managed objects */ routes.flush (); - diskstreams.flush (); _bundles.flush (); AudioDiskstream::free_working_buffers(); - Route::SyncOrderKeys.clear(); + /* tell everyone who is still standing that we're about to die */ + drop_references (); - DEBUG_TRACE (DEBUG::Destruction, "delete named selections\n"); - for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ) { - NamedSelectionList::iterator tmp; - - tmp = i; - ++tmp; - - delete *i; - i = tmp; - } - - DEBUG_TRACE (DEBUG::Destruction, "delete used playlists\n"); - for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ) { - PlaylistList::iterator tmp; - - tmp = i; - ++tmp; - - DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for used playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count())); - (*i)->drop_references (); - - - i = tmp; - } - - DEBUG_TRACE (DEBUG::Destruction, "delete unused playlists\n"); - for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) { - PlaylistList::iterator tmp; - - tmp = i; - ++tmp; - - DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for unused playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count())); - (*i)->drop_references (); - - i = tmp; - } + /* tell everyone to drop references and delete objects as we go */ - playlists.clear (); - unused_playlists.clear (); + DEBUG_TRACE (DEBUG::Destruction, "delete named selections\n"); + named_selections.clear (); DEBUG_TRACE (DEBUG::Destruction, "delete regions\n"); - for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { - RegionList::iterator tmp; - - tmp = i; - ++tmp; - - DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for region %1 (%2); pre-ref = %2\n", i->second->name(), i->second.get(), i->second.use_count())); - i->second->drop_references (); - DEBUG_TRACE(DEBUG::Destruction, string_compose ("region post ref = %1\n", i->second.use_count())); - i = tmp; - } - - regions.clear (); + RegionFactory::delete_all_regions (); DEBUG_TRACE (DEBUG::Destruction, "delete routes\n"); @@ -451,50 +266,35 @@ Session::destroy () auditioner.reset (); _master_out.reset (); - _control_out.reset (); + _monitor_out.reset (); { RCUWriter writer (routes); boost::shared_ptr r = writer.get_copy (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for route %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count())); (*i)->drop_references (); } + r->clear (); /* writer goes out of scope and updates master */ } routes.flush (); - DEBUG_TRACE (DEBUG::Destruction, "delete diskstreams\n"); - { - RCUWriter dwriter (diskstreams); - boost::shared_ptr dsl = dwriter.get_copy(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for diskstream %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count())); - (*i)->drop_references (); - } - dsl->clear (); - } - diskstreams.flush (); + boost::shared_ptr r = routes.reader (); DEBUG_TRACE (DEBUG::Destruction, "delete sources\n"); - for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) { - SourceMap::iterator tmp; - - tmp = i; - ++tmp; - + 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())); i->second->drop_references (); - - i = tmp; } sources.clear (); - DEBUG_TRACE (DEBUG::Destruction, "delete route groups\n"); for (list::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) { + delete *i; } @@ -502,6 +302,9 @@ Session::destroy () delete mmc; + /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */ + playlists.reset (); + boost_debug_list_ptrs (); DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); @@ -537,11 +340,15 @@ Session::when_engine_running () BootMessage (_("Using configuration")); - Config->map_parameters (bind (mem_fun (*this, &Session::config_changed), false)); + boost::function ff (boost::bind (&Session::config_changed, this, _1, false)); + boost::function ft (boost::bind (&Session::config_changed, this, _1, true)); + + Config->map_parameters (ff); + config.map_parameters (ft); /* every time we reconnect, recompute worst case output latencies */ - _engine.Running.connect (mem_fun (*this, &Session::set_worst_io_latencies)); + _engine.Running.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies, this)); if (synced_to_jack()) { _engine.transport_stop (); @@ -685,9 +492,11 @@ Session::when_engine_running () hookup_io (); - if (!no_auto_connect()) { + if (_is_new && !no_auto_connect()) { + + /* don't connect the master bus outputs if there is a monitor bus */ - if (_master_out && Config->get_auto_connect_standard_busses()) { + if (_master_out && Config->get_auto_connect_standard_busses() && !_monitor_out) { /* if requested auto-connect the outputs to the first N physical ports. */ @@ -708,7 +517,7 @@ Session::when_engine_running () } } - if (_control_out) { + if (_monitor_out) { /* AUDIO ONLY as of june 29th 2009, because listen semantics for anything else are undefined, at best. @@ -718,16 +527,16 @@ Session::when_engine_running () under some conditions) */ - uint32_t limit = _control_out->n_inputs().n_audio(); + uint32_t limit = _monitor_out->n_inputs().n_audio(); if (_master_out) { for (uint32_t n = 0; n < limit; ++n) { - AudioPort* p = _control_out->input()->ports().nth_audio_port (n); + AudioPort* p = _monitor_out->input()->ports().nth_audio_port (n); AudioPort* o = _master_out->output()->ports().nth_audio_port (n); if (o) { string connect_to = o->name(); - if (_control_out->input()->connect (p, connect_to, this)) { + if (_monitor_out->input()->connect (p, connect_to, this)) { error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to) << endmsg; break; @@ -736,18 +545,17 @@ Session::when_engine_running () } } - /* if control out is not connected, - connect control out to physical outs, but use ones after the master if possible + /* if control out is not connected, connect control out to physical outs */ - if (!_control_out->output()->connected_to (boost::shared_ptr())) { + if (!_monitor_out->output()->connected ()) { if (!Config->get_monitor_bus_preferred_bundle().empty()) { boost::shared_ptr b = bundle_by_name (Config->get_monitor_bus_preferred_bundle()); if (b) { - _control_out->output()->connect_ports_to_bundle (b, this); + _monitor_out->output()->connect_ports_to_bundle (b, this); } else { warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"), Config->get_monitor_bus_preferred_bundle()) @@ -755,25 +563,24 @@ Session::when_engine_running () } } else { - - /* XXX this logic is wrong for mixed port types */ - - uint32_t shift = _master_out->n_outputs().n_audio(); - uint32_t mod = _engine.n_physical_outputs (DataType::AUDIO); - limit = _control_out->n_outputs().n_audio(); - - cerr << "Connecting " << limit << " control out ports, shift is " << shift << " mod is " << mod << endl; - - for (uint32_t n = 0; n < limit; ++n) { - - Port* p = _control_out->output()->nth (n); - string connect_to = _engine.get_nth_physical_output (DataType (p->type()), (n+shift) % mod); - - if (!connect_to.empty()) { - if (_control_out->output()->connect (p, connect_to, this)) { - error << string_compose (_("cannot connect control output %1 to %2"), n, connect_to) - << endmsg; - break; + + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + uint32_t mod = _engine.n_physical_outputs (*t); + uint32_t limit = _monitor_out->n_outputs().get(*t); + + for (uint32_t n = 0; n < limit; ++n) { + + Port* p = _monitor_out->output()->ports().port(*t, n); + string connect_to = _engine.get_nth_physical_output (*t, (n % mod)); + + if (!connect_to.empty()) { + if (_monitor_out->output()->connect (p, connect_to, this)) { + error << string_compose ( + _("cannot connect control output %1 to %2"), + n, connect_to) + << endmsg; + break; + } } } } @@ -807,11 +614,16 @@ Session::hookup_io () /* we delay creating the auditioner till now because it makes its own connections to ports. - the engine has to be running for this to work. */ try { - auditioner.reset (new Auditioner (*this)); + Auditioner* a = new Auditioner (*this); + if (a->init()) { + delete a; + throw failed_constructor(); + } + a->use_new_diskstream (); + auditioner.reset (a); } catch (failed_constructor& err) { @@ -828,28 +640,38 @@ Session::hookup_io () /* Tell all IO objects to connect themselves together */ IO::enable_connecting (); + MIDI::JACK_MidiPort::MakeConnections (); /* Now reset all panners */ Delivery::reset_panners (); - /* Connect tracks to listen/solo etc. busses XXX generalize this beyond control_out */ - - if (_control_out) { + /* 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 (); - - for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { - - if ((*x)->is_control() || (*x)->is_master()) { - continue; - } - - (*x)->listen_via (_control_out, - (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), - false, false); - } - } + for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { + + if ((*x)->is_monitor()) { + + /* relax */ + + } else if ((*x)->is_master()) { + + /* relax */ + + } else { + + (*x)->listen_via (_monitor_out, + (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), + false, false); + } + } + } /* Anyone who cares about input state, wake up and do something */ @@ -875,9 +697,9 @@ Session::hookup_io () void Session::playlist_length_changed () { - /* we can't just increase end_location->end() if pl->get_maximum_extent() + /* we can't just increase session_range_location->end() if pl->get_maximum_extent() if larger. if the playlist used to be the longest playlist, - and its now shorter, we have to decrease end_location->end(). hence, + and its now shorter, we have to decrease session_range_location->end(). hence, we have to iterate over all diskstreams and check the playlists currently in use. */ @@ -885,17 +707,17 @@ Session::playlist_length_changed () } void -Session::diskstream_playlist_changed (boost::weak_ptr wp) +Session::track_playlist_changed (boost::weak_ptr wp) { - boost::shared_ptr dstream = wp.lock (); - if (!dstream) { + boost::shared_ptr track = wp.lock (); + if (!track) { return; } boost::shared_ptr playlist; - if ((playlist = dstream->playlist()) != 0) { - playlist->LengthChanged.connect (mem_fun (this, &Session::playlist_length_changed)); + if ((playlist = track->playlist()) != 0) { + playlist->LengthChanged.connect_same_thread (*this, boost::bind (&Session::playlist_length_changed, this)); } /* see comment in playlist_length_changed () */ @@ -921,21 +743,23 @@ Session::reset_input_monitor_state () { if (transport_rolling()) { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->record_enabled ()) { + 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; - (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input()); + tr->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input()); } } + } else { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->record_enabled ()) { + + 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; - (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring); + tr->monitor_input (Config->get_monitoring_model() == HardwareMonitoring); } } } @@ -944,7 +768,7 @@ Session::reset_input_monitor_state () void Session::auto_punch_start_changed (Location* location) { - replace_event (Event::PunchIn, location->start()); + replace_event (SessionEvent::PunchIn, location->start()); if (get_record_enabled() && config.get_punch_in()) { /* capture start has been changed, so save new pending state */ @@ -957,7 +781,7 @@ Session::auto_punch_end_changed (Location* location) { nframes_t when_to_stop = location->end(); // when_to_stop += _worst_output_latency + _worst_input_latency; - replace_event (Event::PunchOut, when_to_stop); + replace_event (SessionEvent::PunchOut, when_to_stop); } void @@ -965,15 +789,15 @@ Session::auto_punch_changed (Location* location) { nframes_t when_to_stop = location->end(); - replace_event (Event::PunchIn, location->start()); + replace_event (SessionEvent::PunchIn, location->start()); //when_to_stop += _worst_output_latency + _worst_input_latency; - replace_event (Event::PunchOut, when_to_stop); + replace_event (SessionEvent::PunchOut, when_to_stop); } void Session::auto_loop_changed (Location* location) { - replace_event (Event::AutoLoop, location->end(), location->start()); + replace_event (SessionEvent::AutoLoop, location->end(), location->start()); if (transport_rolling() && play_loop) { @@ -982,7 +806,7 @@ Session::auto_loop_changed (Location* location) if (_transport_frame < location->start() || _transport_frame > location->end()) { // relocate to beginning of loop - clear_events (Event::LocateRoll); + clear_events (SessionEvent::LocateRoll); request_locate (location->start(), true); @@ -994,8 +818,8 @@ Session::auto_loop_changed (Location* location) loop_changing = true; if (location->end() > last_loopend) { - clear_events (Event::LocateRoll); - Event *ev = new Event (Event::LocateRoll, Event::Add, last_loopend, last_loopend, 0, true); + clear_events (SessionEvent::LocateRoll); + SessionEvent *ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, last_loopend, last_loopend, 0, true); queue_event (ev); } @@ -1011,12 +835,10 @@ Session::set_auto_punch_location (Location* location) Location* existing; if ((existing = _locations.auto_punch_location()) != 0 && existing != location) { - auto_punch_start_changed_connection.disconnect(); - auto_punch_end_changed_connection.disconnect(); - auto_punch_changed_connection.disconnect(); + punch_connections.drop_connections(); existing->set_auto_punch (false, this); - remove_event (existing->start(), Event::PunchIn); - clear_events (Event::PunchOut); + remove_event (existing->start(), SessionEvent::PunchIn); + clear_events (SessionEvent::PunchOut); auto_punch_location_changed (0); } @@ -1031,17 +853,14 @@ Session::set_auto_punch_location (Location* location) return; } - auto_punch_start_changed_connection.disconnect(); - auto_punch_end_changed_connection.disconnect(); - auto_punch_changed_connection.disconnect(); + punch_connections.drop_connections (); - auto_punch_start_changed_connection = location->start_changed.connect (mem_fun (this, &Session::auto_punch_start_changed)); - auto_punch_end_changed_connection = location->end_changed.connect (mem_fun (this, &Session::auto_punch_end_changed)); - auto_punch_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_punch_changed)); + location->start_changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_start_changed, this, _1)); + location->end_changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_end_changed, this, _1)); + location->changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_changed, this, _1)); location->set_auto_punch (true, this); - auto_punch_changed (location); auto_punch_location_changed (location); @@ -1053,11 +872,9 @@ Session::set_auto_loop_location (Location* location) Location* existing; if ((existing = _locations.auto_loop_location()) != 0 && existing != location) { - auto_loop_start_changed_connection.disconnect(); - auto_loop_end_changed_connection.disconnect(); - auto_loop_changed_connection.disconnect(); + loop_connections.drop_connections (); existing->set_auto_loop (false, this); - remove_event (existing->end(), Event::AutoLoop); + remove_event (existing->end(), SessionEvent::AutoLoop); auto_loop_location_changed (0); } @@ -1074,16 +891,11 @@ Session::set_auto_loop_location (Location* location) last_loopend = location->end(); - auto_loop_start_changed_connection.disconnect(); - auto_loop_end_changed_connection.disconnect(); - auto_loop_changed_connection.disconnect(); + loop_connections.drop_connections (); - auto_loop_start_changed_connection = location->start_changed.connect ( - mem_fun (this, &Session::auto_loop_changed)); - auto_loop_end_changed_connection = location->end_changed.connect ( - mem_fun (this, &Session::auto_loop_changed)); - auto_loop_changed_connection = location->changed.connect ( - mem_fun (this, &Session::auto_loop_changed)); + location->start_changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1)); + location->end_changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1)); + location->changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1)); location->set_auto_loop (true, this); @@ -1129,11 +941,8 @@ Session::handle_locations_changed (Locations::LocationList& locations) set_loop = true; } - if (location->is_start()) { - start_location = location; - } - if (location->is_end()) { - end_location = location; + if (location->is_session_range()) { + _session_range_location = location; } } @@ -1157,10 +966,12 @@ Session::enable_record () deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location); if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->record_enabled ()) { - (*i)->monitor_input (true); + + 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); } } } @@ -1187,15 +998,17 @@ Session::disable_record (bool rt_context, bool force) // FIXME: timestamp correct? [DR] // FIXME FIXME FIXME: rt_context? this must be called in the process thread. // does this /need/ to be sent in all cases? - if (rt_context) + if (rt_context) { deliver_mmc (MIDI::MachineControl::cmdRecordExit, _transport_frame); + } if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->record_enabled ()) { - (*i)->monitor_input (false); + 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); } } } @@ -1211,17 +1024,15 @@ Session::disable_record (bool rt_context, bool force) void Session::step_back_from_record () { - /* XXX really atomic compare+swap here */ - if (g_atomic_int_get (&_record_status) == Recording) { - g_atomic_int_set (&_record_status, Enabled); + if (g_atomic_int_compare_and_exchange (&_record_status, Recording, Enabled)) { if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->record_enabled ()) { + 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; - (*i)->monitor_input (false); + tr->monitor_input (false); } } } @@ -1293,17 +1104,20 @@ Session::audible_frame () const /* MOVING */ - /* check to see if we have passed the first guaranteed + /* Check to see if we have passed the first guaranteed audible frame past our last start position. if not, return that last start point because in terms of audible frames, we have not moved yet. + + `Start position' in this context means the time we last + either started or changed transport direction. */ if (_transport_speed > 0.0f) { if (!play_loop || !have_looped) { - if (tf < _last_roll_location + offset) { - return _last_roll_location; + if (tf < _last_roll_or_reversal_location + offset) { + return _last_roll_or_reversal_location; } } @@ -1315,8 +1129,8 @@ Session::audible_frame () const /* XXX wot? no backward looping? */ - if (tf > _last_roll_location - offset) { - return _last_roll_location; + if (tf > _last_roll_or_reversal_location - offset) { + return _last_roll_or_reversal_location; } else { /* backwards */ ret += offset; @@ -1365,12 +1179,7 @@ Session::set_block_size (nframes_t nframes) { current_block_size = nframes; - ensure_buffers(_scratch_buffers->available()); - - delete [] _gain_automation_buffer; - _gain_automation_buffer = new gain_t[nframes]; - - allocate_pan_automation_buffers (nframes, _npan_buffers, true); + ensure_buffers (); boost::shared_ptr r = routes.reader (); @@ -1378,9 +1187,12 @@ Session::set_block_size (nframes_t nframes) (*i)->set_block_size (nframes); } - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->set_block_size (nframes); + 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_block_size (nframes); + } } set_worst_io_latencies (); @@ -1562,66 +1374,82 @@ Session::resort_routes_using (shared_ptr r) } -list > -Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many) +/** Find the route name starting with \a base with the lowest \a 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. + */ +bool +Session::find_route_name (const char* base, uint32_t& id, char* name, size_t name_len) { - char track_name[32]; - uint32_t track_id = 0; - uint32_t n = 0; - string port; - RouteList new_routes; - list > ret; - //uint32_t control_id; + do { + snprintf (name, name_len, "%s %" PRIu32, base, id); - // FIXME: need physical I/O and autoconnect stuff for MIDI + if (route_by_name (name) == 0) { + return true; + } - /* count existing midi tracks */ + ++id; - { - shared_ptr r = routes.reader (); + } while (id < (UINT_MAX-1)); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i) != 0) { - if (!(*i)->is_hidden()) { - n++; - //channels_used += (*i)->n_inputs().n_midi(); - } - } + return false; +} + +void +Session::count_existing_route_channels (ChanCount& in, ChanCount& out) +{ + in = ChanCount::ZERO; + out = ChanCount::ZERO; + 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(); } } +} - vector physinputs; - vector physoutputs; +list > +Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many) +{ + 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; - _engine.get_physical_outputs (DataType::MIDI, physoutputs); - _engine.get_physical_inputs (DataType::MIDI, physinputs); + count_existing_route_channels (existing_inputs, existing_outputs); - // control_id = ntracks() + nbusses(); + control_id = ntracks() + nbusses(); while (how_many) { + if (!find_route_name ("Midi", ++track_id, track_name, sizeof(track_name))) { + error << "cannot find name for new midi track" << endmsg; + goto failed; + } - /* check for duplicate route names, since we might have pre-existing - routes with this name (e.g. create Audio1, Audio2, delete Audio1, - save, close,restart,add new route - first named route is now - Audio2) - */ - - - do { - ++track_id; - - snprintf (track_name, sizeof(track_name), "Midi %" PRIu32, track_id); + shared_ptr track; - if (route_by_name (track_name) == 0) { - break; - } + try { + MidiTrack* mt = new MidiTrack (*this, track_name, Route::Flag (0), mode); - } while (track_id < (UINT_MAX-1)); + if (mt->init ()) { + delete mt; + goto failed; + } - shared_ptr track; + mt->use_new_diskstream(); - try { - track = boost::shared_ptr((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; @@ -1634,48 +1462,15 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m goto failed; } - /* - if (nphysical_in) { - for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) { - - port = ""; - - if (Config->get_input_auto_connect() & AutoConnectPhysical) { - port = physinputs[(channels_used+x)%nphysical_in]; - } - - if (port.length() && track->connect_input (track->input (x), port, this)) { - break; - } - } - } - - for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) { + auto_connect_route (track, existing_inputs, existing_outputs); - port = ""; - - if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) { - port = physoutputs[(channels_used+x)%nphysical_out]; - } else if (Config->get_output_auto_connect() & AutoConnectMaster) { - if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs().n_midi())->name(); - } - } - - if (port.length() && track->connect_output (track->output (x), port, this)) { - break; - } + track->non_realtime_input_change(); + if (route_group) { + route_group->add (track); } - channels_used += track->n_inputs ().n_midi(); - - */ - - track->midi_diskstream()->non_realtime_input_change(); - track->set_route_group (route_group, 0); - - track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); - //track->set_remote_control_id (control_id); + track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); + track->set_remote_control_id (control_id); new_routes.push_back (track); ret.push_back (track); @@ -1683,36 +1478,12 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m catch (failed_constructor &err) { error << _("Session: could not create new midi track.") << endmsg; - - if (track) { - /* we need to get rid of this, since the track failed to be created */ - /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */ - - { - RCUWriter writer (diskstreams); - boost::shared_ptr ds = writer.get_copy(); - ds->remove (track->midi_diskstream()); - } - } - goto failed; } catch (AudioEngine::PortRegistrationFailure& pfe) { - error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg; - - if (track) { - /* we need to get rid of this, since the track failed to be created */ - /* XXX arguably, MidiTrack::MidiTrack should not do the Session::add_diskstream() */ - - { - RCUWriter writer (diskstreams); - boost::shared_ptr ds = writer.get_copy(); - ds->remove (track->midi_diskstream()); - } - } - + error << string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with ports if you need this many tracks."), PROGRAM_NAME) << endmsg; goto failed; } @@ -1728,126 +1499,143 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m return ret; } -list > -Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many) +void +Session::auto_connect_route (boost::shared_ptr route, + ChanCount& existing_inputs, ChanCount& existing_outputs) { - char track_name[32]; - uint32_t track_id = 0; - uint32_t n = 0; - uint32_t channels_used = 0; - string port; - RouteList new_routes; - list > ret; - uint32_t control_id; - - /* count existing audio tracks */ - - { - shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i) != 0) { - if (!(*i)->is_hidden()) { - n++; - channels_used += (*i)->n_inputs().n_audio(); - } - } - } - } - - vector physinputs; - vector physoutputs; + /* 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 + first audio output of the route will have the same physical + port number). Otherwise just use the lowest input or output + offset possible. + */ + const bool in_out_physical = + (Config->get_input_auto_connect() & AutoConnectPhysical) + && (Config->get_output_auto_connect() & AutoConnectPhysical); - _engine.get_physical_outputs (DataType::AUDIO, physoutputs); - _engine.get_physical_inputs (DataType::AUDIO, physinputs); + const ChanCount in_offset = in_out_physical + ? ChanCount::max(existing_inputs, existing_outputs) + : existing_inputs; - control_id = ntracks() + nbusses() + 1; + const ChanCount out_offset = in_out_physical + ? ChanCount::max(existing_inputs, existing_outputs) + : existing_outputs; - while (how_many) { + static string empty_string; + string& port = empty_string; - /* check for duplicate route names, since we might have pre-existing - routes with this name (e.g. create Audio1, Audio2, delete Audio1, - save, close,restart,add new route - first named route is now - Audio2) - */ + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + vector physinputs; + vector physoutputs; + _engine.get_physical_outputs (*t, physoutputs); + _engine.get_physical_inputs (*t, physinputs); - do { - ++track_id; + if (!physinputs.empty()) { + uint32_t nphysical_in = physinputs.size(); + for (uint32_t i = 0; i < route->n_inputs().get(*t) && i < nphysical_in; ++i) { + port = empty_string; - snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, track_id); + if (Config->get_input_auto_connect() & AutoConnectPhysical) { + port = physinputs[(in_offset.get(*t) + i) % nphysical_in]; + } - if (route_by_name (track_name) == 0) { - break; + if (!port.empty() && route->input()->connect ( + route->input()->ports().port(*t, i), port, this)) { + break; + } } + } - } while (track_id < (UINT_MAX-1)); + if (!physoutputs.empty()) { + uint32_t nphysical_out = physoutputs.size(); + for (uint32_t i = 0; i < route->n_outputs().get(*t); ++i) { + port = empty_string; - shared_ptr track; + if (Config->get_output_auto_connect() & AutoConnectPhysical) { + port = physoutputs[(out_offset.get(*t) + i) % nphysical_out]; + } else if (Config->get_output_auto_connect() & AutoConnectMaster) { + if (_master_out && _master_out->n_inputs().get(*t) > 0) { + port = _master_out->input()->ports().port(*t, + i % _master_out->input()->n_ports().get(*t))->name(); + } + } + + if (!port.empty() && route->output()->connect ( + route->output()->ports().port(*t, i), port, this)) { + break; + } + } + } + } + + existing_inputs += route->n_inputs(); + existing_outputs += route->n_outputs(); +} + +list< boost::shared_ptr > +Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many) +{ + 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; + + while (how_many) { + if (!find_route_name ("Audio", ++track_id, track_name, sizeof(track_name))) { + error << "cannot find name for new audio track" << endmsg; + goto failed; + } + + shared_ptr track; try { AudioTrack* at = new AudioTrack (*this, track_name, Route::Flag (0), mode); - // boost_debug_shared_ptr_mark_interesting (at, typeid (at).name()); + + if (at->init ()) { + delete at; + goto failed; + } + + at->use_new_diskstream(); + + 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"), + 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"), + error << string_compose ( + _("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) << endmsg; goto failed; } - if (!physinputs.empty()) { - uint32_t nphysical_in = physinputs.size(); - - for (uint32_t x = 0; x < track->n_inputs().n_audio() && x < nphysical_in; ++x) { - - port = ""; + auto_connect_route (track, existing_inputs, existing_outputs); - if (Config->get_input_auto_connect() & AutoConnectPhysical) { - port = physinputs[(channels_used+x)%nphysical_in]; - } - - if (port.length() && track->input()->connect (track->input()->nth(x), port, this)) { - break; - } - } + if (route_group) { + route_group->add (track); } - if (!physoutputs.empty()) { - uint32_t nphysical_out = physoutputs.size(); + track->non_realtime_input_change(); - for (uint32_t x = 0; x < track->n_outputs().n_audio(); ++x) { - port = ""; - - if (Config->get_output_auto_connect() & AutoConnectPhysical) { - port = physoutputs[(channels_used+x)%nphysical_out]; - } else if (Config->get_output_auto_connect() & AutoConnectMaster) { - if (_master_out && _master_out->n_inputs().n_audio() > 0) { - port = _master_out->input()->nth (x % _master_out->input()->n_ports().n_audio())->name(); - } - } - - if (port.length() && track->output()->connect (track->output()->nth(x), port, this)) { - break; - } - } - } - - channels_used += track->n_inputs ().n_audio(); - - track->set_route_group (route_group, 0); - - track->audio_diskstream()->non_realtime_input_change(); - - track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); + track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); track->set_remote_control_id (control_id); ++control_id; @@ -1857,36 +1645,12 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod catch (failed_constructor &err) { error << _("Session: could not create new audio track.") << endmsg; - - if (track) { - /* we need to get rid of this, since the track failed to be created */ - /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */ - - { - RCUWriter writer (diskstreams); - boost::shared_ptr ds = writer.get_copy(); - ds->remove (track->audio_diskstream()); - } - } - goto failed; } catch (AudioEngine::PortRegistrationFailure& pfe) { error << pfe.what() << endmsg; - - if (track) { - /* we need to get rid of this, since the track failed to be created */ - /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */ - - { - RCUWriter writer (diskstreams); - boost::shared_ptr ds = writer.get_copy(); - ds->remove (track->audio_diskstream()); - } - } - goto failed; } @@ -1905,20 +1669,27 @@ void Session::set_remote_control_ids () { RemoteModel m = Config->get_remote_model(); + bool emit_signal = false; shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ( MixerOrdered == m) { + if (MixerOrdered == m) { long order = (*i)->order_key(N_("signal")); - (*i)->set_remote_control_id( order+1 ); - } else if ( EditorOrdered == m) { + (*i)->set_remote_control_id (order+1, false); + emit_signal = true; + } else if (EditorOrdered == m) { long order = (*i)->order_key(N_("editor")); - (*i)->set_remote_control_id( order+1 ); - } else if ( UserOrdered == m) { + (*i)->set_remote_control_id (order+1, false); + emit_signal = true; + } else if (UserOrdered == m) { //do nothing ... only changes to remote id's are initiated by user } } + + if (emit_signal) { + Route::RemoteControlIDChange(); + } } @@ -1926,56 +1697,33 @@ RouteList Session::new_audio_route (bool aux, int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many) { char bus_name[32]; - uint32_t bus_id = 1; - uint32_t n = 0; - uint32_t channels_used = 0; + uint32_t bus_id = 0; + ChanCount existing_inputs; + ChanCount existing_outputs; string port; RouteList ret; uint32_t control_id; - /* count existing audio busses */ - - { - shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i) == 0) { - /* its a bus ? */ - if (!(*i)->is_hidden() && (*i)->name() != _("master")) { - bus_id++; - n++; - channels_used += (*i)->n_inputs().n_audio(); - } - } - } - } - - vector physinputs; - vector physoutputs; - - _engine.get_physical_outputs (DataType::AUDIO, physoutputs); - _engine.get_physical_inputs (DataType::AUDIO, physinputs); - - n_physical_audio_outputs = physoutputs.size(); - n_physical_audio_inputs = physinputs.size(); + count_existing_route_channels (existing_inputs, existing_outputs); control_id = ntracks() + nbusses() + 1; while (how_many) { + if (!find_route_name ("Bus", ++bus_id, bus_name, sizeof(bus_name))) { + error << "cannot find name for new audio bus" << endmsg; + goto failure; + } - do { - snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, bus_id); - - bus_id++; - - if (route_by_name (bus_name) == 0) { - break; - } + try { + Route* rt = new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO); - } while (bus_id < (UINT_MAX-1)); + if (rt->init ()) { + delete rt; + goto failure; + } - try { - shared_ptr bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); + 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"), @@ -1992,37 +1740,11 @@ Session::new_audio_route (bool aux, int input_channels, int output_channels, Rou goto failure; } - for (uint32_t x = 0; n_physical_audio_inputs && x < bus->input()->n_ports().n_audio(); ++x) { - port = ""; + auto_connect_route (bus, existing_inputs, existing_outputs); - if (Config->get_input_auto_connect() & AutoConnectPhysical) { - port = physinputs[((n+x)%n_physical_audio_inputs)]; - } - - if (port.length() && bus->input()->connect (bus->input()->nth (x), port, this)) { - break; - } + if (route_group) { + route_group->add (bus); } - - for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs().n_audio(); ++x) { - port = ""; - - if (Config->get_output_auto_connect() & AutoConnectPhysical) { - port = physoutputs[((n+x)%n_physical_outputs)]; - } else if (Config->get_output_auto_connect() & AutoConnectMaster) { - if (_master_out) { - port = _master_out->input()->nth (x%_master_out->input()->n_ports().n_audio())->name(); - } - } - - if (port.length() && bus->output()->connect (bus->output()->nth(x), port, this)) { - break; - } - } - - channels_used += bus->n_inputs ().n_audio(); - - bus->set_route_group (route_group, 0); bus->set_remote_control_id (control_id); ++control_id; @@ -2064,7 +1786,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template RouteList ret; uint32_t control_id; XMLTree tree; - uint32_t number = 1; + uint32_t number = 0; if (!tree.read (template_path.c_str())) { return ret; @@ -2081,19 +1803,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 */ - - do { - snprintf (name, sizeof (name), "%s %" PRIu32, node_name.c_str(), number); - - number++; - - if (route_by_name (name) == 0) { - break; - } - - } while (number < UINT_MAX); - - if (number == UINT_MAX) { + if (!find_route_name (node_name.c_str(), ++number, name, sizeof(name))) { fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; /*NOTREACHED*/ } @@ -2161,7 +1871,7 @@ Session::add_routes (RouteList& new_routes, bool save) we will resort when done. */ - if (!_control_out && IO::connecting_legal) { + if (!_monitor_out && IO::connecting_legal) { resort_routes_using (r); } } @@ -2169,32 +1879,43 @@ Session::add_routes (RouteList& new_routes, bool save) for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { boost::weak_ptr wpr (*x); + boost::shared_ptr r (*x); + + r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _1, wpr)); + r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, wpr)); + r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this, _1)); + r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2)); + r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1)); + r->route_group_changed.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this)); - (*x)->listen_changed.connect (sigc::bind (mem_fun (*this, &Session::route_listen_changed), wpr)); - (*x)->solo_changed.connect (sigc::bind (mem_fun (*this, &Session::route_solo_changed), wpr)); - (*x)->mute_changed.connect (mem_fun (*this, &Session::route_mute_changed)); - (*x)->output()->changed.connect (mem_fun (*this, &Session::set_worst_io_latencies_x)); - (*x)->processors_changed.connect (bind (mem_fun (*this, &Session::update_latency_compensation), false, false)); - (*x)->route_group_changed.connect (hide (mem_fun (*this, &Session::route_group_changed))); + if (r->is_master()) { + _master_out = r; + } - if ((*x)->is_master()) { - _master_out = (*x); + if (r->is_monitor()) { + _monitor_out = r; } - if ((*x)->is_control()) { - _control_out = (*x); + boost::shared_ptr tr = boost::dynamic_pointer_cast (r); + if (tr) { + tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr (tr))); + track_playlist_changed (boost::weak_ptr (tr)); + tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_track, this)); } } - if (_control_out && IO::connecting_legal) { + if (_monitor_out && IO::connecting_legal) { for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { - if ((*x)->is_control() || (*x)->is_master()) { - continue; - } - (*x)->listen_via (_control_out, - (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), - false, false); + if ((*x)->is_monitor()) { + /* relax */ + } else if ((*x)->is_master()) { + /* relax */ + } else { + (*x)->listen_via (_monitor_out, + (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), + false, false); + } } resort_routes (); @@ -2207,6 +1928,7 @@ Session::add_routes (RouteList& new_routes, bool save) } RouteAdded (new_routes); /* EMIT SIGNAL */ + Route::RemoteControlIDChange (); /* EMIT SIGNAL */ } void @@ -2280,7 +2002,7 @@ Session::globally_add_internal_sends (boost::shared_ptr dest, Placement p void Session::add_internal_sends (boost::shared_ptr dest, Placement p, boost::shared_ptr senders) { - if (dest->is_control() || dest->is_master()) { + if (dest->is_monitor() || dest->is_master()) { return; } @@ -2290,7 +2012,7 @@ Session::add_internal_sends (boost::shared_ptr dest, Placement p, boost:: for (RouteList::iterator i = senders->begin(); i != senders->end(); ++i) { - if ((*i)->is_control() || (*i)->is_master() || (*i) == dest) { + if ((*i)->is_monitor() || (*i)->is_master() || (*i) == dest) { continue; } @@ -2300,31 +2022,6 @@ Session::add_internal_sends (boost::shared_ptr dest, Placement p, boost:: graph_reordered (); } -void -Session::add_diskstream (boost::shared_ptr dstream) -{ - /* need to do this in case we're rolling at the time, to prevent false underruns */ - dstream->do_refill_with_alloc (); - - dstream->set_block_size (current_block_size); - - { - RCUWriter writer (diskstreams); - boost::shared_ptr ds = writer.get_copy(); - ds->push_back (dstream); - /* writer goes out of scope, copies ds back to main */ - } - - dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), boost::weak_ptr (dstream))); - /* this will connect to future changes, and check the current length */ - diskstream_playlist_changed (boost::weak_ptr (dstream)); - - dstream->RecordEnableChanged.connect (mem_fun (*this, &Session::update_have_rec_enabled_diskstream)); - - dstream->prepare (); - -} - void Session::remove_route (shared_ptr route) { @@ -2343,15 +2040,15 @@ Session::remove_route (shared_ptr route) _master_out = shared_ptr (); } - if (route == _control_out) { + if (route == _monitor_out) { /* cancel control outs for all routes */ for (RouteList::iterator r = rs->begin(); r != rs->end(); ++r) { - (*r)->drop_listen (_control_out); + (*r)->drop_listen (_monitor_out); } - _control_out.reset (); + _monitor_out.reset (); } update_route_solo_state (); @@ -2359,22 +2056,6 @@ Session::remove_route (shared_ptr route) /* writer goes out of scope, forces route list update */ } - boost::shared_ptr t; - boost::shared_ptr ds; - - if ((t = boost::dynamic_pointer_cast(route)) != 0) { - ds = t->diskstream(); - } - - if (ds) { - - { - RCUWriter dsl (diskstreams); - boost::shared_ptr d = dsl.get_copy(); - d->remove (ds); - } - } - find_current_end (); // We need to disconnect the routes inputs and outputs @@ -2382,6 +2063,18 @@ Session::remove_route (shared_ptr route) route->input()->disconnect (0); route->output()->disconnect (0); + /* if the route had internal sends sending to it, remove them */ + if (route->internal_return()) { + + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + boost::shared_ptr s = (*i)->internal_send_for (route); + if (s) { + (*i)->remove_processor (s); + } + } + } + update_latency_compensation (false, false); set_dirty(); @@ -2397,6 +2090,8 @@ Session::remove_route (shared_ptr route) sync_order_keys (N_("session")); + Route::RemoteControlIDChange(); /* EMIT SIGNAL */ + /* save the new state of the world */ if (save_state (_current_snapshot_name)) { @@ -2451,7 +2146,7 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr wpr) delta = -1; } - /* now mod the solo level of all other routes except master & control outs + /* now mod the solo level of all other routes except master/control outs/auditioner so that they will be silent if appropriate. */ @@ -2460,8 +2155,7 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr wpr) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { bool via_sends_only; - - if ((*i) == route || !(*i)->solo_isolated() || !(*i)->is_master() || !(*i)->is_control() || (*i)->is_hidden()) { + if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { continue; } else if ((*i)->feeds (route, &via_sends_only)) { if (!via_sends_only) { @@ -2470,18 +2164,6 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr wpr) } } - /* make sure master is never muted by solo */ - - if (_master_out && route != _master_out && _master_out->soloed_by_others() == 0 && !_master_out->soloed()) { - _master_out->mod_solo_by_others (1); - } - - /* ditto for control outs make sure master is never muted by solo */ - - if (_control_out && route != _control_out && _control_out && _control_out->soloed_by_others() == 0) { - _control_out->mod_solo_by_others (1); - } - solo_update_disabled = false; update_route_solo_state (r); SoloChanged (); /* EMIT SIGNAL */ @@ -2491,25 +2173,38 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr wpr) void Session::update_route_solo_state (boost::shared_ptr r) { - /* now figure out if anything that matters is soloed */ + /* now figure out if anything that matters is soloed (or is "listening")*/ bool something_soloed = false; + uint32_t listeners = 0; if (!r) { r = routes.reader(); } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_master() && !(*i)->is_control() && !(*i)->is_hidden() && (*i)->self_soloed()) { + if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_hidden() && (*i)->self_soloed()) { something_soloed = true; break; } - } - if (something_soloed != _non_soloed_outs_muted) { - _non_soloed_outs_muted = something_soloed; - SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */ + if (!(*i)->is_hidden() && (*i)->listening()) { + if (Config->get_solo_control_is_listen_control()) { + listeners++; + } else { + (*i)->set_listen (false, this); + } + } } + + if (something_soloed != _non_soloed_outs_muted) { + _non_soloed_outs_muted = something_soloed; + SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */ + } + + if (listeners) { + _listen_cnt = listeners; + } } boost::shared_ptr @@ -2526,6 +2221,24 @@ Session::get_routes_with_internal_returns() const return rl; } +bool +Session::io_name_is_legal (const std::string& name) +{ + shared_ptr r = routes.reader (); + + 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; + } + } + + return true; +} + shared_ptr Session::route_by_name (string name) { @@ -2577,8 +2290,8 @@ Session::find_current_end () nframes_t max = get_maximum_extent (); - if (max > end_location->end()) { - end_location->set_end (max); + if (max > _session_range_location->end()) { + _session_range_location->set_end (max); set_dirty(); DurationChanged(); /* EMIT SIGNAL */ } @@ -2589,13 +2302,16 @@ Session::get_maximum_extent () const { nframes_t max = 0; nframes_t me; - - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->destructive()) //ignore tape tracks when getting max extents + + 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 max extents continue; - boost::shared_ptr pl = (*i)->playlist(); + } + + boost::shared_ptr pl = tr->playlist(); if ((me = pl->get_maximum_extent()) > max) { max = me; } @@ -2604,309 +2320,13 @@ Session::get_maximum_extent () const return max; } -boost::shared_ptr -Session::diskstream_by_name (string name) -{ - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->name() == name) { - return *i; - } - } - - return boost::shared_ptr((Diskstream*) 0); -} - -boost::shared_ptr -Session::diskstream_by_id (const PBD::ID& id) -{ - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->id() == id) { - return *i; - } - } - - return boost::shared_ptr((Diskstream*) 0); -} - /* Region management */ -string -Session::new_region_name (string old) -{ - string::size_type last_period; - uint32_t number; - string::size_type len = old.length() + 64; - char buf[len]; - - if ((last_period = old.find_last_of ('.')) == string::npos) { - - /* no period present - add one explicitly */ - - old += '.'; - last_period = old.length() - 1; - number = 0; - - } else { - - number = atoi (old.substr (last_period+1).c_str()); - - } - - while (number < (UINT_MAX-1)) { - - RegionList::const_iterator i; - string sbuf; - - number++; - - snprintf (buf, len, "%s%" PRIu32, old.substr (0, last_period + 1).c_str(), number); - sbuf = buf; - - for (i = regions.begin(); i != regions.end(); ++i) { - if (i->second->name() == sbuf) { - break; - } - } - - if (i == regions.end()) { - break; - } - } - - if (number != (UINT_MAX-1)) { - return buf; - } - - error << string_compose (_("cannot create new name for region \"%1\""), old) << endmsg; - return old; -} - -int -Session::region_name (string& result, string base, bool newlevel) -{ - char buf[16]; - string subbase; - - if (base.find("/") != string::npos) { - base = base.substr(base.find_last_of("/") + 1); - } - - if (base == "") { - - Glib::Mutex::Lock lm (region_lock); - - snprintf (buf, sizeof (buf), "%d", (int)regions.size() + 1); - result = "region."; - result += buf; - - } else { - - if (newlevel) { - subbase = base; - } else { - string::size_type pos; - - pos = base.find_last_of ('.'); - - /* pos may be npos, but then we just use entire base */ - - subbase = base.substr (0, pos); - - } - - { - Glib::Mutex::Lock lm (region_lock); - - map::iterator x; - - result = subbase; - - if ((x = region_name_map.find (subbase)) == region_name_map.end()) { - result += ".1"; - region_name_map[subbase] = 1; - } else { - x->second++; - snprintf (buf, sizeof (buf), ".%d", x->second); - - result += buf; - } - } - } - - return 0; -} - -void -Session::add_region (boost::shared_ptr region) -{ - vector > v; - v.push_back (region); - add_regions (v); -} - -void -Session::add_regions (vector >& new_regions) -{ - bool added = false; - - { - Glib::Mutex::Lock lm (region_lock); - - for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { - - boost::shared_ptr region = *ii; - - if (region == 0) { - - error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; - - } else { - - RegionList::iterator x; - - for (x = regions.begin(); x != regions.end(); ++x) { - - if (region->region_list_equivalent (x->second)) { - break; - } - } - - if (x == regions.end()) { - - pair entry; - - entry.first = region->id(); - entry.second = region; - - pair x = regions.insert (entry); - - if (!x.second) { - return; - } - - added = true; - } - } - } - } - - /* mark dirty because something has changed even if we didn't - add the region to the region list. - */ - - set_dirty (); - - if (added) { - - vector > v; - boost::shared_ptr first_r; - - for (vector >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { - - boost::shared_ptr region = *ii; - - if (region == 0) { - - error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; - - } else { - v.push_back (region); - - if (!first_r) { - first_r = region; - } - } - - region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr(region))); - region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr(region))); - - update_region_name_map (region); - } - - if (!v.empty()) { - RegionsAdded (v); /* EMIT SIGNAL */ - } - } -} - -void -Session::update_region_name_map (boost::shared_ptr region) -{ - string::size_type last_period = region->name().find_last_of ('.'); - - if (last_period != string::npos && last_period < region->name().length() - 1) { - - string base = region->name().substr (0, last_period); - string number = region->name().substr (last_period+1); - map::iterator x; - - /* note that if there is no number, we get zero from atoi, - which is just fine - */ - - region_name_map[base] = atoi (number); - } -} - -void -Session::region_changed (Change what_changed, boost::weak_ptr weak_region) -{ - boost::shared_ptr region (weak_region.lock ()); - - if (!region) { - return; - } - - if (what_changed & Region::HiddenChanged) { - /* relay hidden changes */ - RegionHiddenChange (region); - } - - if (what_changed & NameChanged) { - update_region_name_map (region); - } -} - -void -Session::remove_region (boost::weak_ptr weak_region) -{ - RegionList::iterator i; - boost::shared_ptr region (weak_region.lock ()); - - if (!region) { - return; - } - - bool removed = false; - - { - Glib::Mutex::Lock lm (region_lock); - - if ((i = regions.find (region->id())) != regions.end()) { - regions.erase (i); - removed = true; - } - } - - /* mark dirty because something has changed even if we didn't - remove the region from the region list. - */ - - set_dirty(); - - if (removed) { - RegionRemoved(region); /* EMIT SIGNAL */ - } -} - boost::shared_ptr -Session::find_whole_file_parent (boost::shared_ptr child) +Session::find_whole_file_parent (boost::shared_ptr child) const { - RegionList::iterator i; + const RegionFactory::RegionMap& regions (RegionFactory::regions()); + RegionFactory::RegionMap::const_iterator i; boost::shared_ptr region; Glib::Mutex::Lock lm (region_lock); @@ -2926,13 +2346,6 @@ Session::find_whole_file_parent (boost::shared_ptr child) return boost::shared_ptr (); } -void -Session::find_equivalent_playlist_regions (boost::shared_ptr region, vector >& result) -{ - for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) - (*i)->get_region_list_equivalent_regions (region, result); -} - int Session::destroy_region (boost::shared_ptr region) { @@ -2952,10 +2365,10 @@ Session::destroy_region (boost::shared_ptr region) for (vector >::iterator i = srcs.begin(); i != srcs.end(); ++i) { - (*i)->mark_for_remove (); - (*i)->drop_references (); - - cerr << "source was not used by any playlist\n"; + (*i)->mark_for_remove (); + (*i)->drop_references (); + + cerr << "source was not used by any playlist\n"; } return 0; @@ -2975,10 +2388,14 @@ Session::remove_last_capture () { list > r; - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - list >& l = (*i)->last_capture_regions(); + 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) { + continue; + } + + list >& l = tr->last_capture_regions(); if (!l.empty()) { r.insert (r.end(), l.begin(), l.end()); @@ -2993,13 +2410,6 @@ Session::remove_last_capture () return 0; } -int -Session::remove_region_from_region_list (boost::shared_ptr r) -{ - remove_region (r); - return 0; -} - /* Source Management */ void @@ -3017,7 +2427,7 @@ Session::add_source (boost::shared_ptr source) } if (result.second) { - source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr (source))); + source->DropReferences.connect_same_thread (*this, boost::bind (&Session::remove_source, this, boost::weak_ptr (source))); set_dirty(); } @@ -3058,23 +2468,6 @@ Session::remove_source (boost::weak_ptr src) } } -/** Return the number of playlists (not regions) that contain @a src */ -uint32_t -Session::source_use_count (boost::shared_ptr src) const -{ - uint32_t count = 0; - for (PlaylistList::const_iterator p = playlists.begin(); p != playlists.end(); ++p) { - for (Playlist::RegionList::const_iterator r = (*p)->region_list().begin(); - r != (*p)->region_list().end(); ++r) { - if ((*r)->uses_source(src)) { - ++count; - break; - } - } - } - return count; -} - boost::shared_ptr Session::source_by_id (const PBD::ID& id) { @@ -3221,7 +2614,7 @@ Session::change_source_path_by_name (string path, string oldname, string newname return path; } -/** Return the full path (in some session directory) for a new embedded source. +/** Return the full path (in some session directory) for a new within-session source. * \a name must be a session-unique name that does not contain slashes * (e.g. as returned by new_*_source_name) */ @@ -3342,16 +2735,15 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha return Glib::path_get_basename(buf); } -/** Create a new embedded audio source */ +/** Create a new within-session audio source */ boost::shared_ptr -Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive) +Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive) { - const size_t n_chans = ds.n_channels().n_audio(); - const string name = new_audio_source_name (ds.name(), n_chans, chan, destructive); + const string name = new_audio_source_name (n, n_chans, chan, destructive); const string path = new_source_path_from_name(DataType::AUDIO, name); return boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, path, true, destructive, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate())); } /** Return a unique name based on \a base for a new internal MIDI source */ @@ -3403,54 +2795,18 @@ Session::new_midi_source_name (const string& base) } -/** Create a new embedded MIDI source */ +/** Create a new within-session MIDI source */ boost::shared_ptr -Session::create_midi_source_for_session (MidiDiskstream& ds) +Session::create_midi_source_for_session (string const & n) { - const string name = new_midi_source_name (ds.name()); + const string name = new_midi_source_name (n); const string path = new_source_path_from_name (DataType::MIDI, name); return boost::dynamic_pointer_cast ( SourceFactory::createWritable ( - DataType::MIDI, *this, path, true, false, frame_rate())); -} - - -/* Playlist management */ - -boost::shared_ptr -Session::playlist_by_name (string name) -{ - Glib::Mutex::Lock lm (playlist_lock); - for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { - if ((*i)->name() == name) { - return* i; - } - } - for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { - if ((*i)->name() == name) { - return* i; - } - } - - return boost::shared_ptr(); + DataType::MIDI, *this, path, false, frame_rate())); } -void -Session::unassigned_playlists (std::list > & list) -{ - Glib::Mutex::Lock lm (playlist_lock); - for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { - if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) { - list.push_back (*i); - } - } - for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { - if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) { - list.push_back (*i); - } - } -} void Session::add_playlist (boost::shared_ptr playlist, bool unused) @@ -3459,75 +2815,13 @@ Session::add_playlist (boost::shared_ptr playlist, bool unused) return; } - { - Glib::Mutex::Lock lm (playlist_lock); - if (find (playlists.begin(), playlists.end(), playlist) == playlists.end()) { - playlists.insert (playlists.begin(), playlist); - playlist->InUse.connect (sigc::bind (mem_fun (*this, &Session::track_playlist), boost::weak_ptr(playlist))); - playlist->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_playlist), boost::weak_ptr(playlist))); - } - } + playlists->add (playlist); if (unused) { playlist->release(); } set_dirty(); - - PlaylistAdded (playlist); /* EMIT SIGNAL */ -} - -void -Session::get_playlists (vector >& s) -{ - { - Glib::Mutex::Lock lm (playlist_lock); - for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { - s.push_back (*i); - } - for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { - s.push_back (*i); - } - } -} - -void -Session::track_playlist (bool inuse, boost::weak_ptr wpl) -{ - boost::shared_ptr pl(wpl.lock()); - - if (!pl) { - return; - } - - PlaylistList::iterator x; - - if (pl->hidden()) { - /* its not supposed to be visible */ - return; - } - - { - Glib::Mutex::Lock lm (playlist_lock); - - if (!inuse) { - - unused_playlists.insert (pl); - - if ((x = playlists.find (pl)) != playlists.end()) { - playlists.erase (x); - } - - - } else { - - playlists.insert (pl); - - if ((x = unused_playlists.find (pl)) != unused_playlists.end()) { - unused_playlists.erase (x); - } - } - } } void @@ -3543,26 +2837,9 @@ Session::remove_playlist (boost::weak_ptr weak_playlist) return; } - { - Glib::Mutex::Lock lm (playlist_lock); - - PlaylistList::iterator i; - - i = find (playlists.begin(), playlists.end(), playlist); - if (i != playlists.end()) { - playlists.erase (i); - } - - i = find (unused_playlists.begin(), unused_playlists.end(), playlist); - if (i != unused_playlists.end()) { - unused_playlists.erase (i); - } - - } + playlists->remove (playlist); set_dirty(); - - PlaylistRemoved (playlist); /* EMIT SIGNAL */ } void @@ -3576,7 +2853,7 @@ Session::set_audition (boost::shared_ptr r) void Session::audition_playlist () { - Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0); + SessionEvent* ev = new SessionEvent (SessionEvent::Audition, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); ev->region.reset (); queue_event (ev); } @@ -3596,7 +2873,7 @@ Session::non_realtime_set_audition () void Session::audition_region (boost::shared_ptr r) { - Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0); + SessionEvent* ev = new SessionEvent (SessionEvent::Audition, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); ev->region = r; queue_event (ev); } @@ -3604,7 +2881,7 @@ Session::audition_region (boost::shared_ptr r) void Session::cancel_audition () { - if (auditioner->active()) { + if (auditioner->auditioning()) { auditioner->cancel_audition (); AuditionActive (false); /* EMIT SIGNAL */ } @@ -3613,6 +2890,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; + } return a->order_key(N_("signal")) < b->order_key(N_("signal")); } @@ -3628,8 +2911,8 @@ Session::remove_empty_sounds () TapeFileMatcher tape_file_matcher; remove_if (audio_filenames.begin(), audio_filenames.end(), - sigc::mem_fun (tape_file_matcher, &TapeFileMatcher::matches)); - + boost::bind (&TapeFileMatcher::matches, &tape_file_matcher, _1)); + for (vector::iterator i = audio_filenames.begin(); i != audio_filenames.end(); ++i) { sys::path audio_file_path (_session_dir->sound_path()); @@ -3657,69 +2940,12 @@ Session::is_auditioning () const { /* can be called before we have an auditioner object */ if (auditioner) { - return auditioner->active(); + return auditioner->auditioning(); } else { return false; } } -void -Session::set_all_solo (bool yn) -{ - shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { - (*i)->set_solo (yn, this); - } - } - - set_dirty(); -} - -void -Session::set_all_listen (bool yn) -{ - shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { - (*i)->set_listen (yn, this); - } - } - - set_dirty(); -} - -void -Session::set_all_mute (bool yn) -{ - shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { - (*i)->set_mute (yn, this); - } - } - - set_dirty(); -} - -uint32_t -Session::n_diskstreams () const -{ - uint32_t n = 0; - - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (!(*i)->hidden()) { - n++; - } - } - return n; -} - void Session::graph_reordered () { @@ -3743,64 +2969,13 @@ Session::graph_reordered () reflect any changes in latencies within the graph. */ - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->set_capture_offset (); - } -} - -void -Session::record_disenable_all () -{ - record_enable_change_all (false); -} - -void -Session::record_enable_all () -{ - record_enable_change_all (true); -} - -void -Session::record_enable_change_all (bool yn) -{ - shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr t; - - if ((t = boost::dynamic_pointer_cast(*i)) != 0) { - t->set_record_enable (yn, this); + 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 (); } } - - /* since we don't keep rec-enable state, don't mark session dirty */ -} - -void -Session::add_processor (Processor* processor) -{ - processor->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_processor), processor)); - set_dirty(); -} - -void -Session::remove_processor (Processor* processor) -{ - Send* send; - Return* retrn; - PortInsert* port_insert; - - if ((port_insert = dynamic_cast (processor)) != 0) { - insert_bitset[port_insert->bit_slot()] = false; - } else if ((send = dynamic_cast (processor)) != 0) { - send_bitset[send->bit_slot()] = false; - } else if ((retrn = dynamic_cast (processor)) != 0) { - return_bitset[retrn->bit_slot()] = false; - } - - set_dirty(); } nframes_t @@ -3890,17 +3065,11 @@ Session::bundle_by_name (string name) const } void -Session::tempo_map_changed (Change) +Session::tempo_map_changed (const PropertyChange&) { clear_clicks (); - for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { - (*i)->update_after_tempo_map_change (); - } - - for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { - (*i)->update_after_tempo_map_change (); - } + playlists->update_after_tempo_map_change (); set_dirty (); } @@ -3911,18 +3080,7 @@ Session::tempo_map_changed (Change) void Session::ensure_buffers (ChanCount howmany) { - if (current_block_size == 0) { - return; // too early? (is this ok?) - } - - for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - size_t count = std::max(_scratch_buffers->available().get(*t), howmany.get(*t)); - _scratch_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t)); - _mix_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t)); - _silent_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t)); - } - - allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false); + BufferManager::ensure_buffers (howmany); } void @@ -4029,39 +3187,60 @@ Session::mark_insert_id (uint32_t id) insert_bitset[id] = true; } +void +Session::unmark_send_id (uint32_t id) +{ + if (id < send_bitset.size()) { + send_bitset[id] = false; + } +} + +void +Session::unmark_return_id (uint32_t id) +{ + if (id < return_bitset.size()) { + return_bitset[id] = false; + } +} + +void +Session::unmark_insert_id (uint32_t id) +{ + if (id < insert_bitset.size()) { + insert_bitset[id] = false; + } +} + + /* Named Selection management */ -NamedSelection * +boost::shared_ptr Session::named_selection_by_name (string name) { Glib::Mutex::Lock lm (named_selection_lock); for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ++i) { if ((*i)->name == name) { - return* i; + return *i; } } - return 0; + return boost::shared_ptr(); } void -Session::add_named_selection (NamedSelection* named_selection) +Session::add_named_selection (boost::shared_ptr named_selection) { { Glib::Mutex::Lock lm (named_selection_lock); named_selections.insert (named_selections.begin(), named_selection); } - for (list >::iterator i = named_selection->playlists.begin(); i != named_selection->playlists.end(); ++i) { - add_playlist (*i); - } - set_dirty(); NamedSelectionAdded (); /* EMIT SIGNAL */ } void -Session::remove_named_selection (NamedSelection* named_selection) +Session::remove_named_selection (boost::shared_ptr named_selection) { bool removed = false; @@ -4071,7 +3250,6 @@ Session::remove_named_selection (NamedSelection* named_selection) NamedSelectionList::iterator i = find (named_selections.begin(), named_selections.end(), named_selection); if (i != named_selections.end()) { - delete (*i); named_selections.erase (i); set_dirty(); removed = true; @@ -4086,10 +3264,12 @@ Session::remove_named_selection (NamedSelection* named_selection) void Session::reset_native_file_format () { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->reset_write_sources (false); + 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->reset_write_sources (false); + } } } @@ -4121,40 +3301,8 @@ Session::route_name_internal (string n) const return false; } -uint32_t -Session::n_playlists () const -{ - Glib::Mutex::Lock lm (playlist_lock); - return playlists.size(); -} - -void -Session::allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, bool force) -{ - if (!force && howmany <= _npan_buffers) { - return; - } - - if (_pan_automation_buffer) { - - for (uint32_t i = 0; i < _npan_buffers; ++i) { - delete [] _pan_automation_buffer[i]; - } - - delete [] _pan_automation_buffer; - } - - _pan_automation_buffer = new pan_t*[howmany]; - - for (uint32_t i = 0; i < howmany; ++i) { - _pan_automation_buffer[i] = new pan_t[nframes]; - } - - _npan_buffers = howmany; -} - int -Session::freeze (InterThreadInfo& itt) +Session::freeze_all (InterThreadInfo& itt) { shared_ptr r = routes.reader (); @@ -4166,7 +3314,7 @@ Session::freeze (InterThreadInfo& itt) /* XXX this is wrong because itt.progress will keep returning to zero at the start of every track. */ - t->freeze (itt); + t->freeze_me (itt); } } @@ -4183,7 +3331,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, boost::shared_ptr fsource; uint32_t x; char buf[PATH_MAX+1]; - ChanCount nchans(track.audio_diskstream()->n_channels()); + ChanCount nchans(track.n_channels()); nframes_t position; nframes_t this_chunk; nframes_t to_do; @@ -4198,8 +3346,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, return result; } - // any bigger than this seems to cause stack overflows in called functions - const nframes_t chunk_size = (128 * 1024)/4; + const nframes_t chunk_size = (256 * 1024)/4; // block all process callback handling @@ -4207,7 +3354,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, /* call tree *MUST* hold route_lock */ - if ((playlist = track.diskstream()->playlist()) == 0) { + if ((playlist = track.playlist()) == 0) { goto out; } @@ -4233,7 +3380,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, try { fsource = boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, buf, true, false, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); } catch (failed_constructor& err) { @@ -4303,9 +3450,14 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, /* construct a region to represent the bounced material */ - result = RegionFactory::create (srcs, 0, - srcs.front()->length(srcs.front()->timeline_position()), - region_name_from_path (srcs.front()->name(), true)); + PropertyList plist; + + plist.add (Properties::start, 0); + plist.add (Properties::length, srcs.front()->length(srcs.front()->timeline_position())); + plist.add (Properties::name, region_name_from_path (srcs.front()->name(), true)); + + result = RegionFactory::create (srcs, plist); + } out: @@ -4334,9 +3486,23 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, return result; } +gain_t* +Session::gain_automation_buffer() const +{ + return ProcessThread::gain_automation_buffer (); +} + +pan_t** +Session::pan_automation_buffer() const +{ + 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); @@ -4347,11 +3513,14 @@ Session::get_silent_buffers (ChanCount count) } 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); @@ -4360,14 +3529,18 @@ Session::get_scratch_buffers (ChanCount count) } 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 @@ -4415,6 +3588,10 @@ Session::compute_initial_length () void Session::sync_order_keys (std::string const & base) { + if (deletion_in_progress()) { + return; + } + if (!Config->get_sync_all_route_ordering()) { /* leave order keys as they are */ return; @@ -4427,31 +3604,40 @@ Session::sync_order_keys (std::string const & base) } Route::SyncOrderKeys (base); // EMIT SIGNAL -} + /* this might not do anything */ + + set_remote_control_ids (); +} -/** @return true if there is at least one record-enabled diskstream, otherwise false */ +/** @return true if there is at least one record-enabled track, otherwise false */ bool -Session::have_rec_enabled_diskstream () const +Session::have_rec_enabled_track () const { - return g_atomic_int_get (&_have_rec_enabled_diskstream) == 1; + return g_atomic_int_get (&_have_rec_enabled_track) == 1; } -/** Update the state of our rec-enabled diskstreams flag */ +/** Update the state of our rec-enabled tracks flag */ void -Session::update_have_rec_enabled_diskstream () +Session::update_have_rec_enabled_track () { - boost::shared_ptr dsl = diskstreams.reader (); - DiskstreamList::iterator i = dsl->begin (); - while (i != dsl->end () && (*i)->record_enabled () == false) { + boost::shared_ptr rl = routes.reader (); + RouteList::iterator i = rl->begin(); + while (i != rl->end ()) { + + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr && tr->record_enabled ()) { + break; + } + ++i; } - int const old = g_atomic_int_get (&_have_rec_enabled_diskstream); + int const old = g_atomic_int_get (&_have_rec_enabled_track); - g_atomic_int_set (&_have_rec_enabled_diskstream, i != dsl->end () ? 1 : 0); + g_atomic_int_set (&_have_rec_enabled_track, i != rl->end () ? 1 : 0); - if (g_atomic_int_get (&_have_rec_enabled_diskstream) != old) { + if (g_atomic_int_get (&_have_rec_enabled_track) != old) { RecordStateChanged (); /* EMIT SIGNAL */ } } @@ -4474,7 +3660,7 @@ Session::listen_position_changed () boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->put_control_outs_at (p); + (*i)->put_monitor_send_at (p); } } @@ -4483,11 +3669,11 @@ Session::solo_control_mode_changed () { /* cancel all solo or all listen when solo control mode changes */ - if (Config->get_solo_control_is_listen_control()) { - set_all_solo (false); - } else { - set_all_listen (false); - } + if (soloing()) { + set_solo (get_routes(), false); + } else if (listening()) { + set_listen (get_routes(), false); + } } void @@ -4513,3 +3699,28 @@ Session::get_available_sync_options () const return ret; } + +boost::shared_ptr +Session::get_routes_with_regions_at (nframes64_t const p) const +{ + shared_ptr r = routes.reader (); + shared_ptr rl (new RouteList); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (!tr) { + continue; + } + + boost::shared_ptr pl = tr->playlist (); + if (!pl) { + continue; + } + + if (pl->has_region_at (p)) { + rl->push_back (*i); + } + } + + return rl; +}