X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=b7d04c379fefd477897fdfb9c8e3575c87226568;hb=7590b859fd0f836c624394c674aac09f812b3cfb;hp=91b1e2a0f95465e1e7c2a8087b199add54fd4b11;hpb=f4401c59284258c6aa56707da64e3da32756329f;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 91b1e2a0f9..b7d04c379f 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -17,7 +17,6 @@ */ -#define __STDC_LIMIT_MACROS #include #include @@ -35,6 +34,8 @@ #include #include +#include + #include "pbd/error.h" #include "pbd/boost_debug.h" #include "pbd/pathscanner.h" @@ -43,6 +44,7 @@ #include "pbd/stacktrace.h" #include "pbd/file_utils.h" #include "pbd/convert.h" +#include "pbd/strsplit.h" #include "ardour/amp.h" #include "ardour/analyser.h" @@ -97,31 +99,34 @@ #include "ardour/tempo.h" #include "ardour/utils.h" #include "ardour/graph.h" +#include "ardour/speakers.h" +#include "ardour/operations.h" -#include "midi++/jack.h" +#include "midi++/port.h" +#include "midi++/mmc.h" +#include "midi++/manager.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; PBD::Signal1 Session::Dialog; PBD::Signal0 Session::AskAboutPendingState; -PBD::Signal2 Session::AskAboutSampleRateMismatch; +PBD::Signal2 Session::AskAboutSampleRateMismatch; PBD::Signal0 Session::SendFeedback; +PBD::Signal3 Session::MissingFile; -PBD::Signal0 Session::TimecodeOffsetChanged; -PBD::Signal0 Session::StartTimeChanged; -PBD::Signal0 Session::EndTimeChanged; +PBD::Signal1 Session::StartTimeChanged; +PBD::Signal1 Session::EndTimeChanged; PBD::Signal0 Session::AutoBindingOn; PBD::Signal0 Session::AutoBindingOff; PBD::Signal2 Session::Exported; PBD::Signal1 > Session::AskAboutPlaylistDeletion; +PBD::Signal0 Session::Quit; static void clean_up_session_event (SessionEvent* ev) { delete ev; } const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event); @@ -131,54 +136,55 @@ Session::Session (AudioEngine &eng, const string& snapshot_name, BusProfile* bus_profile, string mix_template) + : _engine (eng) + , _target_transport_speed (0.0) + , _requested_return_frame (-1) + , _session_dir (new SessionDirectory(fullpath)) + , state_tree (0) + , _state_of_the_state (Clean) + , _butler (new Butler (*this)) + , _post_transport_work (0) + , _send_timecode_update (false) + , _all_route_group (new RouteGroup (*this, "all")) + , route_graph (new Graph(*this)) + , routes (new RouteList) + , _total_free_4k_blocks (0) + , _bundles (new BundleList) + , _bundle_xml_node (0) + , _current_trans (0) + , _click_io ((IO*) 0) + , click_data (0) + , click_emphasis_data (0) + , main_outs (0) + , _metadata (new SessionMetadata()) + , _have_rec_enabled_track (false) + , _suspend_timecode_transmission (0) +{ + _locations = new Locations (*this); + + playlists.reset (new SessionPlaylists); - : _engine (eng), - _target_transport_speed (0.0), - _requested_return_frame (-1), - mmc (0), - _mmc_port (default_mmc_port), - _mtc_port (default_mtc_port), - _midi_port (default_midi_port), - _midi_clock_port (default_midi_clock_port), - _session_dir (new SessionDirectory(fullpath)), - state_tree (0), - _butler (new Butler (*this)), - _post_transport_work (0), - _send_timecode_update (false), - route_graph (new Graph(*this)), - routes (new RouteList), - _total_free_4k_blocks (0), - _bundles (new BundleList), - _bundle_xml_node (0), - _click_io ((IO*) 0), - click_data (0), - click_emphasis_data (0), - main_outs (0), - _metadata (new SessionMetadata()), - _have_rec_enabled_track (false) + _all_route_group->set_active (true, this); -{ - playlists.reset (new SessionPlaylists); - interpolation.add_channel_to (0, 0); if (!eng.connected()) { throw failed_constructor(); } - n_physical_outputs = _engine.n_physical_outputs(DataType::AUDIO); - n_physical_inputs = _engine.n_physical_inputs(DataType::AUDIO); + n_physical_outputs = _engine.n_physical_outputs (); + n_physical_inputs = _engine.n_physical_inputs (); first_stage_init (fullpath, snapshot_name); - _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); if (_is_new) { if (create (mix_template, bus_profile)) { destroy (); throw failed_constructor (); } - } + } if (second_stage_init ()) { destroy (); @@ -198,6 +204,9 @@ Session::Session (AudioEngine &eng, DirtyChanged (); /* EMIT SIGNAL */ } + StartTimeChanged.connect_same_thread (*this, boost::bind (&Session::start_time_changed, this, _1)); + EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1)); + _is_new = false; } @@ -229,6 +238,10 @@ Session::destroy () delete state_tree; + /* remove all stubfiles that might still be lurking */ + + cleanup_stubfiles (); + /* reset dynamic state version back to default */ Stateful::loading_state_version = 0; @@ -236,6 +249,7 @@ Session::destroy () _butler->drop_references (); delete _butler; delete midi_control_ui; + delete _all_route_group; if (click_data != default_click) { delete [] click_data; @@ -266,7 +280,7 @@ Session::destroy () RegionFactory::delete_all_regions (); DEBUG_TRACE (DEBUG::Destruction, "delete routes\n"); - + /* reset these three references to special routes before we do the usual route delete thing */ auditioner.reset (); @@ -305,14 +319,16 @@ Session::destroy () 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 (); + delete _locations; DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); + +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + boost_debug_list_ptrs (); +#endif } void @@ -396,22 +412,23 @@ Session::when_engine_running () /* 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 - } - } - } + vector outs; + _engine.get_physical_outputs (DataType::AUDIO, outs); - if (_click_io->n_ports () > ChanCount::ZERO) { - _clicking = Config->get_clicking (); - } + for (uint32_t physport = 0; physport < 2; ++physport) { + if (outs.size() > physport) { + if (_click_io->add_port (outs[physport], this)) { + // relax, even though its an error + } + } + } + + if (_click_io->n_ports () > ChanCount::ZERO) { + _clicking = Config->get_clicking (); + } } } - + catch (failed_constructor& err) { error << _("cannot setup Click I/O") << endmsg; } @@ -426,6 +443,13 @@ Session::when_engine_running () BootMessage (_("Set up standard connections")); + vector inputs[DataType::num_types]; + vector outputs[DataType::num_types]; + for (uint32_t i = 0; i < DataType::num_types; ++i) { + _engine.get_physical_inputs (DataType (DataType::Symbol (i)), inputs[i]); + _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]); + } + /* Create a set of Bundle objects that map to the physical I/O currently available. We create both mono and stereo bundles, so that the common cases of mono @@ -436,28 +460,28 @@ Session::when_engine_running () /* mono output bundles */ - for (uint32_t np = 0; np < n_physical_outputs; ++np) { + for (uint32_t np = 0; np < outputs[DataType::AUDIO].size(); ++np) { char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); - shared_ptr c (new Bundle (buf, true)); - c->add_channel (_("mono")); - c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); + boost::shared_ptr c (new Bundle (buf, true)); + c->add_channel (_("mono"), DataType::AUDIO); + c->set_port (0, outputs[DataType::AUDIO][np]); add_bundle (c); } /* stereo output bundles */ - for (uint32_t np = 0; np < n_physical_outputs; np += 2) { - if (np + 1 < n_physical_outputs) { + for (uint32_t np = 0; np < outputs[DataType::AUDIO].size(); np += 2) { + if (np + 1 < outputs[DataType::AUDIO].size()) { char buf[32]; snprintf (buf, sizeof(buf), _("out %" PRIu32 "+%" PRIu32), np + 1, np + 2); - shared_ptr c (new Bundle (buf, true)); - c->add_channel (_("L")); - c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); - c->add_channel (_("R")); - c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1)); + boost::shared_ptr c (new Bundle (buf, true)); + c->add_channel (_("L"), DataType::AUDIO); + c->set_port (0, outputs[DataType::AUDIO][np]); + c->add_channel (_("R"), DataType::AUDIO); + c->set_port (1, outputs[DataType::AUDIO][np + 1]); add_bundle (c); } @@ -465,40 +489,66 @@ Session::when_engine_running () /* mono input bundles */ - for (uint32_t np = 0; np < n_physical_inputs; ++np) { + for (uint32_t np = 0; np < inputs[DataType::AUDIO].size(); ++np) { char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); - shared_ptr c (new Bundle (buf, false)); - c->add_channel (_("mono")); - c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); + boost::shared_ptr c (new Bundle (buf, false)); + c->add_channel (_("mono"), DataType::AUDIO); + c->set_port (0, inputs[DataType::AUDIO][np]); add_bundle (c); } /* stereo input bundles */ - for (uint32_t np = 0; np < n_physical_inputs; np += 2) { - if (np + 1 < n_physical_inputs) { + for (uint32_t np = 0; np < inputs[DataType::AUDIO].size(); np += 2) { + if (np + 1 < inputs[DataType::AUDIO].size()) { char buf[32]; snprintf (buf, sizeof(buf), _("in %" PRIu32 "+%" PRIu32), np + 1, np + 2); - shared_ptr c (new Bundle (buf, false)); - c->add_channel (_("L")); - c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); - c->add_channel (_("R")); - c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1)); + boost::shared_ptr c (new Bundle (buf, false)); + c->add_channel (_("L"), DataType::AUDIO); + c->set_port (0, inputs[DataType::AUDIO][np]); + c->add_channel (_("R"), DataType::AUDIO); + c->set_port (1, inputs[DataType::AUDIO][np + 1]); add_bundle (c); } } + /* MIDI input bundles */ + + for (uint32_t np = 0; np < inputs[DataType::MIDI].size(); ++np) { + string n = inputs[DataType::MIDI][np]; + boost::erase_first (n, X_("alsa_pcm:")); + + boost::shared_ptr c (new Bundle (n, false)); + c->add_channel ("", DataType::MIDI); + c->set_port (0, inputs[DataType::MIDI][np]); + add_bundle (c); + } + + /* MIDI output bundles */ + + for (uint32_t np = 0; np < outputs[DataType::MIDI].size(); ++np) { + string n = outputs[DataType::MIDI][np]; + boost::erase_first (n, X_("alsa_pcm:")); + + boost::shared_ptr c (new Bundle (n, true)); + c->add_channel ("", DataType::MIDI); + c->set_port (0, outputs[DataType::MIDI][np]); + add_bundle (c); + } + BootMessage (_("Setup signal flow and plugins")); hookup_io (); if (_is_new && !no_auto_connect()) { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock()); + /* don't connect the master bus outputs if there is a monitor bus */ if (_master_out && Config->get_auto_connect_standard_busses() && !_monitor_out) { @@ -510,7 +560,10 @@ Session::when_engine_running () 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); + string connect_to; + if (outputs[p->type()].size() > n) { + connect_to = outputs[p->type()][n]; + } if (!connect_to.empty() && p->connected_to (connect_to) == false) { if (_master_out->output()->connect (p, connect_to, this)) { @@ -570,13 +623,16 @@ Session::when_engine_running () } else { for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - uint32_t mod = _engine.n_physical_outputs (*t); + uint32_t mod = n_physical_outputs.get (*t); uint32_t limit = _monitor_out->n_outputs().get(*t); for (uint32_t n = 0; n < limit; ++n) { Port* p = _monitor_out->output()->ports().port(*t, n); - string connect_to = _engine.get_nth_physical_output (*t, (n % mod)); + string connect_to; + if (outputs[*t].size() > (n % mod)) { + connect_to = outputs[*t][n % mod]; + } if (!connect_to.empty()) { if (_monitor_out->output()->connect (p, connect_to, this)) { @@ -603,6 +659,7 @@ Session::when_engine_running () BootMessage (_("Connect to engine")); _engine.set_session (this); + _engine.update_total_latencies (); } void @@ -614,7 +671,6 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting); - if (!auditioner) { /* we delay creating the auditioner till now because @@ -622,13 +678,12 @@ Session::hookup_io () */ try { - Auditioner* a = new Auditioner (*this); - if (a->init()) { - delete a; - throw failed_constructor(); - } - a->use_new_diskstream (); - auditioner.reset (a); + boost::shared_ptr a (new Auditioner (*this)); + if (a->init()) { + throw failed_constructor (); + } + a->use_new_diskstream (); + auditioner = a; } catch (failed_constructor& err) { @@ -645,7 +700,7 @@ Session::hookup_io () /* Tell all IO objects to connect themselves together */ IO::enable_connecting (); - MIDI::JACK_MidiPort::MakeConnections (); + MIDI::Port::MakeConnections (); /* Now reset all panners */ @@ -657,26 +712,24 @@ Session::hookup_io () they connect to the control out specifically. */ - if (_monitor_out) { + if (_monitor_out) { boost::shared_ptr r = routes.reader (); - for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { + for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { - if ((*x)->is_monitor()) { - - /* relax */ + if ((*x)->is_monitor()) { - } else if ((*x)->is_master()) { + /* relax */ - /* relax */ + } else if ((*x)->is_master()) { - } else { + /* relax */ - (*x)->listen_via (_monitor_out, - (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), - false, false); - } - } - } + } else { + + (*x)->listen_via_monitor (); + } + } + } /* Anyone who cares about input state, wake up and do something */ @@ -699,12 +752,6 @@ Session::hookup_io () update_route_solo_state (); } -void -Session::playlist_length_changed () -{ - update_session_range_location_marker (); -} - void Session::track_playlist_changed (boost::weak_ptr wp) { @@ -716,10 +763,9 @@ Session::track_playlist_changed (boost::weak_ptr wp) boost::shared_ptr playlist; if ((playlist = track->playlist()) != 0) { - playlist->LengthChanged.connect_same_thread (*this, boost::bind (&Session::playlist_length_changed, this)); + playlist->RegionAdded.connect_same_thread (*this, boost::bind (&Session::playlist_region_added, this, _1)); + playlist->RangesMoved.connect_same_thread (*this, boost::bind (&Session::playlist_ranges_moved, this, _1)); } - - update_session_range_location_marker (); } bool @@ -777,7 +823,7 @@ Session::auto_punch_start_changed (Location* location) void Session::auto_punch_end_changed (Location* location) { - nframes_t when_to_stop = location->end(); + framepos_t when_to_stop = location->end(); // when_to_stop += _worst_output_latency + _worst_input_latency; replace_event (SessionEvent::PunchOut, when_to_stop); } @@ -785,7 +831,7 @@ Session::auto_punch_end_changed (Location* location) void Session::auto_punch_changed (Location* location) { - nframes_t when_to_stop = location->end(); + framepos_t when_to_stop = location->end(); replace_event (SessionEvent::PunchIn, location->start()); //when_to_stop += _worst_output_latency + _worst_input_latency; @@ -832,7 +878,7 @@ Session::set_auto_punch_location (Location* location) { Location* existing; - if ((existing = _locations.auto_punch_location()) != 0 && existing != location) { + if ((existing = _locations->auto_punch_location()) != 0 && existing != location) { punch_connections.drop_connections(); existing->set_auto_punch (false, this); remove_event (existing->start(), SessionEvent::PunchIn); @@ -869,7 +915,7 @@ Session::set_auto_loop_location (Location* location) { Location* existing; - if ((existing = _locations.auto_loop_location()) != 0 && existing != location) { + if ((existing = _locations->auto_loop_location()) != 0 && existing != location) { loop_connections.drop_connections (); existing->set_auto_loop (false, this); remove_event (existing->end(), SessionEvent::AutoLoop); @@ -915,7 +961,7 @@ Session::locations_added (Location *) void Session::locations_changed () { - _locations.apply (*this, &Session::handle_locations_changed); + _locations->apply (*this, &Session::handle_locations_changed); } void @@ -957,24 +1003,32 @@ Session::handle_locations_changed (Locations::LocationList& locations) void Session::enable_record () { - /* XXX really atomic compare+swap here */ - if (g_atomic_int_get (&_record_status) != Recording) { - g_atomic_int_set (&_record_status, Recording); - _last_record_location = _transport_frame; - deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location); + while (1) { + RecordState rs = (RecordState) g_atomic_int_get (&_record_status); + + if (rs == Recording) { + break; + } - if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { - - boost::shared_ptr rl = routes.reader (); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && tr->record_enabled ()) { - tr->monitor_input (true); + if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) { + + _last_record_location = _transport_frame; + MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe)); + + if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { + + boost::shared_ptr rl = routes.reader (); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr && tr->record_enabled ()) { + tr->monitor_input (true); + } } } + + RecordStateChanged (); + break; } - - RecordStateChanged (); } } @@ -987,19 +1041,13 @@ Session::disable_record (bool rt_context, bool force) if ((!Config->get_latched_record_enable () && !play_loop) || force) { g_atomic_int_set (&_record_status, Disabled); + MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit)); } else { if (rs == Recording) { g_atomic_int_set (&_record_status, Enabled); } } - // 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) { - deliver_mmc (MIDI::MachineControl::cmdRecordExit, _transport_frame); - } - if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { boost::shared_ptr rl = routes.reader (); @@ -1040,32 +1088,38 @@ Session::step_back_from_record () void Session::maybe_enable_record () { + if (_step_editors > 0) { + return; + } + g_atomic_int_set (&_record_status, Enabled); - /* this function is currently called from somewhere other than an RT thread. - this save_state() call therefore doesn't impact anything. + /* This function is currently called from somewhere other than an RT thread. + This save_state() call therefore doesn't impact anything. Doing it here + means that we save pending state of which sources the next record will use, + which gives us some chance of recovering from a crash during the record. */ - + save_state ("", true); - + if (_transport_speed) { if (!config.get_punch_in()) { enable_record (); } } else { - deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame); + MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause)); RecordStateChanged (); /* EMIT SIGNAL */ } set_dirty(); } -nframes64_t +framepos_t Session::audible_frame () const { - nframes64_t ret; - nframes64_t tf; - nframes_t offset; + framepos_t ret; + framepos_t tf; + framecnt_t offset; /* the first of these two possible settings for "offset" mean that the audible frame is stationary until @@ -1140,9 +1194,9 @@ Session::audible_frame () const } void -Session::set_frame_rate (nframes_t frames_per_second) +Session::set_frame_rate (framecnt_t frames_per_second) { - /** \fn void Session::set_frame_size(nframes_t) + /** \fn void Session::set_frame_size(framecnt_t) the AudioEngine object that calls this guarantees that it will not be called while we are also in ::process(). Its fine to do things that block @@ -1153,7 +1207,7 @@ Session::set_frame_rate (nframes_t frames_per_second) sync_time_vars(); - Automatable::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval()))); + Automatable::set_automation_interval (ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval()))); clear_clicks (); @@ -1166,7 +1220,7 @@ Session::set_frame_rate (nframes_t frames_per_second) } void -Session::set_block_size (nframes_t nframes) +Session::set_block_size (pframes_t nframes) { /* the AudioEngine guarantees that it will not be called while we are also in @@ -1197,48 +1251,14 @@ Session::set_block_size (nframes_t nframes) } } -void -Session::set_default_fade (float /*steepness*/, float /*fade_msecs*/) -{ -#if 0 - nframes_t fade_frames; - - /* Don't allow fade of less 1 frame */ - - if (fade_msecs < (1000.0 * (1.0/_current_frame_rate))) { - - fade_msecs = 0; - fade_frames = 0; - - } else { - - fade_frames = (nframes_t) floor (fade_msecs * _current_frame_rate * 0.001); - - } - - default_fade_msecs = fade_msecs; - default_fade_steepness = steepness; - - { - // jlc, WTF is this! - Glib::RWLock::ReaderLock lm (route_lock); - AudioRegion::set_default_fade (steepness, fade_frames); - } - - set_dirty(); - - /* XXX have to do this at some point */ - /* foreach region using default fade, reset, then - refill_all_diskstream_buffers (); - */ -#endif -} - struct RouteSorter { + /** @return true to run r1 before r2, otherwise false */ bool operator() (boost::shared_ptr r1, boost::shared_ptr r2) { if (r2->feeds (r1)) { + /* r1 fed by r2; run r2 early */ return false; } else if (r1->feeds (r2)) { + /* r2 fed by r1; run r1 early */ return true; } else { if (r1->not_fed ()) { @@ -1250,16 +1270,22 @@ struct RouteSorter { return true; } } else { - return r1->order_key(N_("signal")) < r2->order_key(N_("signal")); + if (r2->not_fed()) { + /* r1 has connections, r2 does not; run r2 early */ + return false; + } else { + /* both r1 and r2 have connections, but not to each other. just use signal order */ + return r1->order_key(N_("signal")) < r2->order_key(N_("signal")); + } } } } }; static void -trace_terminal (shared_ptr r1, shared_ptr rbase) +trace_terminal (boost::shared_ptr r1, boost::shared_ptr rbase) { - shared_ptr r2; + boost::shared_ptr r2; if (r1->feeds (rbase) && rbase->feeds (r1)) { info << string_compose(_("feedback loop setup between %1 and %2"), r1->name(), rbase->name()) << endmsg; @@ -1268,7 +1294,7 @@ trace_terminal (shared_ptr r1, shared_ptr rbase) /* make a copy of the existing list of routes that feed r1 */ - Route::FedBy existing (r1->fed_by()); + Route::FedBy existing (r1->fed_by()); /* for each route that feeds r1, recurse, marking it as feeding rbase as well. @@ -1317,11 +1343,9 @@ Session::resort_routes () return; } - { - RCUWriter writer (routes); - shared_ptr r = writer.get_copy (); + boost::shared_ptr r = writer.get_copy (); resort_routes_using (r); /* writer goes out of scope and forces update */ } @@ -1329,24 +1353,24 @@ Session::resort_routes () //route_graph->dump(1); #ifndef NDEBUG - boost::shared_ptr rl = routes.reader (); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name())); + boost::shared_ptr rl = routes.reader (); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name())); - const Route::FedBy& fb ((*i)->fed_by()); - - for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) { - boost::shared_ptr sf = f->r.lock(); - if (sf) { - DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only)); - } - } - } + const Route::FedBy& fb ((*i)->fed_by()); + + for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) { + boost::shared_ptr sf = f->r.lock(); + if (sf) { + DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only)); + } + } + } #endif } void -Session::resort_routes_using (shared_ptr r) +Session::resort_routes_using (boost::shared_ptr r) { RouteList::iterator i, j; @@ -1366,7 +1390,7 @@ Session::resort_routes_using (shared_ptr r) continue; } - bool via_sends_only; + bool via_sends_only; if ((*j)->direct_feeds (*i, &via_sends_only)) { (*i)->add_fed_by (*j, via_sends_only); @@ -1381,32 +1405,41 @@ Session::resort_routes_using (shared_ptr r) RouteSorter cmp; r->sort (cmp); - route_graph->rechain( r ); + route_graph->rechain (r); #ifndef NDEBUG - DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n"); + DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n"); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n", - (*i)->name(), (*i)->order_key ("signal"))); + (*i)->name(), (*i)->order_key ("signal"))); } #endif } -/** Find the route name starting with \a base with the lowest \a id. +/** Find a route name starting with \a base, maybe followed by the + * lowest \a id. \a id will always be added if \a definitely_add_number + * is true on entry; otherwise it will only be added if required + * to make the name unique. * - * Names are constructed like e.g. "Audio 3" for base="Audio" and id=3. - * The available route name with the lowest ID will be used, and \a id - * will be set to the ID. + * Names are constructed like e.g. "Audio 3" for base="Audio" and id=3. + * The available route name with the lowest ID will be used, and \a id + * will be set to the ID. * - * \return false if a route name could not be found, and \a track_name - * and \a id do not reflect a free route name. + * \return false if a route name could not be found, and \a track_name + * and \a id do not reflect a free route name. */ bool -Session::find_route_name (const char* base, uint32_t& id, char* name, size_t name_len) +Session::find_route_name (string const & base, uint32_t& id, char* name, size_t name_len, bool definitely_add_number) { + if (!definitely_add_number && route_by_name (base) == 0) { + /* juse use the base */ + snprintf (name, name_len, "%s", base.c_str()); + return true; + } + do { - snprintf (name, name_len, "%s %" PRIu32, base, id); + snprintf (name, name_len, "%s %" PRIu32, base.c_str(), id); if (route_by_name (name) == 0) { return true; @@ -1419,22 +1452,26 @@ Session::find_route_name (const char* base, uint32_t& id, char* name, size_t nam return false; } +/** Count the total ins and outs of all non-hidden routes in the session and return them in in and out */ void Session::count_existing_route_channels (ChanCount& in, ChanCount& out) { in = ChanCount::ZERO; out = ChanCount::ZERO; - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (!(*i)->is_hidden()) { - in += (*i)->n_inputs(); - out += (*i)->n_outputs(); + in += (*i)->n_inputs(); + out += (*i)->n_outputs(); } } } +/** Caller must not hold process lock + * @param name_template string to use for the start of the name, or "" to use "Midi". + */ list > -Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many) +Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) { char track_name[32]; uint32_t track_id = 0; @@ -1450,38 +1487,39 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m control_id = ntracks() + nbusses(); while (how_many) { - if (!find_route_name ("Midi", ++track_id, track_name, sizeof(track_name))) { + if (!find_route_name (name_template.empty() ? _("Midi") : name_template, ++track_id, track_name, sizeof(track_name), false)) { error << "cannot find name for new midi track" << endmsg; goto failed; } - shared_ptr track; - + boost::shared_ptr track; + try { - MidiTrack* mt = new MidiTrack (*this, track_name, Route::Flag (0), mode); - - if (mt->init ()) { - delete mt; - goto failed; - } - - mt->use_new_diskstream(); + track.reset (new MidiTrack (*this, track_name, Route::Flag (0), mode)); - boost_debug_shared_ptr_mark_interesting (mt, "Track"); - track = boost::shared_ptr(mt); - - if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { - error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + if (track->init ()) { goto failed; } + track->use_new_diskstream(); - if (track->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { - error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; - goto failed; +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); +#endif + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) { + error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + goto failed; + } + + 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; + } } - auto_connect_route (track, existing_inputs, existing_outputs); + auto_connect_route (track.get(), existing_inputs, existing_outputs); track->non_realtime_input_change(); @@ -1519,9 +1557,15 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m return ret; } +/** Caller must hold process lock. + * @param connect_inputs true to connect inputs as well as outputs, false to connect just outputs. + * @param input_start Where to start from when auto-connecting inputs; e.g. if this is 0, auto-connect starting from input 0. + * @param output_start As \a input_start, but for outputs. + */ void -Session::auto_connect_route (boost::shared_ptr route, - ChanCount& existing_inputs, ChanCount& existing_outputs) +Session::auto_connect_route ( + Route* route, ChanCount& existing_inputs, ChanCount& existing_outputs, bool connect_inputs, ChanCount input_start, ChanCount output_start + ) { /* If both inputs and outputs are auto-connected to physical ports, use the max of input and output offsets to ensure auto-connected @@ -1530,9 +1574,11 @@ Session::auto_connect_route (boost::shared_ptr route, port number). Otherwise just use the lowest input or output offset possible. */ + const bool in_out_physical = (Config->get_input_auto_connect() & AutoConnectPhysical) - && (Config->get_output_auto_connect() & AutoConnectPhysical); + && (Config->get_output_auto_connect() & AutoConnectPhysical) + && connect_inputs; const ChanCount in_offset = in_out_physical ? ChanCount::max(existing_inputs, existing_outputs) @@ -1549,9 +1595,9 @@ Session::auto_connect_route (boost::shared_ptr route, _engine.get_physical_outputs (*t, physoutputs); _engine.get_physical_inputs (*t, physinputs); - if (!physinputs.empty()) { + if (!physinputs.empty() && connect_inputs) { uint32_t nphysical_in = physinputs.size(); - for (uint32_t i = 0; i < route->n_inputs().get(*t) && i < nphysical_in; ++i) { + for (uint32_t i = input_start.get(*t); i < route->n_inputs().get(*t) && i < nphysical_in; ++i) { string port; if (Config->get_input_auto_connect() & AutoConnectPhysical) { @@ -1567,7 +1613,7 @@ Session::auto_connect_route (boost::shared_ptr route, if (!physoutputs.empty()) { uint32_t nphysical_out = physoutputs.size(); - for (uint32_t i = 0; i < route->n_outputs().get(*t); ++i) { + for (uint32_t i = output_start.get(*t); i < route->n_outputs().get(*t); ++i) { string port; if (Config->get_output_auto_connect() & AutoConnectPhysical) { @@ -1591,8 +1637,13 @@ Session::auto_connect_route (boost::shared_ptr route, existing_outputs += route->n_outputs(); } +/** Caller must not hold process lock + * @param name_template string to use for the start of the name, or "" to use "Audio". + */ list< boost::shared_ptr > -Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many) +Session::new_audio_track ( + int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template + ) { char track_name[32]; uint32_t track_id = 0; @@ -1608,43 +1659,46 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod control_id = ntracks() + nbusses() + 1; while (how_many) { - if (!find_route_name ("Audio", ++track_id, track_name, sizeof(track_name))) { + if (!find_route_name (name_template.empty() ? _("Audio") : name_template, ++track_id, track_name, sizeof(track_name), false)) { error << "cannot find name for new audio track" << endmsg; goto failed; } - shared_ptr track; - + boost::shared_ptr track; + try { - AudioTrack* at = new AudioTrack (*this, track_name, Route::Flag (0), mode); - - if (at->init ()) { - delete at; - goto failed; - } - - at->use_new_diskstream(); - - boost_debug_shared_ptr_mark_interesting (at, "Track"); - track = boost::shared_ptr(at); + track.reset (new AudioTrack (*this, track_name, Route::Flag (0), mode)); - if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { - error << string_compose ( - _("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; + if (track->init ()) { goto failed; } - if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { - error << string_compose ( - _("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; - goto failed; - } + track->use_new_diskstream(); - auto_connect_route (track, existing_inputs, existing_outputs); +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); +#endif + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { + error << string_compose ( + _("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failed; + } + + if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { + error << string_compose ( + _("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failed; + } + + auto_connect_route (track.get(), existing_inputs, existing_outputs); + } if (route_group) { route_group->add (track); @@ -1688,15 +1742,15 @@ Session::set_remote_control_ids () RemoteModel m = Config->get_remote_model(); bool emit_signal = false; - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (MixerOrdered == m) { - long order = (*i)->order_key(N_("signal")); + int32_t order = (*i)->order_key(N_("signal")); (*i)->set_remote_control_id (order+1, false); emit_signal = true; } else if (EditorOrdered == m) { - long order = (*i)->order_key(N_("editor")); + int32_t order = (*i)->order_key(N_("editor")); (*i)->set_remote_control_id (order+1, false); emit_signal = true; } else if (UserOrdered == m) { @@ -1709,9 +1763,11 @@ Session::set_remote_control_ids () } } - +/** Caller must not hold process lock. + * @param name_template string to use for the start of the name, or "" to use "Bus". + */ RouteList -Session::new_audio_route (bool aux, int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many) +Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template) { char bus_name[32]; uint32_t bus_id = 0; @@ -1726,38 +1782,41 @@ Session::new_audio_route (bool aux, int input_channels, int output_channels, Rou control_id = ntracks() + nbusses() + 1; while (how_many) { - if (!find_route_name ("Bus", ++bus_id, bus_name, sizeof(bus_name))) { + if (!find_route_name (name_template.empty () ? _("Bus") : name_template, ++bus_id, bus_name, sizeof(bus_name), false)) { error << "cannot find name for new audio bus" << endmsg; goto failure; } try { - Route* rt = new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO); + boost::shared_ptr bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); - if (rt->init ()) { - delete rt; - goto failure; - } - - boost_debug_shared_ptr_mark_interesting (rt, "Route"); - shared_ptr bus (rt); - - if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { - error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), - input_channels, output_channels) - << endmsg; + if (bus->init ()) { goto failure; } +#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS + boost_debug_shared_ptr_mark_interesting (bus.get(), "Route"); +#endif + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - if (bus->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; - } + if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failure; + } + + + if (bus->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) { + error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), + input_channels, output_channels) + << endmsg; + goto failure; + } - auto_connect_route (bus, existing_inputs, existing_outputs); + auto_connect_route (bus.get(), existing_inputs, existing_outputs, false); + } if (route_group) { route_group->add (bus); @@ -1765,9 +1824,7 @@ Session::new_audio_route (bool aux, int input_channels, int output_channels, Rou bus->set_remote_control_id (control_id); ++control_id; - if (aux) { - bus->add_internal_return (); - } + bus->add_internal_return (); ret.push_back (bus); } @@ -1820,7 +1877,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template std::string node_name = IO::name_from_state (*node_copy.children().front()); /* generate a new name by adding a number to the end of the template name */ - if (!find_route_name (node_name.c_str(), ++number, name, sizeof(name))) { + if (!find_route_name (node_name.c_str(), ++number, name, sizeof(name), true)) { fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; /*NOTREACHED*/ } @@ -1836,7 +1893,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template Track::zero_diskstream_id_in_xml (node_copy); try { - shared_ptr route (XMLRouteFactory (node_copy, 3000)); + boost::shared_ptr route (XMLRouteFactory (node_copy, 3000)); if (route == 0) { error << _("Session: cannot create track/bus from template description") << endmsg; @@ -1848,8 +1905,14 @@ 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); + + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + IOChange change (IOChange::Type (IOChange::ConfigurationChanged | IOChange::ConnectionsChanged)); + change.after = route->input()->n_ports(); + route->input()->changed (change, this); + change.after = route->output()->n_ports(); + route->output()->changed (change, this); } route->set_remote_control_id (control_id); @@ -1884,7 +1947,7 @@ Session::add_routes (RouteList& new_routes, bool save) { { RCUWriter writer (routes); - shared_ptr r = writer.get_copy (); + boost::shared_ptr r = writer.get_copy (); r->insert (r->end(), new_routes.begin(), new_routes.end()); @@ -1910,7 +1973,7 @@ Session::add_routes (RouteList& new_routes, bool save) 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)); + r->order_key_changed.connect_same_thread (*this, boost::bind (&Session::route_order_key_changed, this)); if (r->is_master()) { _master_out = r; @@ -1925,6 +1988,11 @@ Session::add_routes (RouteList& new_routes, bool save) tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr (tr))); track_playlist_changed (boost::weak_ptr (tr)); tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_track, this)); + + boost::shared_ptr mt = boost::dynamic_pointer_cast (tr); + if (mt) { + mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1)); + } } } @@ -1932,14 +2000,12 @@ Session::add_routes (RouteList& new_routes, bool save) for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { if ((*x)->is_monitor()) { - /* relax */ - } else if ((*x)->is_master()) { - /* relax */ + /* relax */ + } else if ((*x)->is_master()) { + /* relax */ } else { - (*x)->listen_via (_monitor_out, - (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader), - false, false); - } + (*x)->listen_via_monitor (); + } } resort_routes (); @@ -1961,13 +2027,9 @@ Session::globally_set_send_gains_to_zero (boost::shared_ptr dest) boost::shared_ptr r = routes.reader (); boost::shared_ptr s; - /* only tracks */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i)) { - if ((s = (*i)->internal_send_for (dest)) != 0) { - s->amp()->gain_control()->set_value (0.0); - } + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value (0.0); } } } @@ -1978,13 +2040,9 @@ Session::globally_set_send_gains_to_unity (boost::shared_ptr dest) boost::shared_ptr r = routes.reader (); boost::shared_ptr s; - /* only tracks */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i)) { - if ((s = (*i)->internal_send_for (dest)) != 0) { - s->amp()->gain_control()->set_value (1.0); - } + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value (1.0); } } } @@ -1995,27 +2053,22 @@ Session::globally_set_send_gains_from_track(boost::shared_ptr dest) boost::shared_ptr r = routes.reader (); boost::shared_ptr s; - /* only tracks */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i)) { - if ((s = (*i)->internal_send_for (dest)) != 0) { - s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value()); - } + if ((s = (*i)->internal_send_for (dest)) != 0) { + s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value()); } } } +/** @param include_buses true to add sends to buses and tracks, false for just tracks */ void -Session::globally_add_internal_sends (boost::shared_ptr dest, Placement p) +Session::globally_add_internal_sends (boost::shared_ptr dest, Placement p, bool include_buses) { boost::shared_ptr r = routes.reader (); boost::shared_ptr t (new RouteList); - /* only send tracks */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (boost::dynamic_pointer_cast(*i)) { + if (include_buses || boost::dynamic_pointer_cast(*i)) { t->push_back (*i); } } @@ -2040,22 +2093,24 @@ Session::add_internal_sends (boost::shared_ptr dest, Placement p, boost:: continue; } - (*i)->listen_via (dest, p, true, true); + (*i)->listen_via (dest, p); } graph_reordered (); } void -Session::remove_route (shared_ptr route) +Session::remove_route (boost::shared_ptr route) { - if (((route == _master_out) || (route == _monitor_out)) && !Config->get_allow_special_bus_removal()) { - return; - } + if (((route == _master_out) || (route == _monitor_out)) && !Config->get_allow_special_bus_removal()) { + return; + } + + route->set_solo (false, this); { RCUWriter writer (routes); - shared_ptr rs = writer.get_copy (); + boost::shared_ptr rs = writer.get_copy (); rs->remove (route); @@ -2065,7 +2120,7 @@ Session::remove_route (shared_ptr route) */ if (route == _master_out) { - _master_out = shared_ptr (); + _master_out = boost::shared_ptr (); } if (route == _monitor_out) { @@ -2081,9 +2136,8 @@ Session::remove_route (shared_ptr route) /* writer goes out of scope, forces route list update */ } - - update_route_solo_state (); - update_session_range_location_marker (); + + update_route_solo_state (); // We need to disconnect the route's inputs and outputs @@ -2102,9 +2156,23 @@ Session::remove_route (shared_ptr route) } } + boost::shared_ptr mt = boost::dynamic_pointer_cast (route); + if (mt && mt->step_editing()) { + if (_step_editors > 0) { + _step_editors--; + } + } + update_latency_compensation (false, false); set_dirty(); + /* Re-sort routes to remove the graph's current references to the one that is + * going away, then flush old references out of the graph. + */ + + resort_routes (); + route_graph->clear_other_chain (); + /* get rid of it from the dead wood collection in the route list manager */ /* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */ @@ -2141,18 +2209,18 @@ Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) return; } - if (route->listening()) { + if (route->listening_via_monitor ()) { - if (Config->get_exclusive_solo()) { - /* new listen: disable all other listen */ - shared_ptr r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { - continue; - } - (*i)->set_listen (false, this); - } - } + if (Config->get_exclusive_solo()) { + /* new listen: disable all other listen */ + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { + continue; + } + (*i)->set_listen (false, this); + } + } _listen_cnt++; @@ -2160,6 +2228,8 @@ Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) _listen_cnt--; } + + update_route_solo_state (); } void Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr wpr) @@ -2175,29 +2245,29 @@ Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr wpr) bool send_changed = false; if (route->solo_isolated()) { - if (_solo_isolated_cnt == 0) { - send_changed = true; - } - _solo_isolated_cnt++; + if (_solo_isolated_cnt == 0) { + send_changed = true; + } + _solo_isolated_cnt++; } else if (_solo_isolated_cnt > 0) { - _solo_isolated_cnt--; - if (_solo_isolated_cnt == 0) { - send_changed = true; - } + _solo_isolated_cnt--; + if (_solo_isolated_cnt == 0) { + send_changed = true; + } } if (send_changed) { - IsolatedChanged (); /* EMIT SIGNAL */ + IsolatedChanged (); /* EMIT SIGNAL */ } } void Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_ptr wpr) { - if (!self_solo_change) { - // session doesn't care about changes to soloed-by-others - return; - } + if (!self_solo_change) { + // session doesn't care about changes to soloed-by-others + return; + } if (solo_update_disabled) { // We know already @@ -2212,7 +2282,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p return; } - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); int32_t delta; if (route->self_soloed()) { @@ -2221,59 +2291,59 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p delta = -1; } - if (delta == 1 && Config->get_exclusive_solo()) { - /* new solo: disable all other solos */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { - continue; - } - (*i)->set_solo (false, this); - } - } + if (delta == 1 && Config->get_exclusive_solo()) { + /* new solo: disable all other solos */ + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { + continue; + } + (*i)->set_solo (false, this); + } + } solo_update_disabled = true; - RouteList uninvolved; + RouteList uninvolved; for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { bool via_sends_only; - bool in_signal_flow; + bool in_signal_flow; if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { continue; } - in_signal_flow = false; + in_signal_flow = false; - if ((*i)->feeds (route, &via_sends_only)) { + if ((*i)->feeds (route, &via_sends_only)) { if (!via_sends_only) { - if (!route->soloed_by_others_upstream()) { - (*i)->mod_solo_by_others_downstream (delta); - } - in_signal_flow = true; + if (!route->soloed_by_others_upstream()) { + (*i)->mod_solo_by_others_downstream (delta); + } + in_signal_flow = true; } } - if (route->feeds (*i, &via_sends_only)) { - (*i)->mod_solo_by_others_upstream (delta); - in_signal_flow = true; - } + if (route->feeds (*i, &via_sends_only)) { + (*i)->mod_solo_by_others_upstream (delta); + in_signal_flow = true; + } - if (!in_signal_flow) { - uninvolved.push_back (*i); - } + if (!in_signal_flow) { + uninvolved.push_back (*i); + } } solo_update_disabled = false; update_route_solo_state (r); - /* now notify that the mute state of the routes not involved in the signal - pathway of the just-solo-changed route may have altered. - */ + /* now notify that the mute state of the routes not involved in the signal + pathway of the just-solo-changed route may have altered. + */ - for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { - (*i)->mute_changed (this); - } + for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { + (*i)->mute_changed (this); + } SoloChanged (); /* EMIT SIGNAL */ set_dirty(); @@ -2285,8 +2355,8 @@ Session::update_route_solo_state (boost::shared_ptr r) /* now figure out if anything that matters is soloed (or is "listening")*/ bool something_soloed = false; - uint32_t listeners = 0; - uint32_t isolated = 0; + uint32_t listeners = 0; + uint32_t isolated = 0; if (!r) { r = routes.reader(); @@ -2297,36 +2367,36 @@ Session::update_route_solo_state (boost::shared_ptr r) something_soloed = true; } - if (!(*i)->is_hidden() && (*i)->listening()) { - if (Config->get_solo_control_is_listen_control()) { - listeners++; - } else { - (*i)->set_listen (false, this); - } - } + if (!(*i)->is_hidden() && (*i)->listening_via_monitor()) { + if (Config->get_solo_control_is_listen_control()) { + listeners++; + } else { + (*i)->set_listen (false, this); + } + } - if ((*i)->solo_isolated()) { - isolated++; - } + if ((*i)->solo_isolated()) { + isolated++; + } } - if (something_soloed != _non_soloed_outs_muted) { - _non_soloed_outs_muted = something_soloed; - SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */ - } + if (something_soloed != _non_soloed_outs_muted) { + _non_soloed_outs_muted = something_soloed; + SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */ + } - _listen_cnt = listeners; + _listen_cnt = listeners; - if (isolated != _solo_isolated_cnt) { - _solo_isolated_cnt = isolated; - IsolatedChanged (); /* EMIT SIGNAL */ - } + if (isolated != _solo_isolated_cnt) { + _solo_isolated_cnt = isolated; + IsolatedChanged (); /* EMIT SIGNAL */ + } } boost::shared_ptr Session::get_routes_with_internal_returns() const { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); boost::shared_ptr rl (new RouteList); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { @@ -2340,25 +2410,25 @@ Session::get_routes_with_internal_returns() const bool Session::io_name_is_legal (const std::string& name) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if ((*i)->name() == name) { - return false; - } + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if ((*i)->name() == name) { + return false; + } - if ((*i)->has_io_processor_named (name)) { - return false; - } - } + if ((*i)->has_io_processor_named (name)) { + return false; + } + } - return true; + return true; } -shared_ptr +boost::shared_ptr Session::route_by_name (string name) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == name) { @@ -2366,13 +2436,13 @@ Session::route_by_name (string name) } } - return shared_ptr ((Route*) 0); + return boost::shared_ptr ((Route*) 0); } -shared_ptr +boost::shared_ptr Session::route_by_id (PBD::ID id) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->id() == id) { @@ -2380,13 +2450,13 @@ Session::route_by_id (PBD::ID id) } } - return shared_ptr ((Route*) 0); + return boost::shared_ptr ((Route*) 0); } -shared_ptr +boost::shared_ptr Session::route_by_remote_id (uint32_t id) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->remote_control_id() == id) { @@ -2394,67 +2464,80 @@ Session::route_by_remote_id (uint32_t id) } } - return shared_ptr ((Route*) 0); + return boost::shared_ptr ((Route*) 0); } -/** If either end of the session range location marker lies inside the current - * session extent, move it to the corresponding session extent. +void +Session::playlist_region_added (boost::weak_ptr w) +{ + boost::shared_ptr r = w.lock (); + if (!r) { + return; + } + + /* These are the operations that are currently in progress... */ + list curr = _current_trans_quarks; + curr.sort (); + + /* ...and these are the operations during which we want to update + the session range location markers. + */ + list ops; + ops.push_back (Operations::capture); + ops.push_back (Operations::paste); + ops.push_back (Operations::duplicate_region); + ops.push_back (Operations::insert_file); + ops.push_back (Operations::insert_region); + ops.push_back (Operations::drag_region_brush); + ops.push_back (Operations::region_drag); + ops.push_back (Operations::selection_grab); + ops.push_back (Operations::region_fill); + ops.push_back (Operations::fill_selection); + ops.push_back (Operations::create_region); + ops.sort (); + + /* See if any of the current operations match the ones that we want */ + list in; + set_intersection (_current_trans_quarks.begin(), _current_trans_quarks.end(), ops.begin(), ops.end(), back_inserter (in)); + + /* If so, update the session range markers */ + if (!in.empty ()) { + maybe_update_session_range (r->position (), r->last_frame ()); + } +} + +/** Update the session range markers if a is before the current start or + * b is after the current end. */ void -Session::update_session_range_location_marker () +Session::maybe_update_session_range (framepos_t a, framepos_t b) { if (_state_of_the_state & Loading) { return; } - pair const ext = get_extent (); - if (_session_range_location == 0) { - /* we don't have a session range yet; use this one (provided it is valid) */ - if (ext.first != max_frames) { - add_session_range_location (ext.first, ext.second); - } + + add_session_range_location (a, b); + } else { - /* update the existing session range */ - if (ext.first < _session_range_location->start()) { - _session_range_location->set_start (ext.first); - set_dirty (); - } - if (ext.second > _session_range_location->end()) { - _session_range_location->set_end (ext.second); - set_dirty (); + if (a < _session_range_location->start()) { + _session_range_location->set_start (a); } + if (b > _session_range_location->end()) { + _session_range_location->set_end (b); + } } } -/** @return Extent of the session's contents; if the session is empty, the first value of - * the pair will equal max_frames. - */ -pair -Session::get_extent () const +void +Session::playlist_ranges_moved (list > const & ranges) { - pair ext (max_frames, 0); - - boost::shared_ptr rl = routes.reader (); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (!tr || tr->destructive()) { - // ignore tape tracks when getting extents - continue; - } - - pair e = tr->playlist()->get_extent (); - if (e.first < ext.first) { - ext.first = e.first; - } - if (e.second > ext.second) { - ext.second = e.second; - } + for (list >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) { + maybe_update_session_range (i->to, i->to + i->length); } - - return ext; } /* Region management */ @@ -2462,7 +2545,7 @@ Session::get_extent () const boost::shared_ptr Session::find_whole_file_parent (boost::shared_ptr child) const { - const RegionFactory::RegionMap& regions (RegionFactory::regions()); + const RegionFactory::RegionMap& regions (RegionFactory::regions()); RegionFactory::RegionMap::const_iterator i; boost::shared_ptr region; @@ -2486,48 +2569,48 @@ Session::find_whole_file_parent (boost::shared_ptr child) const int Session::destroy_sources (list > srcs) { - set > relevant_regions; + set > relevant_regions; for (list >::iterator s = srcs.begin(); s != srcs.end(); ++s) { - RegionFactory::get_regions_using_source (*s, relevant_regions); + RegionFactory::get_regions_using_source (*s, relevant_regions); } - cerr << "There are " << relevant_regions.size() << " using " << srcs.size() << " sources" << endl; + cerr << "There are " << relevant_regions.size() << " using " << srcs.size() << " sources" << endl; - for (set >::iterator r = relevant_regions.begin(); r != relevant_regions.end(); ) { - set >::iterator tmp; + for (set >::iterator r = relevant_regions.begin(); r != relevant_regions.end(); ) { + set >::iterator tmp; - tmp = r; - ++tmp; + tmp = r; + ++tmp; - cerr << "Cleanup " << (*r)->name() << " UC = " << (*r).use_count() << endl; + cerr << "Cleanup " << (*r)->name() << " UC = " << (*r).use_count() << endl; - playlists->destroy_region (*r); - RegionFactory::map_remove (*r); + playlists->destroy_region (*r); + RegionFactory::map_remove (*r); - (*r)->drop_sources (); - (*r)->drop_references (); + (*r)->drop_sources (); + (*r)->drop_references (); - cerr << "\tdone UC = " << (*r).use_count() << endl; + cerr << "\tdone UC = " << (*r).use_count() << endl; - relevant_regions.erase (r); + relevant_regions.erase (r); - r = tmp; - } + r = tmp; + } for (list >::iterator s = srcs.begin(); s != srcs.end(); ) { - { - Glib::Mutex::Lock ls (source_lock); - /* remove from the main source list */ - sources.erase ((*s)->id()); - } + { + Glib::Mutex::Lock ls (source_lock); + /* remove from the main source list */ + sources.erase ((*s)->id()); + } - (*s)->mark_for_remove (); - (*s)->drop_references (); + (*s)->mark_for_remove (); + (*s)->drop_references (); - s = srcs.erase (s); - } + s = srcs.erase (s); + } return 0; } @@ -2577,17 +2660,17 @@ Session::add_source (boost::shared_ptr source) if (result.second) { - /* yay, new source */ + /* yay, new source */ set_dirty(); - boost::shared_ptr afs; + boost::shared_ptr afs; - if ((afs = boost::dynamic_pointer_cast(source)) != 0) { - if (Config->get_auto_analyse_audio()) { - Analyser::queue_source_for_analysis (source, false); - } - } + if ((afs = boost::dynamic_pointer_cast(source)) != 0) { + if (Config->get_auto_analyse_audio()) { + Analyser::queue_source_for_analysis (source, false); + } + } } } @@ -2605,6 +2688,7 @@ Session::remove_source (boost::weak_ptr src) Glib::Mutex::Lock lm (source_lock); if ((i = sources.find (source->id())) != sources.end()) { + cerr << "Removing source " << source->name() << endl; sources.erase (i); } } @@ -2634,12 +2718,11 @@ Session::source_by_id (const PBD::ID& id) } boost::shared_ptr -Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn) +Session::source_by_path_and_channel (const string& path, uint16_t chn) { Glib::Mutex::Lock lm (source_lock); for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) { - cerr << "comparing " << path << " with " << i->second->name() << endl; boost::shared_ptr afs = boost::dynamic_pointer_cast(i->second); @@ -2650,6 +2733,24 @@ Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn) return boost::shared_ptr(); } +uint32_t +Session::count_sources_by_origin (const string& path) +{ + uint32_t cnt = 0; + Glib::Mutex::Lock lm (source_lock); + + for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) { + boost::shared_ptr fs + = boost::dynamic_pointer_cast(i->second); + + if (fs && fs->origin() == path) { + ++cnt; + } + } + + return cnt; +} + string Session::change_source_path_by_name (string path, string oldname, string newname, bool destructive) @@ -2669,18 +2770,12 @@ Session::change_source_path_by_name (string path, string oldname, string newname the task here is to replace NAME with the new name. */ - /* find last slash */ - string dir; string prefix; - string::size_type slash; string::size_type dash; - if ((slash = path.find_last_of ('/')) == string::npos) { - return ""; - } - - dir = path.substr (0, slash+1); + dir = Glib::path_get_dirname (path); + path = Glib::path_get_basename (path); /* '-' is not a legal character for the NAME part of the path */ @@ -2688,13 +2783,13 @@ Session::change_source_path_by_name (string path, string oldname, string newname return ""; } - prefix = path.substr (slash+1, dash-(slash+1)); + prefix = path.substr (0, dash); - path = dir; path += prefix; path += '-'; path += new_legalized; - path += ".wav"; /* XXX gag me with a spoon */ + path += native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); + path = Glib::build_filename (dir, path); } else { @@ -2707,17 +2802,11 @@ Session::change_source_path_by_name (string path, string oldname, string newname string dir; string suffix; - string::size_type slash; string::size_type dash; string::size_type postfix; - /* find last slash */ - - if ((slash = path.find_last_of ('/')) == string::npos) { - return ""; - } - - dir = path.substr (0, slash+1); + dir = Glib::path_get_dirname (path); + path = Glib::path_get_basename (path); /* '-' is not a legal character for the NAME part of the path */ @@ -2747,19 +2836,21 @@ Session::change_source_path_by_name (string path, string oldname, string newname for (uint32_t cnt = 1; cnt <= limit; ++cnt) { - snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str()); + snprintf (buf, sizeof(buf), "%s-%u%s", newname.c_str(), cnt, suffix.c_str()); - if (access (buf, F_OK) != 0) { - path = buf; + if (!matching_unsuffixed_filename_exists_in (dir, buf)) { + path = Glib::build_filename (dir, buf); break; } + path = ""; } - if (path == "") { - error << "FATAL ERROR! Could not find a " << endl; + if (path.empty()) { + fatal << string_compose (_("FATAL ERROR! Could not find a suitable version of %1 for a rename"), + newname) << endl; + /*NOTREACHED*/ } - } return path; @@ -2770,7 +2861,7 @@ Session::change_source_path_by_name (string path, string oldname, string newname * (e.g. as returned by new_*_source_name) */ string -Session::new_source_path_from_name (DataType type, const string& name) +Session::new_source_path_from_name (DataType type, const string& name, bool as_stub) { assert(name.find("/") == string::npos); @@ -2778,9 +2869,9 @@ Session::new_source_path_from_name (DataType type, const string& name) sys::path p; if (type == DataType::AUDIO) { - p = sdir.sound_path(); + p = (as_stub ? sdir.sound_stub_path() : sdir.sound_path()); } else if (type == DataType::MIDI) { - p = sdir.midi_path(); + p = (as_stub ? sdir.midi_stub_path() : sdir.midi_path()); } else { error << "Unknown source type, unable to create file path" << endmsg; return ""; @@ -2790,11 +2881,11 @@ Session::new_source_path_from_name (DataType type, const string& name) return p.to_string(); } -Glib::ustring -Session::peak_path (Glib::ustring base) const +string +Session::peak_path (string base) const { sys::path peakfile_path(_session_dir->peak_path()); - peakfile_path /= basename_nosuffix (base) + peakfile_suffix; + peakfile_path /= base + peakfile_suffix; return peakfile_path.to_string(); } @@ -2802,11 +2893,11 @@ Session::peak_path (Glib::ustring base) const string Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t chan, bool destructive) { - string spath; uint32_t cnt; char buf[PATH_MAX+1]; const uint32_t limit = 10000; string legalized; + string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); buf[0] = '\0'; legalized = legalize_for_path (base); @@ -2819,55 +2910,60 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { - SessionDirectory sdir((*i).path); - - spath = sdir.sound_path().to_string(); - if (destructive) { if (nchan < 2) { - snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", - spath.c_str(), cnt, legalized.c_str()); + snprintf (buf, sizeof(buf), "T%04d-%s%s", + cnt, legalized.c_str(), ext.c_str()); } else if (nchan == 2) { if (chan == 0) { - snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav", - spath.c_str(), cnt, legalized.c_str()); + snprintf (buf, sizeof(buf), "T%04d-%s%%L%s", + cnt, legalized.c_str(), ext.c_str()); } else { - snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav", - spath.c_str(), cnt, legalized.c_str()); + snprintf (buf, sizeof(buf), "T%04d-%s%%R%s", + cnt, legalized.c_str(), ext.c_str()); } } else if (nchan < 26) { - snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav", - spath.c_str(), cnt, legalized.c_str(), 'a' + chan); + snprintf (buf, sizeof(buf), "T%04d-%s%%%c%s", + cnt, legalized.c_str(), 'a' + chan, ext.c_str()); } else { - snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", - spath.c_str(), cnt, legalized.c_str()); + snprintf (buf, sizeof(buf), "T%04d-%s%s", + cnt, legalized.c_str(), ext.c_str()); } } else { - spath += '/'; - spath += legalized; - if (nchan < 2) { - snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); + snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str()); } else if (nchan == 2) { if (chan == 0) { - snprintf (buf, sizeof(buf), "%s-%u%%L.wav", spath.c_str(), cnt); + snprintf (buf, sizeof(buf), "%s-%u%%L%s", legalized.c_str(), cnt, ext.c_str()); } else { - snprintf (buf, sizeof(buf), "%s-%u%%R.wav", spath.c_str(), cnt); + snprintf (buf, sizeof(buf), "%s-%u%%R%s", legalized.c_str(), cnt, ext.c_str()); } } else if (nchan < 26) { - snprintf (buf, sizeof(buf), "%s-%u%%%c.wav", spath.c_str(), cnt, 'a' + chan); + snprintf (buf, sizeof(buf), "%s-%u%%%c%s", legalized.c_str(), cnt, 'a' + chan, ext.c_str()); } else { - snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt); + snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str()); } } - if (sys::exists(buf)) { + SessionDirectory sdir((*i).path); + + string spath = sdir.sound_path().to_string(); + string spath_stubs = sdir.sound_stub_path().to_string(); + + /* note that we search *without* the extension so that + we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf" + in the event that this new name is required for + a file format change. + */ + + if (matching_unsuffixed_filename_exists_in (spath, buf) || + matching_unsuffixed_filename_exists_in (spath_stubs, buf)) { existing++; + break; } - } if (existing == 0) { @@ -2882,19 +2978,19 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha throw failed_constructor(); } } - - return Glib::path_get_basename(buf); + + return Glib::path_get_basename (buf); } /** Create a new within-session audio source */ boost::shared_ptr -Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive) +Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive, bool as_stub) { const string name = new_audio_source_name (n, n_chans, chan, destructive); - const string path = new_source_path_from_name(DataType::AUDIO, name); + const string path = new_source_path_from_name(DataType::AUDIO, name, as_stub); return boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, path, string(), destructive, frame_rate())); } /** Return a unique name based on \a base for a new internal MIDI source */ @@ -2948,14 +3044,30 @@ Session::new_midi_source_name (const string& base) /** Create a new within-session MIDI source */ boost::shared_ptr -Session::create_midi_source_for_session (string const & n) +Session::create_midi_source_for_session (Track* track, string const & n, bool as_stub) { + /* try to use the existing write source for the track, to keep numbering sane + */ + + if (track) { + /*MidiTrack* mt = dynamic_cast (track); + assert (mt); + */ + + list > l = track->steal_write_sources (); + + if (!l.empty()) { + assert (boost::dynamic_pointer_cast (l.front())); + return boost::dynamic_pointer_cast (l.front()); + } + } + const string name = new_midi_source_name (n); - const string path = new_source_path_from_name (DataType::MIDI, name); + const string path = new_source_path_from_name (DataType::MIDI, name, as_stub); return boost::dynamic_pointer_cast ( - SourceFactory::createWritable ( - DataType::MIDI, *this, path, false, frame_rate())); + SourceFactory::createWritable ( + DataType::MIDI, *this, path, string(), false, frame_rate())); } @@ -3041,49 +3153,13 @@ Session::cancel_audition () bool Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost::shared_ptr b) { - if (a->is_monitor()) { - return true; - } - if (b->is_monitor()) { - return false; - } - return a->order_key(N_("signal")) < b->order_key(N_("signal")); -} - -void -Session::remove_empty_sounds () -{ - vector audio_filenames; - - get_files_in_directory (_session_dir->sound_path(), audio_filenames); - - Glib::Mutex::Lock lm (source_lock); - - TapeFileMatcher tape_file_matcher; - - remove_if (audio_filenames.begin(), audio_filenames.end(), - 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()); - - audio_file_path /= *i; - - if (AudioFileSource::is_empty (*this, audio_file_path.to_string())) { - - try - { - sys::remove (audio_file_path); - const string peakfile = peak_path (audio_file_path.to_string()); - sys::remove (peakfile); - } - catch (const sys::filesystem_error& err) - { - error << err.what() << endmsg; - } - } + if (a->is_monitor()) { + return true; } + if (b->is_monitor()) { + return false; + } + return a->order_key(N_("signal")) < b->order_key(N_("signal")); } bool @@ -3129,7 +3205,7 @@ Session::graph_reordered () } } -nframes_t +framecnt_t Session::available_capture_duration () { float sample_bytes_on_disk = 4.0; // keep gcc happy @@ -3157,15 +3233,15 @@ Session::available_capture_duration () double scale = 4096.0 / sample_bytes_on_disk; - if (_total_free_4k_blocks * scale > (double) max_frames) { - return max_frames; + if (_total_free_4k_blocks * scale > (double) max_framecnt) { + return max_framecnt; } - - return (nframes_t) floor (_total_free_4k_blocks * scale); + + return (framecnt_t) floor (_total_free_4k_blocks * scale); } void -Session::add_bundle (shared_ptr bundle) +Session::add_bundle (boost::shared_ptr bundle) { { RCUWriter writer (_bundles); @@ -3179,7 +3255,7 @@ Session::add_bundle (shared_ptr bundle) } void -Session::remove_bundle (shared_ptr bundle) +Session::remove_bundle (boost::shared_ptr bundle) { bool removed = false; @@ -3201,7 +3277,7 @@ Session::remove_bundle (shared_ptr bundle) set_dirty(); } -shared_ptr +boost::shared_ptr Session::bundle_by_name (string name) const { boost::shared_ptr b = _bundles.reader (); @@ -3222,16 +3298,26 @@ Session::tempo_map_changed (const PropertyChange&) playlists->update_after_tempo_map_change (); + _locations->apply (*this, &Session::update_locations_after_tempo_map_change); + set_dirty (); } +void +Session::update_locations_after_tempo_map_change (Locations::LocationList& loc) +{ + for (Locations::LocationList::iterator i = loc.begin(); i != loc.end(); ++i) { + (*i)->recompute_frames_from_bbt (); + } +} + /** Ensures that all buffers (scratch, send, silent, etc) are allocated for * the given count with the current block size. */ void Session::ensure_buffers (ChanCount howmany) { - BufferManager::ensure_buffers (howmany); + BufferManager::ensure_buffers (howmany); } void @@ -3342,24 +3428,24 @@ void Session::unmark_send_id (uint32_t id) { if (id < send_bitset.size()) { - send_bitset[id] = false; - } + send_bitset[id] = false; + } } void Session::unmark_return_id (uint32_t id) { if (id < return_bitset.size()) { - return_bitset[id] = false; - } + return_bitset[id] = false; + } } void Session::unmark_insert_id (uint32_t id) { if (id < insert_bitset.size()) { - insert_bitset[id] = false; - } + insert_bitset[id] = false; + } } @@ -3419,7 +3505,12 @@ Session::reset_native_file_format () for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); if (tr) { + /* don't save state as we do this, there's no point + */ + + _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup); tr->reset_write_sources (false); + _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup); } } } @@ -3427,7 +3518,7 @@ Session::reset_native_file_format () bool Session::route_name_unique (string n) const { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->name() == n) { @@ -3455,7 +3546,7 @@ Session::route_name_internal (string n) const int Session::freeze_all (InterThreadInfo& itt) { - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { @@ -3473,7 +3564,7 @@ Session::freeze_all (InterThreadInfo& itt) } boost::shared_ptr -Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, +Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, bool /*overwrite*/, vector >& srcs, InterThreadInfo& itt, bool enable_processing) { @@ -3482,14 +3573,17 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, boost::shared_ptr fsource; uint32_t x; char buf[PATH_MAX+1]; - ChanCount nchans(track.n_channels()); - nframes_t position; - nframes_t this_chunk; - nframes_t to_do; + ChanCount diskstream_channels (track.n_channels()); + framepos_t position; + framecnt_t this_chunk; + framepos_t to_do; BufferSet buffers; SessionDirectory sdir(get_best_session_directory_for_new_source ()); const string sound_dir = sdir.sound_path().to_string(); - nframes_t len = end - start; + framepos_t len = end - start; + bool need_block_size_reset = false; + string ext; + ChanCount const max_proc = track.max_processor_streams (); if (end <= start) { error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"), @@ -3497,7 +3591,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, return result; } - const nframes_t chunk_size = (256 * 1024)/4; + const framecnt_t chunk_size = (256 * 1024)/4; // block all process callback handling @@ -3515,10 +3609,12 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, goto out; } - for (uint32_t chan_n=0; chan_n < nchans.n_audio(); ++chan_n) { + ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); + + for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) { for (x = 0; x < 99999; ++x) { - snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 ".wav", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1); + snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 "%s", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1, ext.c_str()); if (access (buf, F_OK) != 0) { break; } @@ -3531,7 +3627,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, false, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, buf, string(), false, frame_rate())); } catch (failed_constructor& err) { @@ -3542,16 +3638,21 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, srcs.push_back (fsource); } + /* tell redirects that care that we are about to use a much larger blocksize */ + + need_block_size_reset = true; + track.set_block_size (chunk_size); + /* XXX need to flush all redirects */ position = start; to_do = len; /* create a set of reasonably-sized buffers */ - buffers.ensure_buffers(DataType::AUDIO, nchans.n_audio(), chunk_size); - buffers.set_count(nchans); + buffers.ensure_buffers (DataType::AUDIO, max_proc.n_audio(), chunk_size); + buffers.set_count (max_proc); - for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src) { + for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); if (afs) afs->prepare_for_peakfile_writes (); @@ -3632,6 +3733,11 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, } } + + if (need_block_size_reset) { + track.set_block_size (get_block_size()); + } + unblock_processing (); return result; @@ -3640,19 +3746,19 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, gain_t* Session::gain_automation_buffer() const { - return ProcessThread::gain_automation_buffer (); + return ProcessThread::gain_automation_buffer (); } pan_t** Session::pan_automation_buffer() const { - return ProcessThread::pan_automation_buffer (); + return ProcessThread::pan_automation_buffer (); } BufferSet& Session::get_silent_buffers (ChanCount count) { - return ProcessThread::get_silent_buffers (count); + return ProcessThread::get_silent_buffers (count); #if 0 assert(_silent_buffers->available() >= count); _silent_buffers->set_count(count); @@ -3670,7 +3776,7 @@ Session::get_silent_buffers (ChanCount count) BufferSet& Session::get_scratch_buffers (ChanCount count) { - return ProcessThread::get_scratch_buffers (count); + return ProcessThread::get_scratch_buffers (count); #if 0 if (count != ChanCount::ZERO) { assert(_scratch_buffers->available() >= count); @@ -3686,7 +3792,7 @@ Session::get_scratch_buffers (ChanCount count) BufferSet& Session::get_mix_buffers (ChanCount count) { - return ProcessThread::get_mix_buffers (count); + return ProcessThread::get_mix_buffers (count); #if 0 assert(_mix_buffers->available() >= count); _mix_buffers->set_count(count); @@ -3698,7 +3804,7 @@ uint32_t Session::ntracks () const { uint32_t n = 0; - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { @@ -3713,7 +3819,7 @@ uint32_t Session::nbusses () const { uint32_t n = 0; - shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader (); for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { if (boost::dynamic_pointer_cast(*i) == 0) { @@ -3790,22 +3896,10 @@ Session::update_have_rec_enabled_track () void Session::listen_position_changed () { - Placement p; - - switch (Config->get_listen_position()) { - case AfterFaderListen: - p = PostFader; - break; - - case PreFaderListen: - p = PreFader; - break; - } - boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->put_monitor_send_at (p); + (*i)->listen_position_changed (); } } @@ -3814,13 +3908,14 @@ Session::solo_control_mode_changed () { /* cancel all solo or all listen when solo control mode changes */ - if (soloing()) { - set_solo (get_routes(), false); - } else if (listening()) { - set_listen (get_routes(), false); - } + if (soloing()) { + set_solo (get_routes(), false); + } else if (listening()) { + set_listen (get_routes(), false); + } } +/** Called when anything about any of our route groups changes (membership, state etc.) */ void Session::route_group_changed () { @@ -3833,23 +3928,17 @@ 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); - } + ret.push_back (MTC); + ret.push_back (MIDIClock); return ret; } boost::shared_ptr -Session::get_routes_with_regions_at (nframes64_t const p) const +Session::get_routes_with_regions_at (framepos_t const p) const { - shared_ptr r = routes.reader (); - shared_ptr rl (new RouteList); + boost::shared_ptr r = routes.reader (); + boost::shared_ptr rl (new RouteList); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); @@ -3890,41 +3979,233 @@ Session::goto_start () } } +framepos_t +Session::current_start_frame () const +{ + return _session_range_location ? _session_range_location->start() : 0; +} + +framepos_t +Session::current_end_frame () const +{ + return _session_range_location ? _session_range_location->end() : 0; +} + void -Session::set_session_start (nframes_t start) +Session::add_session_range_location (framepos_t start, framepos_t end) { - if (_session_range_location) { - _session_range_location->set_start (start); + _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange); + _locations->add (_session_range_location); +} + +/** Called when one of our routes' order keys has changed */ +void +Session::route_order_key_changed () +{ + RouteOrderKeyChanged (); /* EMIT SIGNAL */ +} + +void +Session::step_edit_status_change (bool yn) +{ + bool send = false; + + bool val = false; + if (yn) { + send = (_step_editors == 0); + val = true; + + _step_editors++; } else { - add_session_range_location (start, start); + send = (_step_editors == 1); + val = false; + + if (_step_editors > 0) { + _step_editors--; + } + } + + if (send) { + StepEditStatusChange (val); } } - + + void -Session::set_session_end (nframes_t end) +Session::start_time_changed (framepos_t old) { - if (_session_range_location) { - _session_range_location->set_end (end); + /* Update the auto loop range to match the session range + (unless the auto loop range has been changed by the user) + */ + + Location* s = _locations->session_range_location (); + if (s == 0) { + return; + } + + Location* l = _locations->auto_loop_location (); + + if (l->start() == old) { + l->set_start (s->start(), true); + } +} + +void +Session::end_time_changed (framepos_t old) +{ + /* Update the auto loop range to match the session range + (unless the auto loop range has been changed by the user) + */ + + Location* s = _locations->session_range_location (); + if (s == 0) { + return; + } + + Location* l = _locations->auto_loop_location (); + + if (l->end() == old) { + l->set_end (s->end(), true); + } +} + +string +Session::source_search_path (DataType type) const +{ + string search_path; + + if (session_dirs.size() == 1) { + switch (type) { + case DataType::AUDIO: + search_path = _session_dir->sound_path().to_string(); + break; + case DataType::MIDI: + search_path = _session_dir->midi_path().to_string(); + break; + } } else { - add_session_range_location (end, end); + for (vector::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) { + SessionDirectory sdir (i->path); + if (!search_path.empty()) { + search_path += ':'; + } + switch (type) { + case DataType::AUDIO: + search_path += sdir.sound_path().to_string(); + break; + case DataType::MIDI: + search_path += sdir.midi_path().to_string(); + break; + } + } + } + + /* now add user-specified locations + */ + + vector dirs; + + switch (type) { + case DataType::AUDIO: + split (config.get_audio_search_path (), dirs, ':'); + break; + case DataType::MIDI: + split (config.get_midi_search_path (), dirs, ':'); + break; } + + for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { + search_path += ':'; + search_path += *i; + + } + + return search_path; } -nframes_t -Session::current_start_frame () const +void +Session::ensure_search_path_includes (const string& path, DataType type) { - return _session_range_location ? _session_range_location->start() : 0; + string search_path; + vector dirs; + + if (path == ".") { + return; + } + + switch (type) { + case DataType::AUDIO: + search_path = config.get_audio_search_path (); + break; + case DataType::MIDI: + search_path = config.get_midi_search_path (); + break; + } + + split (search_path, dirs, ':'); + + for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { + if (*i == path) { + return; + } + } + + if (!search_path.empty()) { + search_path += ':'; + } + + search_path += path; + + switch (type) { + case DataType::AUDIO: + config.set_audio_search_path (search_path); + break; + case DataType::MIDI: + config.set_midi_search_path (search_path); + break; + } } -nframes_t -Session::current_end_frame () const +boost::shared_ptr +Session::get_speakers() { - return _session_range_location ? _session_range_location->end() : 0; + return _speakers; } +list +Session::unknown_processors () const +{ + list p; + + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + list t = (*i)->unknown_processors (); + copy (t.begin(), t.end(), back_inserter (p)); + } + + p.sort (); + p.unique (); + + return p; +} + +#ifdef HAVE_JACK_NEW_LATENCY void -Session::add_session_range_location (nframes_t start, nframes_t end) +Session::update_latency (bool playback) { - _session_range_location = new Location (start, end, _("session"), Location::IsSessionRange); - _locations.add (_session_range_location); + DEBUG_TRACE (DEBUG::Latency, "JACK latency callback\n"); + + boost::shared_ptr r = routes.reader (); + + if (playback) { + /* reverse the list so that we work backwards from the last route to run to the first */ + reverse (r->begin(), r->end()); + } + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + DEBUG_TRACE (DEBUG::Latency, string_compose ("------------- Working on latency for %1\n", (*i)->name())); + (*i)->set_latency_ranges (playback); + DEBUG_TRACE (DEBUG::Latency, string_compose ("------------- Done working on latency for %1\n\n", (*i)->name())); + } } +#endif