X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=31693daff39d715ae5384e123f1d7cfcf7f6b3ce;hb=9e0d03020ff47773f7d1c0414de1c74e6c9e0dac;hp=f2e1b2a730e72c6930eccd4577132cae63440e60;hpb=a4b8955f3eb87405ceafe5c8680d9439f441458e;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index f2e1b2a730..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,30 +55,37 @@ #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" #include "ardour/route_group.h" #include "ardour/send.h" #include "ardour/session.h" #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" @@ -85,32 +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; - -#ifdef __x86_64__ -static const int CPU_CACHE_ALIGN = 64; -#else -static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */ -#endif +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, @@ -118,6 +127,7 @@ Session::Session (AudioEngine &eng, string mix_template) : _engine (eng), + _target_transport_speed (0.0), _requested_return_frame (-1), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), @@ -128,17 +138,12 @@ 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), - auditioner ((Auditioner*) 0), _total_free_4k_blocks (0), _bundles (new BundleList), _bundle_xml_node (0), @@ -146,16 +151,21 @@ Session::Session (AudioEngine &eng, click_data (0), click_emphasis_data (0), main_outs (0), - _metadata (new SessionMetadata()) + _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 << " (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); @@ -182,7 +192,8 @@ Session::Session (AudioEngine &eng, _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); - Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed)); + 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 */ @@ -201,6 +212,7 @@ Session::Session (AudioEngine &eng, nframes_t initial_length) : _engine (eng), + _target_transport_speed (0.0), _requested_return_frame (-1), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), @@ -211,17 +223,12 @@ 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), - auditioner ((Auditioner *) 0), _total_free_4k_blocks (0), _bundles (new BundleList), _bundle_xml_node (0), @@ -229,15 +236,20 @@ Session::Session (AudioEngine &eng, click_data (0), click_emphasis_data (0), main_outs (0), - _metadata (new SessionMetadata()) + _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); @@ -267,15 +279,13 @@ Session::Session (AudioEngine &eng, RouteList rl; int control_id = 1; - if (control_out_channels) { - shared_ptr r (new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut)); - r->set_remote_control_id (control_id++); - - rl.push_back (r); - } - if (master_out_channels) { - shared_ptr r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut)); + ChanCount count(DataType::AUDIO, master_out_channels); + 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); rl.push_back (r); @@ -284,12 +294,29 @@ 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); } } + 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); @@ -302,7 +329,7 @@ Session::Session (AudioEngine &eng, _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); - Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed)); + Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, false)); } Session::~Session () @@ -313,6 +340,8 @@ Session::~Session () void Session::destroy () { + vector debug_pointers; + /* if we got to here, leaving pending capture state around is a mistake. */ @@ -323,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 (); @@ -337,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; @@ -354,146 +381,90 @@ Session::destroy () delete _silent_buffers; delete _mix_buffers; - AudioDiskstream::free_working_buffers(); - - Route::SyncOrderKeys.clear(); + /* clear out any pending dead wood from RCU managed objects */ -#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; - - (*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; - - delete *i; + diskstreams.flush (); - 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 @@ -509,8 +480,8 @@ Session::set_worst_io_latencies () boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - _worst_output_latency = max (_worst_output_latency, (*i)->output_latency()); - _worst_input_latency = max (_worst_input_latency, (*i)->input_latency()); + _worst_output_latency = max (_worst_output_latency, (*i)->output()->latency()); + _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency()); } } @@ -519,8 +490,6 @@ Session::when_engine_running () { string first_physical_output; - /* we don't want to run execute this again */ - BootMessage (_("Set block size and sample rate")); set_block_size (_engine.frames_per_cycle()); @@ -528,17 +497,21 @@ Session::when_engine_running () BootMessage (_("Using configuration")); - Config->map_parameters (mem_fun (*this, &Session::config_changed)); + 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 (); } - if (Config->get_jack_time_master()) { + if (config.get_jack_time_master()) { _engine.transport_locate (_transport_frame); } @@ -547,22 +520,30 @@ Session::when_engine_running () try { XMLNode* child = 0; - _click_io.reset (new ClickIO (*this, "click", 0, 0, -1, -1)); + _click_io.reset (new ClickIO (*this, "click")); 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 */ @@ -571,13 +552,13 @@ Session::when_engine_running () string physical_output = _engine.get_nth_physical_output (DataType::AUDIO, physport); if (physical_output.length()) { - if (_click_io->add_output_port (physical_output, this)) { + if (_click_io->add_port (physical_output, this)) { // relax, even though its an error } } } - - if (_click_io->n_outputs () > ChanCount::ZERO) { + + if (_click_io->n_ports () > ChanCount::ZERO) { _clicking = Config->get_clicking (); } } @@ -602,19 +583,24 @@ 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 */ for (uint32_t np = 0; np < n_physical_outputs; ++np) { 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 */ + for (uint32_t np = 0; np < n_physical_outputs; np += 2) { if (np + 1 < n_physical_outputs) { char buf[32]; @@ -629,17 +615,21 @@ Session::when_engine_running () } } + /* mono input bundles */ + for (uint32_t np = 0; np < n_physical_inputs; ++np) { 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 */ + for (uint32_t np = 0; np < n_physical_inputs; np += 2) { if (np + 1 < n_physical_inputs) { char buf[32]; @@ -655,79 +645,108 @@ Session::when_engine_running () } } - if (_master_out) { + BootMessage (_("Setup signal flow and plugins")); - /* create master/control ports */ + hookup_io (); - if (_master_out) { - uint32_t n; + if (!no_auto_connect()) { - /* force the master to ignore any later call to this */ + if (_master_out && Config->get_auto_connect_standard_busses()) { - if (_master_out->pending_state_node) { - _master_out->ports_became_legal(); - } + /* if requested auto-connect the outputs to the first N physical ports. + */ - /* no panner resets till we are through */ + uint32_t limit = _master_out->n_outputs().n_total(); - _master_out->defer_pan_reset (); + 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); - while (_master_out->n_inputs().n_audio() - < _master_out->input_maximum().n_audio()) { - if (_master_out->add_input_port ("", this, DataType::AUDIO)) { - error << _("cannot setup master inputs") - << endmsg; - break; + 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) + << endmsg; + break; + } } } - n = 0; - while (_master_out->n_outputs().n_audio() - < _master_out->output_maximum().n_audio()) { - if (_master_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this, DataType::AUDIO)) { - error << _("cannot setup master outputs") - << endmsg; - break; + } + + if (_control_out) { + + /* 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; + } + } } - n++; } - _master_out->allow_pan_reset (); + /* if control out is not connected, + connect control out to physical outs, but use ones after the master if possible + */ - } - } + if (!_control_out->output()->connected_to (boost::shared_ptr())) { - BootMessage (_("Setup signal flow and plugins")); + if (!Config->get_monitor_bus_preferred_bundle().empty()) { - hookup_io (); + boost::shared_ptr b = bundle_by_name (Config->get_monitor_bus_preferred_bundle()); - /* catch up on send+insert cnts */ + 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; + } - BootMessage (_("Catch up with send/insert state")); + } else { - insert_cnt = 0; + /* XXX this logic is wrong for mixed port types */ - for (list::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) { - uint32_t id; + 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(); - if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) { - if (id > insert_cnt) { - insert_cnt = id; - } - } - } + cerr << "Connecting " << limit << " control out ports, shift is " << shift << " mod is " << mod << endl; - send_cnt = 0; + for (uint32_t n = 0; n < limit; ++n) { - for (list::iterator i = _sends.begin(); i != _sends.end(); ++i) { - uint32_t id; + Port* p = _control_out->output()->nth (n); + string connect_to = _engine.get_nth_physical_output (DataType (p->type()), (n+shift) % mod); - if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) { - if (id > send_cnt) { - send_cnt = id; + 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 */ _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); @@ -748,7 +767,7 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting); - if (auditioner == 0) { + if (!auditioner) { /* we delay creating the auditioner till now because it makes its own connections to ports. @@ -764,45 +783,6 @@ Session::hookup_io () } } - /* Tell all IO objects to create their ports */ - - IO::enable_ports (); - - if (_control_out) { - uint32_t n; - vector cports; - - while (_control_out->n_inputs().n_audio() < _control_out->input_maximum().n_audio()) { - if (_control_out->add_input_port ("", this)) { - error << _("cannot setup control inputs") - << endmsg; - break; - } - } - n = 0; - while (_control_out->n_outputs().n_audio() < _control_out->output_maximum().n_audio()) { - if (_control_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this)) { - error << _("cannot set up master outputs") - << endmsg; - break; - } - n++; - } - - - uint32_t ni = _control_out->n_inputs().get (DataType::AUDIO); - - for (n = 0; n < ni; ++n) { - cports.push_back (_control_out->input(n)->name()); - } - - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { - (*x)->set_control_outs (cports); - } - } - /* load bundles, which we may have postponed earlier on */ if (_bundle_xml_node) { load_bundles (*_bundle_xml_node); @@ -812,10 +792,29 @@ Session::hookup_io () /* Tell all IO objects to connect themselves together */ IO::enable_connecting (); + MIDI::JACK_MidiPort::MakeConnections (); /* Now reset all panners */ - IO::reset_panners (); + Delivery::reset_panners (); + + /* Connect tracks to listen/solo etc. busses XXX generalize this beyond control_out */ + + if (_control_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); + } + } /* Anyone who cares about input state, wake up and do something */ @@ -823,16 +822,19 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state & ~InitialConnecting); - /* now handle the whole enchilada as if it was one graph reorder event. */ 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 @@ -848,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 () */ @@ -864,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; @@ -884,7 +891,7 @@ Session::reset_input_monitor_state () for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; - (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !Config->get_auto_input()); + (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input()); } } } else { @@ -902,9 +909,9 @@ 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()) { + if (get_record_enabled() && config.get_punch_in()) { /* capture start has been changed, so save new pending state */ save_state ("", true); } @@ -915,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 @@ -923,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); @@ -951,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); } @@ -968,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); } @@ -988,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); @@ -1010,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); } @@ -1031,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); @@ -1051,7 +1050,7 @@ Session::set_auto_loop_location (Location* location) } void -Session::locations_added (Location* ignored) +Session::locations_added (Location *) { set_dirty (); } @@ -1110,7 +1109,7 @@ Session::enable_record () _last_record_location = _transport_frame; deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location); - if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) { + 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 ()) { @@ -1141,10 +1140,11 @@ 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()) { + 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) { @@ -1169,7 +1169,7 @@ Session::step_back_from_record () if (g_atomic_int_get (&_record_status) == Recording) { g_atomic_int_set (&_record_status, Enabled); - if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) { + 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) { @@ -1194,7 +1194,7 @@ Session::maybe_enable_record () save_state ("", true); if (_transport_speed) { - if (!Config->get_punch_in()) { + if (!config.get_punch_in()) { enable_record (); } } else { @@ -1205,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 @@ -1240,7 +1240,7 @@ Session::audible_frame () const } else { tf = _transport_frame; } - + ret = tf; if (!non_realtime_work_pending()) { @@ -1259,8 +1259,8 @@ Session::audible_frame () const if (tf < _last_roll_location + offset) { return _last_roll_location; } - } - + } + /* forwards */ ret -= offset; @@ -1317,7 +1317,6 @@ Session::set_block_size (nframes_t nframes) */ { - current_block_size = nframes; ensure_buffers(_scratch_buffers->available()); @@ -1343,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; @@ -1413,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 */ @@ -1432,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; @@ -1515,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; @@ -1575,7 +1577,13 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) try { track = boost::shared_ptr((new MidiTrack (*this, track_name, Route::Flag (0), mode))); - if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::AUDIO, 1), false, this)) { + if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { + error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + goto failed; + } + + + 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; } @@ -1618,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); @@ -1674,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; @@ -1731,9 +1742,18 @@ 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"), + input_channels, output_channels) + << endmsg; + goto failed; + } - if (track->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) { + if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) << endmsg; @@ -1751,7 +1771,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod port = physinputs[(channels_used+x)%nphysical_in]; } - if (port.length() && track->connect_input (track->input (x), port, this)) { + if (port.length() && track->input()->connect (track->input()->nth(x), port, this)) { break; } } @@ -1761,18 +1781,17 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod uint32_t nphysical_out = physoutputs.size(); 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) { - port = _master_out->input (x%_master_out->n_inputs().n_audio())->name(); + 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->connect_output (track->output (x), port, this)) { + + if (port.length() && track->output()->connect (track->output()->nth(x), port, this)) { break; } } @@ -1780,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; @@ -1840,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; @@ -1910,9 +1940,11 @@ 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, -1, -1, -1, -1, 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->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) { + if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), input_channels, output_channels) << endmsg; @@ -1920,21 +1952,24 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ } + if (bus->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failure; + } - /* - for (uint32_t x = 0; n_physical_audio_inputs && x < bus->n_inputs(); ++x) { - + 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->connect_input (bus->input (x), port, this)) { + } + + if (port.length() && bus->input()->connect (bus->input()->nth (x), port, this)) { break; } } - */ for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs().n_audio(); ++x) { port = ""; @@ -1943,20 +1978,27 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ port = physoutputs[((n+x)%n_physical_outputs)]; } else if (Config->get_output_auto_connect() & AutoConnectMaster) { if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs().n_audio())->name(); + port = _master_out->input()->nth (x%_master_out->input()->n_ports().n_audio())->name(); } } - if (port.length() && bus->connect_output (bus->output (x), port, this)) { + if (port.length() && bus->output()->connect (bus->output()->nth(x), port, this)) { break; } } 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); } @@ -1991,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; @@ -2003,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) { + 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 */ + /* generate a new name by adding a number to the end of the template name */ - uint32_t number = 1; + do { + snprintf (name, sizeof (name), "%s %" PRIu32, node_name.c_str(), 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); + number++; - 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; @@ -2046,26 +2084,26 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template picks up the configuration of the route. During session loading this normally happens in a different way. */ - route->input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); - route->output_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this); + 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; } @@ -2084,39 +2122,52 @@ Session::add_routes (RouteList& new_routes, bool save) RCUWriter writer (routes); shared_ptr r = writer.get_copy (); r->insert (r->end(), new_routes.begin(), new_routes.end()); - resort_routes_using (r); + + + /* 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); + } } 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) { - vector cports; - uint32_t ni = _control_out->n_inputs().n_audio(); - - for (uint32_t n = 0; n < ni; ++n) { - cports.push_back (_control_out->input(n)->name()); - } - for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { - (*x)->set_control_outs (cports); + if ((*x)->is_control() || (*x)->is_master()) { + continue; + } + (*x)->listen_via (_control_out, + (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), + false, false); } + + resort_routes (); } set_dirty(); @@ -2126,13 +2177,105 @@ Session::add_routes (RouteList& new_routes, bool save) } RouteAdded (new_routes); /* EMIT SIGNAL */ + Route::RemoteControlIDChange (); /* EMIT SIGNAL */ } void -Session::add_diskstream (boost::shared_ptr dstream) +Session::globally_set_send_gains_to_zero (boost::shared_ptr dest) { - /* need to do this in case we're rolling at the time, to prevent false underruns */ - dstream->do_refill_with_alloc (); + 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 +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); @@ -2143,9 +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_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_diskstream, this)); dstream->prepare (); @@ -2170,15 +2315,14 @@ Session::remove_route (shared_ptr route) } if (route == _control_out) { - _control_out = shared_ptr (); /* cancel control outs for all routes */ - vector empty; - for (RouteList::iterator r = rs->begin(); r != rs->end(); ++r) { - (*r)->set_control_outs (empty); + (*r)->drop_listen (_control_out); } + + _control_out.reset (); } update_route_solo_state (); @@ -2206,8 +2350,20 @@ Session::remove_route (shared_ptr route) // We need to disconnect the routes inputs and outputs - route->disconnect_inputs (0); - route->disconnect_outputs (0); + 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(); @@ -2224,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)) { @@ -2232,20 +2390,35 @@ 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 return; } - bool is_track; boost::shared_ptr route = wpr.lock (); if (!route) { @@ -2254,218 +2427,90 @@ Session::route_solo_changed (void* src, boost::weak_ptr wpr) return; } - is_track = (boost::dynamic_pointer_cast(route) != 0); - shared_ptr r = routes.reader (); + int32_t delta; - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - - /* soloing a track mutes all other tracks, soloing a bus mutes all other busses */ - - if (is_track) { - - /* don't mess with busses */ - - if (boost::dynamic_pointer_cast(*i) == 0) { - continue; - } - - } else { - - /* don't mess with tracks */ - - if (boost::dynamic_pointer_cast(*i) != 0) { - continue; - } - } - - if ((*i) != route && - ((*i)->mix_group () == 0 || - (*i)->mix_group () != route->mix_group () || - !route->mix_group ()->is_active())) { - - if ((*i)->soloed()) { - - /* if its already soloed, and solo latching is enabled, - then leave it as it is. - */ + if (route->self_soloed()) { + delta = 1; + } else { + delta = -1; + } - if (Config->get_solo_latched()) { - continue; - } - } + /* now mod the solo level of all other routes except master & control outs + so that they will be silent if appropriate. + */ - /* do it */ + solo_update_disabled = true; - solo_update_disabled = true; - (*i)->set_solo (false, src); - solo_update_disabled = false; - } - } + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + bool via_sends_only; - bool something_soloed = false; - bool same_thing_soloed = false; - bool signal = false; - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i)->soloed()) { - something_soloed = true; - if (boost::dynamic_pointer_cast(*i)) { - if (is_track) { - same_thing_soloed = true; - break; - } - } else { - if (!is_track) { - same_thing_soloed = true; - break; - } + 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); } - break; - } + } } - if (something_soloed != currently_soloing) { - signal = true; - currently_soloing = something_soloed; - } + /* make sure master is never muted by solo */ - modify_solo_mute (is_track, same_thing_soloed); + 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 (signal) { - SoloActive (currently_soloing); /* EMIT SIGNAL */ + 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 */ - set_dirty(); } void -Session::update_route_solo_state () +Session::update_route_solo_state (boost::shared_ptr r) { - bool mute = false; - bool is_track = false; - bool signal = false; + /* now figure out if anything that matters is soloed */ - /* this is where we actually implement solo by changing - the solo mute setting of each track. - */ + bool something_soloed = false; - shared_ptr r = routes.reader (); + if (!r) { + r = routes.reader(); + } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i)->soloed()) { - mute = true; - if (boost::dynamic_pointer_cast(*i)) { - is_track = true; - } + if (!(*i)->is_master() && !(*i)->is_control() && !(*i)->is_hidden() && (*i)->self_soloed()) { + something_soloed = true; break; } } - if (mute != currently_soloing) { - signal = true; - currently_soloing = mute; - } - - if (!is_track && !mute) { - - /* nothing is soloed */ - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->set_solo_mute (false); - } - - if (signal) { - SoloActive (false); - } - - return; - } - - modify_solo_mute (is_track, mute); - - if (signal) { - SoloActive (currently_soloing); + if (something_soloed != _non_soloed_outs_muted) { + _non_soloed_outs_muted = something_soloed; + SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */ } } -void -Session::modify_solo_mute (bool is_track, bool mute) +boost::shared_ptr +Session::get_routes_with_internal_returns() const { shared_ptr r = routes.reader (); + boost::shared_ptr rl (new RouteList); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - - if (is_track) { - - /* only alter track solo mute */ - - if (boost::dynamic_pointer_cast(*i)) { - if ((*i)->soloed()) { - (*i)->set_solo_mute (!mute); - } else { - (*i)->set_solo_mute (mute); - } - } - - } else { - - /* only alter bus solo mute */ - - if (!boost::dynamic_pointer_cast(*i)) { - - if ((*i)->soloed()) { - - (*i)->set_solo_mute (false); - - } else { - - /* don't mute master or control outs - in response to another bus solo - */ - - if ((*i) != _master_out && - (*i) != _control_out) { - (*i)->set_solo_mute (mute); - } - } - } - + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i)->internal_return ()) { + rl->push_back (*i); } } + return rl; } - -void -Session::catch_up_on_solo () -{ - /* 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() != InverseMute) { - return; - } - - /* this is called whenever the param solo-mute-override is - changed. - */ - shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->catch_up_on_solo_mute_override (); - } -} - shared_ptr Session::route_by_name (string name) { @@ -2761,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); } @@ -2777,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); } } @@ -2866,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) { @@ -2926,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); @@ -2957,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(); } @@ -3144,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) */ @@ -3265,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 */ @@ -3326,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) { @@ -3335,46 +3374,10 @@ Session::create_midi_source_for_session (MidiDiskstream& ds) return boost::dynamic_pointer_cast ( SourceFactory::createWritable ( - DataType::MIDI, *this, path, true, false, frame_rate())); + DataType::MIDI, *this, path, 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(); -} - -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) { @@ -3382,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 @@ -3466,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); } @@ -3519,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); } @@ -3551,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()); @@ -3586,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 { @@ -3633,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; } @@ -3659,54 +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) { - Send* send; - PortInsert* port_insert; - PluginInsert* plugin_insert; - - if ((port_insert = dynamic_cast (processor)) != 0) { - _port_inserts.insert (_port_inserts.begin(), port_insert); - } else if ((plugin_insert = dynamic_cast (processor)) != 0) { - _plugin_inserts.insert (_plugin_inserts.begin(), plugin_insert); - } else if ((send = dynamic_cast (processor)) != 0) { - _sends.insert (_sends.begin(), send); - } else { - fatal << _("programming error: unknown type of Insert created!") << endmsg; - /*NOTREACHED*/ - } - - 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(); } @@ -3714,26 +3570,15 @@ void Session::remove_processor (Processor* processor) { Send* send; + Return* retrn; PortInsert* port_insert; - PluginInsert* plugin_insert; if ((port_insert = dynamic_cast (processor)) != 0) { - list::iterator x = find (_port_inserts.begin(), _port_inserts.end(), port_insert); - if (x != _port_inserts.end()) { - insert_bitset[port_insert->bit_slot()] = false; - _port_inserts.erase (x); - } - } else if ((plugin_insert = dynamic_cast (processor)) != 0) { - _plugin_inserts.remove (plugin_insert); + insert_bitset[port_insert->bit_slot()] = false; } else if ((send = dynamic_cast (processor)) != 0) { - list::iterator x = find (_sends.begin(), _sends.end(), send); - if (x != _sends.end()) { - send_bitset[send->bit_slot()] = false; - _sends.erase (x); - } - } else { - fatal << _("programming error: unknown type of Insert deleted!") << endmsg; - /*NOTREACHED*/ + send_bitset[send->bit_slot()] = false; + } else if ((retrn = dynamic_cast (processor)) != 0) { + return_bitset[retrn->bit_slot()] = false; } set_dirty(); @@ -3744,7 +3589,7 @@ Session::available_capture_duration () { float sample_bytes_on_disk = 4.0; // keep gcc happy - switch (Config->get_native_file_data_format()) { + switch (config.get_native_file_data_format()) { case FormatFloat: sample_bytes_on_disk = 4.0; break; @@ -3815,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; @@ -3826,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 (); } @@ -3847,22 +3686,28 @@ Session::tempo_map_changed (Change ignored) void Session::ensure_buffers (ChanCount howmany) { - if (current_block_size == 0) + if (current_block_size == 0) { return; // too early? (is this ok?) + } - // We need at least 2 MIDI scratch buffers to mix/merge - if (howmany.n_midi() < 2) - howmany.set_midi(2); - - // FIXME: JACK needs to tell us maximum MIDI buffer size - // Using nasty assumption (max # events == nframes) for now - _scratch_buffers->ensure_buffers(howmany, current_block_size); - _mix_buffers->ensure_buffers(howmany, current_block_size); - _silent_buffers->ensure_buffers(howmany, current_block_size); + 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); } +void +Session::ensure_buffer_set(BufferSet& buffers, const ChanCount& count) +{ + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + buffers.ensure_buffers(*t, count.get(*t), _engine.raw_buffer_size(*t)); + } +} + uint32_t Session::next_insert_id () { @@ -3903,6 +3748,26 @@ Session::next_send_id () } } +uint32_t +Session::next_return_id () +{ + /* this doesn't really loop forever. just think about it */ + + while (true) { + for (boost::dynamic_bitset::size_type n = 0; n < return_bitset.size(); ++n) { + if (!return_bitset[n]) { + return_bitset[n] = true; + return n; + + } + } + + /* none available, so resize and try again */ + + return_bitset.resize (return_bitset.size() + 16, false); + } +} + void Session::mark_send_id (uint32_t id) { @@ -3915,6 +3780,18 @@ Session::mark_send_id (uint32_t id) send_bitset[id] = true; } +void +Session::mark_return_id (uint32_t id) +{ + if (id >= return_bitset.size()) { + return_bitset.resize (id+16, false); + } + if (return_bitset[id]) { + warning << string_compose (_("return ID %1 appears to be in use already"), id) << endmsg; + } + return_bitset[id] = true; +} + void Session::mark_insert_id (uint32_t id) { @@ -3929,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; @@ -3969,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; @@ -4005,11 +3877,18 @@ Session::route_name_unique (string n) const return true; } -uint32_t -Session::n_playlists () const +bool +Session::route_name_internal (string n) const { - Glib::Mutex::Lock lm (playlist_lock); - return playlists.size(); + if (auditioner && auditioner->name() == n) { + return true; + } + + if (_click_io && _click_io->name() == n) { + return true; + } + + return false; } void @@ -4058,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; @@ -4082,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 @@ -4117,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) { @@ -4134,7 +4012,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, to_do = len; /* create a set of reasonably-sized buffers */ - buffers.ensure_buffers(nchans, chunk_size); + buffers.ensure_buffers(DataType::AUDIO, nchans.n_audio(), chunk_size); buffers.set_count(nchans); for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { @@ -4188,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)); } @@ -4200,7 +4078,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, if (afs) { afs->mark_for_remove (); } - + (*src)->drop_references (); } @@ -4297,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; @@ -4311,6 +4193,122 @@ 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 +Session::have_rec_enabled_diskstream () const +{ + return g_atomic_int_get (&_have_rec_enabled_diskstream) == 1; +} + +/** Update the state of our rec-enabled diskstreams flag */ +void +Session::update_have_rec_enabled_diskstream () +{ + boost::shared_ptr dsl = diskstreams.reader (); + DiskstreamList::iterator i = dsl->begin (); + while (i != dsl->end () && (*i)->record_enabled () == false) { + ++i; + } + + int const old = g_atomic_int_get (&_have_rec_enabled_diskstream); + + g_atomic_int_set (&_have_rec_enabled_diskstream, i != dsl->end () ? 1 : 0); + + if (g_atomic_int_get (&_have_rec_enabled_diskstream) != old) { + RecordStateChanged (); /* EMIT SIGNAL */ + } +} + +void +Session::listen_position_changed () +{ + Placement p; + + switch (Config->get_listen_position()) { + case AfterFaderListen: + p = PostFader; + break; + + case PreFaderListen: + p = PreFader; + break; + } + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->put_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; +}