X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=31693daff39d715ae5384e123f1d7cfcf7f6b3ce;hb=9e0d03020ff47773f7d1c0414de1c74e6c9e0dac;hp=23c6ce89c1c577cd7f3344af366ef36dd0e7a3f0;hpb=566ce557368e84ce550f480cb5cf03aae1aa0cfa;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 23c6ce89c1..31693daff3 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -28,24 +28,25 @@ #include #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 "ardour/amp.h" #include "ardour/analyser.h" #include "ardour/audio_buffer.h" #include "ardour/audio_diskstream.h" +#include "ardour/audio_port.h" #include "ardour/audio_track.h" #include "ardour/audioengine.h" #include "ardour/audiofilesource.h" @@ -54,22 +55,27 @@ #include "ardour/auditioner.h" #include "ardour/buffer_set.h" #include "ardour/bundle.h" +#include "ardour/butler.h" #include "ardour/click.h" #include "ardour/configuration.h" #include "ardour/crossfade.h" #include "ardour/cycle_timer.h" #include "ardour/data_type.h" +#include "ardour/debug.h" #include "ardour/filename_extensions.h" +#include "ardour/internal_send.h" #include "ardour/io_processor.h" #include "ardour/midi_diskstream.h" #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/playlist.h" #include "ardour/plugin_insert.h" #include "ardour/port_insert.h" #include "ardour/processor.h" +#include "ardour/rc_configuration.h" #include "ardour/recent_sessions.h" #include "ardour/region_factory.h" #include "ardour/return.h" @@ -79,6 +85,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" @@ -86,26 +93,33 @@ #include "ardour/tempo.h" #include "ardour/utils.h" +#include "midi++/jack.h" + #include "i18n.h" using namespace std; using namespace ARDOUR; using namespace PBD; using boost::shared_ptr; +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::SMPTEOffsetChanged; -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, @@ -124,14 +138,10 @@ Session::Session (AudioEngine &eng, _midi_port (default_midi_port), _midi_clock_port (default_midi_clock_port), _session_dir (new SessionDirectory(fullpath)), - pending_events (2048), state_tree (0), - butler_mixdown_buffer (0), - butler_gain_buffer (0), - post_transport_work((PostTransportWork)0), - _send_smpte_update (false), - midi_thread (pthread_t (0)), - midi_requests (128), // the size of this should match the midi request pool size + _butler (new Butler (*this)), + _post_transport_work (0), + _send_timecode_update (false), diskstreams (new DiskstreamList), routes (new RouteList), _total_free_4k_blocks (0), @@ -145,13 +155,17 @@ Session::Session (AudioEngine &eng, _have_rec_enabled_diskstream (false) { + playlists.reset (new SessionPlaylists); + bool new_session; + interpolation.add_channel_to (0, 0); + if (!eng.connected()) { throw failed_constructor(); } - cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl; + 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); @@ -178,8 +192,8 @@ 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 */ @@ -209,14 +223,10 @@ Session::Session (AudioEngine &eng, _midi_port (default_midi_port), _midi_clock_port (default_midi_clock_port), _session_dir ( new SessionDirectory(fullpath)), - pending_events (2048), state_tree (0), - butler_mixdown_buffer (0), - butler_gain_buffer (0), - post_transport_work((PostTransportWork)0), - _send_smpte_update (false), - midi_thread (pthread_t (0)), - midi_requests (16), + _butler (new Butler (*this)), + _post_transport_work (0), + _send_timecode_update (false), diskstreams (new DiskstreamList), routes (new RouteList), _total_free_4k_blocks (0), @@ -229,13 +239,17 @@ Session::Session (AudioEngine &eng, _metadata (new SessionMetadata()), _have_rec_enabled_diskstream (false) { + playlists.reset (new SessionPlaylists); + bool new_session; + interpolation.add_channel_to (0, 0); + if (!eng.connected()) { throw failed_constructor(); } - cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl; + 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); @@ -265,19 +279,11 @@ Session::Session (AudioEngine &eng, RouteList rl; int control_id = 1; - 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 (master_out_channels) { ChanCount count(DataType::AUDIO, master_out_channels); - shared_ptr r (new Route (*this, _("master"), Route::MasterOut, DataType::AUDIO)); + Route* rt = new Route (*this, _("master"), Route::MasterOut, DataType::AUDIO); + boost_debug_shared_ptr_mark_interesting (rt, "Route"); + boost::shared_ptr r (rt); r->input()->ensure_io (count, false, this); r->output()->ensure_io (count, false, this); r->set_remote_control_id (control_id); @@ -288,6 +294,18 @@ Session::Session (AudioEngine &eng, output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster); } + if (control_out_channels) { + ChanCount count(DataType::AUDIO, control_out_channels); + Route* rt = new Route (*this, _("monitor"), Route::ControlOut, DataType::AUDIO); + boost_debug_shared_ptr_mark_interesting (rt, "Route"); + shared_ptr r (rt); + 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); } @@ -311,7 +329,7 @@ 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_same_thread (*this, boost::bind (&Session::config_changed, this, _1, false)); } Session::~Session () @@ -322,6 +340,8 @@ Session::~Session () void Session::destroy () { + vector debug_pointers; + /* if we got to here, leaving pending capture state around is a mistake. */ @@ -332,12 +352,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 (); @@ -346,8 +360,12 @@ Session::destroy () delete state_tree; - terminate_butler_thread (); - //terminate_midi_thread (); + /* reset dynamic state version back to default */ + + Stateful::loading_state_version = 0; + + delete _butler; + delete midi_control_ui; if (click_data != default_click) { delete [] click_data; @@ -363,146 +381,90 @@ Session::destroy () delete _silent_buffers; delete _mix_buffers; - AudioDiskstream::free_working_buffers(); - - Route::SyncOrderKeys.clear(); - -#undef TRACK_DESTRUCTION -#ifdef TRACK_DESTRUCTION - cerr << "delete named selections\n"; -#endif /* TRACK_DESTRUCTION */ - for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ) { - NamedSelectionList::iterator tmp; - - tmp = i; - ++tmp; - - delete *i; - i = tmp; - } - -#ifdef TRACK_DESTRUCTION - cerr << "delete playlists\n"; -#endif /* TRACK_DESTRUCTION */ - for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ) { - PlaylistList::iterator tmp; - - tmp = i; - ++tmp; - - (*i)->drop_references (); - - i = tmp; - } - - for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) { - PlaylistList::iterator tmp; - - tmp = i; - ++tmp; + /* clear out any pending dead wood from RCU managed objects */ - (*i)->drop_references (); - - i = tmp; - } - - playlists.clear (); - unused_playlists.clear (); + routes.flush (); + diskstreams.flush (); + _bundles.flush (); + + AudioDiskstream::free_working_buffers(); -#ifdef TRACK_DESTRUCTION - cerr << "delete regions\n"; -#endif /* TRACK_DESTRUCTION */ + /* tell everyone who is still standing that we're about to die */ + drop_references (); - for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { - RegionList::iterator tmp; + /* tell everyone to drop references and delete objects as we go */ - tmp = i; - ++tmp; + 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(); ++i) { + DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for region %1 ; pre-ref = %2\n", i->second->name(), i->second.use_count())); i->second->drop_references (); - - i = tmp; } - regions.clear (); -#ifdef TRACK_DESTRUCTION - cerr << "delete routes\n"; -#endif /* TRACK_DESTRUCTION */ + DEBUG_TRACE (DEBUG::Destruction, "delete routes\n"); + + /* reset these three references to special routes before we do the usual route delete thing */ + + auditioner.reset (); + _master_out.reset (); + _control_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 (); -#ifdef TRACK_DESTRUCTION - cerr << "delete diskstreams\n"; -#endif /* TRACK_DESTRUCTION */ - { - RCUWriter dwriter (diskstreams); - boost::shared_ptr dsl = dwriter.get_copy(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->drop_references (); - } - dsl->clear (); - } - diskstreams.flush (); - -#ifdef TRACK_DESTRUCTION - cerr << "delete audio sources\n"; -#endif /* TRACK_DESTRUCTION */ - for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) { - SourceMap::iterator tmp; - - tmp = i; - ++tmp; + boost::shared_ptr r = routes.reader (); - i->second->drop_references (); + 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 (); + } - i = tmp; + dsl->clear (); } - sources.clear (); - -#ifdef TRACK_DESTRUCTION - cerr << "delete mix groups\n"; -#endif /* TRACK_DESTRUCTION */ - for (list::iterator i = mix_groups.begin(); i != mix_groups.end(); ) { - list::iterator tmp; - - tmp = i; - ++tmp; + diskstreams.flush (); - delete *i; - - i = tmp; + 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())); + i->second->drop_references (); } -#ifdef TRACK_DESTRUCTION - cerr << "delete edit groups\n"; -#endif /* TRACK_DESTRUCTION */ - for (list::iterator i = edit_groups.begin(); i != edit_groups.end(); ) { - list::iterator tmp; - - tmp = 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; - - i = tmp; } - delete [] butler_mixdown_buffer; - delete [] butler_gain_buffer; - Crossfade::set_buffer_size (0); 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"); } void @@ -535,11 +497,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 (); @@ -559,31 +525,39 @@ Session::when_engine_running () if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) { /* existing state for Click */ + int c; - if (_click_io->set_state (*child->children().front()) == 0) { + if (Stateful::loading_state_version < 3000) { + c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false); + } else { + c = _click_io->set_state (*child->children().front(), Stateful::loading_state_version); + } + + if (c == 0) { _clicking = Config->get_clicking (); - + } else { - + error << _("could not setup Click I/O") << endmsg; _clicking = false; } + } else { /* default state for Click: dual-mono to first 2 physical outputs */ for (int physport = 0; physport < 2; ++physport) { string physical_output = _engine.get_nth_physical_output (DataType::AUDIO, physport); - + if (physical_output.length()) { if (_click_io->add_port (physical_output, this)) { // relax, even though its an error } } } - + if (_click_io->n_ports () > ChanCount::ZERO) { _clicking = Config->get_clicking (); } @@ -609,7 +583,7 @@ Session::when_engine_running () mono and stereo bundles, so that the common cases of mono and stereo tracks get bundles to put in their mixer strip in / out menus. There may be a nicer way of achieving that; - it doesn't really scale that well to higher channel counts + it doesn't really scale that well to higher channel counts */ /* mono output bundles */ @@ -618,11 +592,11 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); - shared_ptr c (new Bundle (buf, true)); + shared_ptr c (new Bundle (buf, true)); c->add_channel (_("mono")); - c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); + c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); - add_bundle (c); + add_bundle (c); } /* stereo output bundles */ @@ -647,11 +621,11 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); - shared_ptr c (new Bundle (buf, false)); + shared_ptr c (new Bundle (buf, false)); c->add_channel (_("mono")); - c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); + c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); - add_bundle (c); + add_bundle (c); } /* stereo input bundles */ @@ -671,22 +645,26 @@ Session::when_engine_running () } } - if (Config->get_auto_connect_standard_busses() && !no_auto_connect()) { + BootMessage (_("Setup signal flow and plugins")); + + hookup_io (); + + if (!no_auto_connect()) { + + if (_master_out && Config->get_auto_connect_standard_busses()) { - if (_master_out) { - /* if requested auto-connect the outputs to the first N physical ports. */ uint32_t limit = _master_out->n_outputs().n_total(); - + for (uint32_t n = 0; n < limit; ++n) { Port* p = _master_out->output()->nth (n); string connect_to = _engine.get_nth_physical_output (DataType (p->type()), n); - if (!connect_to.empty()) { + if (!connect_to.empty() && p->connected_to (connect_to) == false) { if (_master_out->output()->connect (p, connect_to, this)) { - error << string_compose (_("cannot connect master output %1 to %2"), n, connect_to) + error << string_compose (_("cannot connect master output %1 to %2"), n, connect_to) << endmsg; break; } @@ -696,26 +674,77 @@ Session::when_engine_running () if (_control_out) { - uint32_t limit = _control_out->n_outputs().n_total(); - - 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); - - 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; + /* AUDIO ONLY as of june 29th 2009, because listen semantics for anything else + are undefined, at best. + */ + + /* control out listens to master bus (but ignores it + under some conditions) + */ + + uint32_t limit = _control_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* 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)) { + error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to) + << endmsg; + break; + } } } } - } - } - BootMessage (_("Setup signal flow and plugins")); + /* if control out is not connected, + connect control out to physical outs, but use ones after the master if possible + */ - hookup_io (); + if (!_control_out->output()->connected_to (boost::shared_ptr())) { + + 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); + } else { + warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"), + Config->get_monitor_bus_preferred_bundle()) + << endmsg; + } + + } 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; + } + } + } + } + } + } + } /* catch up on send+insert cnts */ @@ -763,6 +792,7 @@ Session::hookup_io () /* Tell all IO objects to connect themselves together */ IO::enable_connecting (); + MIDI::JACK_MidiPort::MakeConnections (); /* Now reset all panners */ @@ -773,14 +803,16 @@ Session::hookup_io () if (_control_out) { boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { - boost::shared_ptr t = boost::dynamic_pointer_cast (*x); + for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { - if (t) { - t->listen_via (_control_out, X_("listen")); + if ((*x)->is_control() || (*x)->is_master()) { + continue; } + + (*x)->listen_via (_control_out, + (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), + false, false); } } @@ -796,9 +828,13 @@ Session::hookup_io () graph_reordered (); - /* update mixer solo state */ + /* update the full solo state, which can't be + correctly determined on a per-route basis, but + needs the global overview that only the session + has. + */ - catch_up_on_solo(); + update_route_solo_state (); } void @@ -814,12 +850,17 @@ Session::playlist_length_changed () } void -Session::diskstream_playlist_changed (boost::shared_ptr dstream) +Session::diskstream_playlist_changed (boost::weak_ptr wp) { + boost::shared_ptr dstream = wp.lock (); + if (!dstream) { + return; + } + boost::shared_ptr playlist; if ((playlist = dstream->playlist()) != 0) { - playlist->LengthChanged.connect (mem_fun (this, &Session::playlist_length_changed)); + playlist->LengthChanged.connect_same_thread (*this, boost::bind (&Session::playlist_length_changed, this)); } /* see comment in playlist_length_changed () */ @@ -830,9 +871,9 @@ bool Session::record_enabling_legal () const { /* this used to be in here, but survey says.... we don't need to restrict it */ - // if (record_status() == Recording) { - // return false; - // } + // if (record_status() == Recording) { + // return false; + // } if (Config->get_all_safe()) { return false; @@ -868,7 +909,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 */ @@ -881,7 +922,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 @@ -889,23 +930,24 @@ 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) { - //if (_transport_frame < location->start() || _transport_frame > location->end()) { - if (_transport_frame > location->end()) { + // if (_transport_frame > location->end()) { + + 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); @@ -917,8 +959,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); } @@ -934,12 +976,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); } @@ -954,17 +994,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); @@ -976,11 +1013,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); } @@ -997,13 +1032,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); @@ -1017,7 +1050,7 @@ Session::set_auto_loop_location (Location* location) } void -Session::locations_added (Location* ignored) +Session::locations_added (Location *) { set_dirty (); } @@ -1107,8 +1140,9 @@ 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(); @@ -1171,12 +1205,12 @@ Session::maybe_enable_record () set_dirty(); } -nframes_t +nframes64_t Session::audible_frame () const { - nframes_t ret; + nframes64_t ret; + nframes64_t tf; nframes_t offset; - nframes_t tf; /* the first of these two possible settings for "offset" mean that the audible frame is stationary until @@ -1206,7 +1240,7 @@ Session::audible_frame () const } else { tf = _transport_frame; } - + ret = tf; if (!non_realtime_work_pending()) { @@ -1225,8 +1259,8 @@ Session::audible_frame () const if (tf < _last_roll_location + offset) { return _last_roll_location; } - } - + } + /* forwards */ ret -= offset; @@ -1308,7 +1342,7 @@ Session::set_block_size (nframes_t nframes) } void -Session::set_default_fade (float steepness, float fade_msecs) +Session::set_default_fade (float /*steepness*/, float /*fade_msecs*/) { #if 0 nframes_t fade_frames; @@ -1378,15 +1412,18 @@ trace_terminal (shared_ptr r1, shared_ptr rbase) /* make a copy of the existing list of routes that feed r1 */ - set > existing = r1->fed_by; + set > existing = r1->fed_by; /* for each route that feeds r1, recurse, marking it as feeding rbase as well. */ - for (set >::iterator i = existing.begin(); i != existing.end(); ++i) { - r2 =* i; - + for (set >::iterator i = existing.begin(); i != existing.end(); ++i) { + if (!(r2 = (*i).lock ())) { + /* (*i) went away, ignore it */ + continue; + } + /* r2 is a route that feeds r1 which somehow feeds base. mark base as being fed by r2 */ @@ -1397,7 +1434,7 @@ trace_terminal (shared_ptr r1, shared_ptr rbase) /* 2nd level feedback loop detection. if r1 feeds or is fed by r2, stop here. - */ + */ if ((r1->fed_by.find (r2) != r1->fed_by.end()) && (r2->fed_by.find (r1) != r2->fed_by.end())) { continue; @@ -1480,7 +1517,7 @@ Session::resort_routes_using (shared_ptr r) } list > -Session::new_midi_track (TrackMode mode, uint32_t how_many) +Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many) { char track_name[32]; uint32_t track_id = 0; @@ -1546,7 +1583,7 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) } - if (track->output()->ensure_io (ChanCount(DataType::AUDIO, 1), false, this)) { + if (track->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; goto failed; } @@ -1589,8 +1626,11 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) */ track->midi_diskstream()->non_realtime_input_change(); + if (route_group) { + route_group->add (track); + } - 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); new_routes.push_back (track); @@ -1645,7 +1685,7 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) } list > -Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, uint32_t how_many) +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; @@ -1702,7 +1742,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod shared_ptr track; try { - track = boost::shared_ptr((new AudioTrack (*this, track_name, Route::Flag (0), mode))); + AudioTrack* at = new AudioTrack (*this, track_name, Route::Flag (0), mode); + boost_debug_shared_ptr_mark_interesting (at, "Track"); + track = boost::shared_ptr(at); if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), @@ -1710,7 +1752,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod << endmsg; goto failed; } - + if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) @@ -1740,7 +1782,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod 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) { @@ -1748,7 +1790,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod 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; } @@ -1757,9 +1799,13 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod channels_used += track->n_inputs ().n_audio(); + if (route_group) { + route_group->add (track); + } + 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; @@ -1817,25 +1863,32 @@ 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(); + } } RouteList -Session::new_audio_route (int input_channels, int output_channels, uint32_t how_many) +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; @@ -1887,7 +1940,9 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ } while (bus_id < (UINT_MAX-1)); try { - shared_ptr bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); + Route* rt = 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"), @@ -1906,11 +1961,11 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ for (uint32_t x = 0; n_physical_audio_inputs && x < bus->input()->n_ports().n_audio(); ++x) { port = ""; - + 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; } @@ -1934,9 +1989,16 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ channels_used += bus->n_inputs ().n_audio(); + if (route_group) { + route_group->add (bus); + } bus->set_remote_control_id (control_id); ++control_id; + if (aux) { + bus->add_internal_return (); + } + ret.push_back (bus); } @@ -1971,6 +2033,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; if (!tree.read (template_path.c_str())) { return ret; @@ -1983,38 +2046,33 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template while (how_many) { XMLNode node_copy (*node); // make a copy so we can change the name if we need to - + std::string node_name = IO::name_from_state (*node_copy.children().front()); - if (route_by_name (node_name) != 0) { + /* generate a new name by adding a number to the end of the template name */ - /* 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); - uint32_t number = 1; + number++; - 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) { - fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; - /*NOTREACHED*/ + if (route_by_name (name) == 0) { + break; } - IO::set_name_in_state (*node_copy.children().front(), name); + } while (number < UINT_MAX); + + if (number == UINT_MAX) { + fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; + /*NOTREACHED*/ } + IO::set_name_in_state (*node_copy.children().front(), name); + Track::zero_diskstream_id_in_xml (node_copy); try { - shared_ptr route (XMLRouteFactory (node_copy)); + shared_ptr route (XMLRouteFactory (node_copy, 3000)); if (route == 0) { error << _("Session: cannot create track/bus from template description") << endmsg; @@ -2029,23 +2087,23 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template route->input()->changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); route->output()->changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); } - + route->set_remote_control_id (control_id); ++control_id; - + ret.push_back (route); } - + catch (failed_constructor &err) { error << _("Session: could not create new route from template") << endmsg; goto out; } - + catch (AudioEngine::PortRegistrationFailure& pfe) { error << pfe.what() << endmsg; goto out; } - + --how_many; } @@ -2065,6 +2123,13 @@ Session::add_routes (RouteList& new_routes, bool save) shared_ptr r = writer.get_copy (); r->insert (r->end(), new_routes.begin(), new_routes.end()); + + /* if there is no control out and we're not in the middle of loading, + resort the graph here. if there is a control out, we will resort + toward the end of this method. if we are in the middle of loading, + we will resort when done. + */ + if (!_control_out && IO::connecting_legal) { resort_routes_using (r); } @@ -2073,25 +2138,33 @@ 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); - (*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)); + 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)); - if ((*x)->is_master()) { - _master_out = (*x); + if (r->is_master()) { + _master_out = r; } - if ((*x)->is_control()) { - _control_out = (*x); + if (r->is_control()) { + _control_out = r; } } if (_control_out && IO::connecting_legal) { for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { - (*x)->listen_via (_control_out, "control"); + if ((*x)->is_control() || (*x)->is_master()) { + continue; + } + (*x)->listen_via (_control_out, + (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), + false, false); } resort_routes (); @@ -2104,6 +2177,98 @@ Session::add_routes (RouteList& new_routes, bool save) } RouteAdded (new_routes); /* EMIT SIGNAL */ + Route::RemoteControlIDChange (); /* EMIT SIGNAL */ +} + +void +Session::globally_set_send_gains_to_zero (boost::shared_ptr dest) +{ + boost::shared_ptr r = routes.reader (); + boost::shared_ptr s; + + /* only tracks */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (boost::dynamic_pointer_cast(*i)) { + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value (0.0); + } + } + } +} + +void +Session::globally_set_send_gains_to_unity (boost::shared_ptr dest) +{ + boost::shared_ptr r = routes.reader (); + boost::shared_ptr s; + + /* only tracks */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (boost::dynamic_pointer_cast(*i)) { + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value (1.0); + } + } + } +} + +void +Session::globally_set_send_gains_from_track(boost::shared_ptr dest) +{ + boost::shared_ptr r = routes.reader (); + boost::shared_ptr s; + + /* only tracks */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (boost::dynamic_pointer_cast(*i)) { + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value()); + } + } + } +} + +void +Session::globally_add_internal_sends (boost::shared_ptr dest, Placement p) +{ + boost::shared_ptr r = routes.reader (); + boost::shared_ptr t (new RouteList); + + /* only send tracks */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (boost::dynamic_pointer_cast(*i)) { + t->push_back (*i); + } + } + + add_internal_sends (dest, p, t); +} + +void +Session::add_internal_sends (boost::shared_ptr dest, Placement p, boost::shared_ptr senders) +{ + if (dest->is_control() || dest->is_master()) { + return; + } + + if (!dest->internal_return()) { + dest->add_internal_return(); + } + + for (RouteList::iterator i = senders->begin(); i != senders->end(); ++i) { + + if ((*i)->is_control() || (*i)->is_master() || (*i) == dest) { + continue; + } + + (*i)->listen_via (dest, p, true, true); + } + + graph_reordered (); } void @@ -2121,11 +2286,11 @@ Session::add_diskstream (boost::shared_ptr dstream) /* writer goes out of scope, copies ds back to main */ } - dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), dstream)); + dstream->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::diskstream_playlist_changed, this, boost::weak_ptr (dstream))); /* this will connect to future changes, and check the current length */ - diskstream_playlist_changed (dstream); + diskstream_playlist_changed (boost::weak_ptr (dstream)); - dstream->RecordEnableChanged.connect (mem_fun (*this, &Session::update_have_rec_enabled_diskstream)); + dstream->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_diskstream, this)); dstream->prepare (); @@ -2188,6 +2353,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(); @@ -2203,6 +2380,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)) { @@ -2211,13 +2390,29 @@ Session::remove_route (shared_ptr route) } void -Session::route_mute_changed (void* src) +Session::route_mute_changed (void* /*src*/) { set_dirty (); } void -Session::route_solo_changed (void* src, boost::weak_ptr wpr) +Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) +{ + boost::shared_ptr route = wpr.lock(); + if (!route) { + error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg; + return; + } + + if (route->listening()) { + _listen_cnt++; + } else if (_listen_cnt > 0) { + _listen_cnt--; + } +} + +void +Session::route_solo_changed (void* /*src*/, boost::weak_ptr wpr) { if (solo_update_disabled) { // We know already @@ -2235,31 +2430,41 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) shared_ptr r = routes.reader (); int32_t delta; - if (route->soloed()) { + if (route->self_soloed()) { delta = 1; } else { delta = -1; } + /* now mod the solo level of all other routes except master & control outs + so that they will be silent if appropriate. + */ + solo_update_disabled = true; + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + bool via_sends_only; - if ((*i)->feeds (route)) { - /* do it */ - (*i)->mod_solo_level (delta); - } + + if ((*i) == route || !(*i)->solo_isolated() || !(*i)->is_master() || !(*i)->is_control() || (*i)->is_hidden()) { + continue; + } else if ((*i)->feeds (route, &via_sends_only)) { + if (!via_sends_only) { + (*i)->mod_solo_by_others (delta); + } + } } /* make sure master is never muted by solo */ - if (_master_out->solo_level() == 0) { - _master_out->mod_solo_level (1); - } - + 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 && _control_out->solo_level() == 0) { - _control_out->mod_solo_level (1); + 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; @@ -2280,7 +2485,7 @@ Session::update_route_solo_state (boost::shared_ptr r) } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_master() && !(*i)->is_control() && !(*i)->is_hidden() && (*i)->soloed()) { + if (!(*i)->is_master() && !(*i)->is_control() && !(*i)->is_hidden() && (*i)->self_soloed()) { something_soloed = true; break; } @@ -2292,33 +2497,19 @@ Session::update_route_solo_state (boost::shared_ptr r) } } -void -Session::catch_up_on_solo () +boost::shared_ptr +Session::get_routes_with_internal_returns() const { - /* this is called after set_state() to catch the full solo - state, which can't be correctly determined on a per-route - basis, but needs the global overview that only the session - has. - */ - update_route_solo_state(); -} - -void -Session::catch_up_on_solo_mute_override () -{ - if (Config->get_solo_model() != SoloInPlace) { - return; - } - - /* this is called whenever the param solo-mute-override is - changed. - */ shared_ptr r = routes.reader (); + boost::shared_ptr rl (new RouteList); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - // (*i)->catch_up_on_solo_mute_override (); + if ((*i)->internal_return ()) { + rl->push_back (*i); + } } -} + return rl; +} shared_ptr Session::route_by_name (string name) @@ -2615,9 +2806,7 @@ Session::add_regions (vector >& new_regions) } } - 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))); - + region->StateChanged.connect_same_thread (*this, boost::bind (&Session::region_changed, this, _1, boost::weak_ptr(region))); update_region_name_map (region); } @@ -2631,17 +2820,17 @@ 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); } } @@ -2720,13 +2909,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) { @@ -2780,6 +2962,10 @@ Session::remove_last_capture () } } + for (list >::iterator i = r.begin(); i != r.end(); ++i) { + remove_region (*i); + } + destroy_regions (r); save_state (_current_snapshot_name); @@ -2811,7 +2997,6 @@ 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))); set_dirty(); } @@ -2998,7 +3183,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) */ @@ -3119,16 +3304,16 @@ 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) { 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 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 */ @@ -3180,7 +3365,7 @@ 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) { @@ -3189,45 +3374,9 @@ Session::create_midi_source_for_session (MidiDiskstream& ds) 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) @@ -3236,75 +3385,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 @@ -3320,40 +3407,23 @@ 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 Session::set_audition (boost::shared_ptr r) { pending_audition_region = r; - post_transport_work = PostTransportWork (post_transport_work | PostTransportAudition); - schedule_butler_transport_work (); + add_post_transport_work (PostTransportAudition); + _butler->schedule_transport_work (); } 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); } @@ -3373,7 +3443,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); } @@ -3405,8 +3475,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()); @@ -3440,34 +3510,6 @@ Session::is_auditioning () const } } -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_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 { @@ -3487,10 +3529,10 @@ void Session::graph_reordered () { /* don't do this stuff if we are setting up connections - from a set_state() call or creating new tracks. + from a set_state() call or creating new tracks. Ditto for deletion. */ - if (_state_of_the_state & InitialConnecting) { + if (_state_of_the_state & (InitialConnecting|Deletion)) { return; } @@ -3513,38 +3555,14 @@ Session::graph_reordered () } } -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); - } - } - - /* 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)); + /* Session does not own Processors (they belong to a Route) but we do want to track + the arrival and departure of port inserts, sends and returns for naming + purposes. + */ + processor->DropReferences.connect_same_thread (*this, boost::bind (&Session::remove_processor, this, processor)); set_dirty(); } @@ -3560,7 +3578,7 @@ Session::remove_processor (Processor* processor) } else if ((send = dynamic_cast (processor)) != 0) { send_bitset[send->bit_slot()] = false; } else if ((retrn = dynamic_cast (processor)) != 0) { - return_bitset[send->bit_slot()] = false; + return_bitset[retrn->bit_slot()] = false; } set_dirty(); @@ -3642,7 +3660,7 @@ shared_ptr Session::bundle_by_name (string name) const { boost::shared_ptr b = _bundles.reader (); - + for (BundleList::const_iterator i = b->begin(); i != b->end(); ++i) { if ((*i)->name() == name) { return* i; @@ -3653,17 +3671,11 @@ Session::bundle_by_name (string name) const } void -Session::tempo_map_changed (Change ignored) +Session::tempo_map_changed (Change) { 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 (); } @@ -3794,37 +3806,33 @@ Session::mark_insert_id (uint32_t id) /* 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; @@ -3834,7 +3842,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; @@ -3884,13 +3891,6 @@ 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) { @@ -3937,8 +3937,8 @@ Session::freeze (InterThreadInfo& itt) } boost::shared_ptr -Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, - bool overwrite, vector >& srcs, +Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, + bool /*overwrite*/, vector >& srcs, InterThreadInfo& itt, bool enable_processing) { boost::shared_ptr result; @@ -3961,8 +3961,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 @@ -3996,7 +3995,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) { @@ -4067,7 +4066,7 @@ 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()), + srcs.front()->length(srcs.front()->timeline_position()), region_name_from_path (srcs.front()->name(), true)); } @@ -4079,7 +4078,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, if (afs) { afs->mark_for_remove (); } - + (*src)->drop_references (); } @@ -4176,8 +4175,12 @@ Session::compute_initial_length () } void -Session::sync_order_keys (const char* base) +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; @@ -4190,8 +4193,11 @@ Session::sync_order_keys (const char* 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 */ bool @@ -4220,19 +4226,16 @@ Session::update_have_rec_enabled_diskstream () } void -Session::solo_model_changed () +Session::listen_position_changed () { Placement p; - switch (Config->get_solo_model()) { - case SoloInPlace: - return; - - case SoloAFL: + switch (Config->get_listen_position()) { + case AfterFaderListen: p = PostFader; break; - case SoloPFL: + case PreFaderListen: p = PreFader; break; } @@ -4243,3 +4246,69 @@ Session::solo_model_changed () (*i)->put_control_outs_at (p); } } + +void +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_solo (routes.reader(), false); + } else { + set_listen (routes.reader(), false); + } +} + +void +Session::route_group_changed () +{ + RouteGroupChanged (); /* EMIT SIGNAL */ +} + +vector +Session::get_available_sync_options () const +{ + vector ret; + + ret.push_back (JACK); + + if (mtc_port()) { + ret.push_back (MTC); + } + + if (midi_clock_port()) { + ret.push_back (MIDIClock); + } + + 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 ds = tr->diskstream (); + if (!ds) { + continue; + } + + boost::shared_ptr pl = ds->playlist (); + if (!pl) { + continue; + } + + if (pl->has_region_at (p)) { + rl->push_back (*i); + } + } + + return rl; +}