X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=0f93db71282f2689741e587dcb983fe9e46de85a;hb=b9054a1f723f6107f42a04ae9838820bbbd1f0ce;hp=9f8d0808afefc9c4c5cbfd59b4fd0c69e9ba17db;hpb=2b8912f7901ff00aadc20e01371b4b8a53971967;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 9f8d0808af..0f93db7128 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include @@ -45,11 +45,11 @@ #include "pbd/file_utils.h" #include "pbd/convert.h" #include "pbd/strsplit.h" -#include "pbd/strsplit.h" #include "pbd/unwind.h" #include "ardour/amp.h" #include "ardour/analyser.h" +#include "ardour/async_midi_port.h" #include "ardour/audio_buffer.h" #include "ardour/audio_diskstream.h" #include "ardour/audio_port.h" @@ -67,9 +67,9 @@ #include "ardour/debug.h" #include "ardour/filename_extensions.h" #include "ardour/graph.h" +#include "ardour/midiport_manager.h" #include "ardour/midi_track.h" #include "ardour/midi_ui.h" -#include "ardour/named_selection.h" #include "ardour/operations.h" #include "ardour/playlist.h" #include "ardour/plugin.h" @@ -87,12 +87,11 @@ #include "ardour/session_playlists.h" #include "ardour/smf_source.h" #include "ardour/source_factory.h" +#include "ardour/speakers.h" #include "ardour/utils.h" #include "midi++/port.h" -#include "midi++/jack_midi_port.h" #include "midi++/mmc.h" -#include "midi++/manager.h" #include "i18n.h" @@ -108,6 +107,7 @@ using namespace PBD; bool Session::_disable_all_loaded_plugins = false; +PBD::Signal1 Session::AudioEngineSetupRequired; PBD::Signal1 Session::Dialog; PBD::Signal0 Session::AskAboutPendingState; PBD::Signal2 Session::AskAboutSampleRateMismatch; @@ -121,78 +121,207 @@ PBD::Signal1 > Session::AskAboutPlaylistDeletion PBD::Signal0 Session::Quit; PBD::Signal0 Session::FeedbackDetected; PBD::Signal0 Session::SuccessfulGraphSort; +PBD::Signal2 Session::VersionMismatch; static void clean_up_session_event (SessionEvent* ev) { delete ev; } const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event); -/** @param snapshot_name Snapshot name, without .ardour prefix */ +/** @param snapshot_name Snapshot name, without .ardour suffix */ Session::Session (AudioEngine &eng, const string& fullpath, const string& snapshot_name, BusProfile* bus_profile, string mix_template) - : _engine (eng) + : playlists (new SessionPlaylists) + , _engine (eng) + , process_function (&Session::process_with_events) + , waiting_for_sync_offset (false) + , _base_frame_rate (0) + , _current_frame_rate (0) + , _nominal_frame_rate (0) + , transport_sub_state (0) + , _record_status (Disabled) + , _transport_frame (0) + , _session_range_location (0) + , _slave (0) + , _silent (false) + , _transport_speed (0) + , _default_transport_speed (1.0) + , _last_transport_speed (0) , _target_transport_speed (0.0) + , auto_play_legal (false) + , _last_slave_transport_frame (0) + , maximum_output_latency (0) , _requested_return_frame (-1) - , _session_dir (new SessionDirectory(fullpath)) + , current_block_size (0) + , _worst_output_latency (0) + , _worst_input_latency (0) + , _worst_track_latency (0) + , _have_captured (false) + , _meter_hold (0) + , _meter_falloff (0) + , _non_soloed_outs_muted (false) + , _listen_cnt (0) + , _solo_isolated_cnt (0) + , _writable (false) + , _was_seamless (Config->get_seamless_loop ()) + , _under_nsm_control (false) + , delta_accumulator_cnt (0) + , average_slave_delta (1800) // !!! why 1800 ??? + , average_dir (0) + , have_first_delta_accumulator (false) + , _slave_state (Stopped) + , post_export_sync (false) + , post_export_position (0) + , _exporting (false) + , _export_started (false) + , _export_rolling (false) + , _pre_export_mmc_enabled (false) + , _name (snapshot_name) + , _is_new (true) + , _send_qf_mtc (false) + , _pframes_since_last_mtc (0) + , session_midi_feedback (0) + , play_loop (false) + , loop_changing (false) + , last_loopend (0) + , _session_dir (new SessionDirectory (fullpath)) + , _current_snapshot_name (snapshot_name) , state_tree (0) - , _state_of_the_state (Clean) + , state_was_pending (false) + , _state_of_the_state (StateOfTheState(CannotSave|InitialConnecting|Loading)) + , _last_roll_location (0) + , _last_roll_or_reversal_location (0) + , _last_record_location (0) + , pending_locate_roll (false) + , pending_locate_frame (0) + , pending_locate_flush (false) + , pending_abort (false) + , pending_auto_loop (false) , _butler (new Butler (*this)) , _post_transport_work (0) + , cumulative_rf_motion (0) + , rf_scale (1.0) + , _locations (new Locations (*this)) + , step_speed (0) + , outbound_mtc_timecode_frame (0) + , next_quarter_frame_to_send (-1) + , _frames_per_timecode_frame (0) + , _frames_per_hour (0) + , _timecode_frames_per_hour (0) + , last_timecode_valid (false) + , last_timecode_when (0) , _send_timecode_update (false) + , ltc_encoder (0) + , ltc_enc_buf(0) + , ltc_buf_off (0) + , ltc_buf_len (0) + , ltc_speed (0) + , ltc_enc_byte (0) + , ltc_enc_pos (0) + , ltc_enc_cnt (0) + , ltc_enc_off (0) + , restarting (false) + , ltc_prev_cycle (0) + , ltc_timecode_offset (0) + , ltc_timecode_negative_offset (false) + , midi_control_ui (0) + , _tempo_map (0) , _all_route_group (new RouteGroup (*this, "all")) , routes (new RouteList) + , _adding_routes_in_progress (false) + , destructive_index (0) + , solo_update_disabled (false) + , default_fade_steepness (0) + , default_fade_msecs (0) , _total_free_4k_blocks (0) , _total_free_4k_blocks_uncertain (false) + , no_questions_about_missing_files (false) + , _playback_load (0) + , _capture_load (0) , _bundles (new BundleList) , _bundle_xml_node (0) , _current_trans (0) - , _click_io ((IO*) 0) + , _clicking (false) , click_data (0) , click_emphasis_data (0) + , click_length (0) + , click_emphasis_length (0) + , _clicks_cleared (0) + , _play_range (false) , main_outs (0) + , first_file_data_format_reset (true) + , first_file_header_format_reset (true) + , have_looped (false) , _have_rec_enabled_track (false) + , _step_editors (0) , _suspend_timecode_transmission (0) + , _speakers (new Speakers) + , _order_hint (0) + , ignore_route_processor_changes (false) + , _midi_ports (0) + , _mmc (0) { - _locations = new Locations (*this); - - if (how_many_dsp_threads () > 1) { - /* For now, only create the graph if we are using >1 DSP threads, as - it is a bit slower than the old code with 1 thread. - */ - _process_graph.reset (new Graph (*this)); - } + uint32_t sr = 0; - playlists.reset (new SessionPlaylists); + pre_engine_init (fullpath); + + if (_is_new) { + if (ensure_engine (sr)) { + destroy (); + throw failed_constructor (); + } - _all_route_group->set_active (true, this); + if (create (mix_template, bus_profile)) { + destroy (); + throw failed_constructor (); + } - interpolation.add_channel_to (0, 0); + /* if a mix template was provided, then ::create() will + * have copied it into the session and we need to load it + * so that we have the state ready for ::set_state() + * after the engine is started. + * + * Note that we do NOT try to get the sample rate from + * the template at this time, though doing so would + * be easy if we decided this was an appropriate part + * of a template. + */ - if (!eng.connected()) { - throw failed_constructor(); - } + if (!mix_template.empty() && load_state (_current_snapshot_name)) { + throw failed_constructor (); + } - n_physical_outputs = _engine.n_physical_outputs (); - n_physical_inputs = _engine.n_physical_inputs (); + } else { - first_stage_init (fullpath, snapshot_name); + if (load_state (_current_snapshot_name)) { + throw failed_constructor (); + } + + /* try to get sample rate from XML state so that we + * can influence the SR if we set up the audio + * engine. + */ - _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + if (state_tree) { + const XMLProperty* prop; + if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) { + sr = atoi (prop->value()); + } + } - if (_is_new) { - if (create (mix_template, bus_profile)) { + if (ensure_engine (sr)) { destroy (); throw failed_constructor (); } } - if (second_stage_init ()) { + if (post_engine_init ()) { destroy (); throw failed_constructor (); } - store_recent_sessions(_name, _path); + store_recent_sessions (_name, _path); bool was_dirty = dirty(); @@ -209,6 +338,16 @@ Session::Session (AudioEngine &eng, EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1)); _is_new = false; + + /* hook us up to the engine since we are now completely constructed */ + + BootMessage (_("Connect to engine")); + + _engine.set_session (this); + _engine.reset_timebase (); + + BootMessage (_("Session loading complete")); + } Session::~Session () @@ -219,6 +358,81 @@ Session::~Session () destroy (); } +int +Session::ensure_engine (uint32_t desired_sample_rate) +{ + if (_engine.current_backend() == 0) { + /* backend is unknown ... */ + boost::optional r = AudioEngineSetupRequired (desired_sample_rate); + if (r.get_value_or (-1) != 0) { + return -1; + } + } else if (_engine.setup_required()) { + /* backend is known, but setup is needed */ + boost::optional r = AudioEngineSetupRequired (desired_sample_rate); + if (r.get_value_or (-1) != 0) { + return -1; + } + } else if (!_engine.running()) { + if (_engine.start()) { + return -1; + } + } + + /* at this point the engine should be running + */ + + if (!_engine.running()) { + return -1; + } + + return immediately_post_engine (); + +} + +int +Session::immediately_post_engine () +{ + /* Do various initializations that should take place directly after we + * know that the engine is running, but before we either create a + * session or set state for an existing one. + */ + + if (how_many_dsp_threads () > 1) { + /* For now, only create the graph if we are using >1 DSP threads, as + it is a bit slower than the old code with 1 thread. + */ + _process_graph.reset (new Graph (*this)); + } + + /* every time we reconnect, recompute worst case output latencies */ + + _engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this)); + + if (synced_to_engine()) { + _engine.transport_stop (); + } + + if (config.get_jack_time_master()) { + _engine.transport_locate (_transport_frame); + } + + try { + BootMessage (_("Set up LTC")); + setup_ltc (); + BootMessage (_("Set up Click")); + setup_click (); + BootMessage (_("Set up standard connections")); + setup_bundles (); + } + + catch (failed_constructor& err) { + return -1; + } + + return 0; +} + void Session::destroy () { @@ -232,8 +446,20 @@ Session::destroy () _state_of_the_state = StateOfTheState (CannotSave|Deletion); + /* disconnect from any and all signals that we are connected to */ + + drop_connections (); + _engine.remove_session (); + /* deregister all ports - there will be no process or any other + * callbacks from the engine any more. + */ + + Port::PortDrop (); /* EMIT SIGNAL */ + + ltc_tx_cleanup(); + /* clear history so that no references to objects are held any more */ _history.clear (); @@ -275,9 +501,6 @@ Session::destroy () /* tell everyone to drop references and delete objects as we go */ - DEBUG_TRACE (DEBUG::Destruction, "delete named selections\n"); - named_selections.clear (); - DEBUG_TRACE (DEBUG::Destruction, "delete regions\n"); RegionFactory::delete_all_regions (); @@ -303,13 +526,16 @@ Session::destroy () } routes.flush (); - 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->name(), i->second.use_count())); - i->second->drop_references (); - } + { + DEBUG_TRACE (DEBUG::Destruction, "delete sources\n"); + Glib::Threads::Mutex::Lock lm (source_lock); + 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->name(), i->second.use_count())); + i->second->drop_references (); + } - sources.clear (); + sources.clear (); + } DEBUG_TRACE (DEBUG::Destruction, "delete route groups\n"); for (list::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) { @@ -320,7 +546,9 @@ Session::destroy () /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */ playlists.reset (); - delete _locations; + delete _mmc; _mmc = 0; + delete _midi_ports; _midi_ports = 0; + delete _locations; _locations = 0; DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); @@ -330,105 +558,112 @@ Session::destroy () } void -Session::when_engine_running () +Session::setup_ltc () { - string first_physical_output; - - BootMessage (_("Set block size and sample rate")); - - set_block_size (_engine.frames_per_cycle()); - set_frame_rate (_engine.frame_rate()); - - BootMessage (_("Using configuration")); - - 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_same_thread (*this, boost::bind (&Session::initialize_latencies, this)); - - if (synced_to_jack()) { - _engine.transport_stop (); + XMLNode* child = 0; + + _ltc_input.reset (new IO (*this, X_("LTC In"), IO::Input)); + _ltc_output.reset (new IO (*this, X_("LTC Out"), IO::Output)); + + if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC In"))) != 0) { + _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version); + } else { + { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this); + } + reconnect_ltc_input (); } - - if (config.get_jack_time_master()) { - _engine.transport_locate (_transport_frame); + + if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC Out"))) != 0) { + _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version); + } else { + { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + _ltc_output->ensure_io (ChanCount (DataType::AUDIO, 1), true, this); + } + reconnect_ltc_output (); } + + /* fix up names of LTC ports because we don't want the normal + * IO style of NAME/TYPE-{in,out}N + */ + + _ltc_input->nth (0)->set_name (X_("LTC-in")); + _ltc_output->nth (0)->set_name (X_("LTC-out")); +} +void +Session::setup_click () +{ _clicking = false; + _click_io.reset (new ClickIO (*this, X_("Click"))); + _click_gain.reset (new Amp (*this)); + _click_gain->activate (); + if (state_tree) { + setup_click_state (state_tree->root()); + } else { + setup_click_state (0); + } +} - try { - XMLNode* child = 0; - - _click_io.reset (new ClickIO (*this, "click")); - _click_gain.reset (new Amp (*this)); - _click_gain->activate (); - - if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) { - - /* existing state for Click */ - int c = 0; +void +Session::setup_click_state (const XMLNode* node) +{ + const XMLNode* child = 0; + + if (node && (child = find_named_node (*node, "Click")) != 0) { + + /* existing state for Click */ + int c = 0; - if (Stateful::loading_state_version < 3000) { - c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false); - } else { - const XMLNodeList& children (child->children()); - XMLNodeList::const_iterator i = children.begin(); - if ((c = _click_io->set_state (**i, Stateful::loading_state_version)) == 0) { - ++i; - if (i != children.end()) { - c = _click_gain->set_state (**i, Stateful::loading_state_version); - } + if (Stateful::loading_state_version < 3000) { + c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false); + } else { + const XMLNodeList& children (child->children()); + XMLNodeList::const_iterator i = children.begin(); + if ((c = _click_io->set_state (**i, Stateful::loading_state_version)) == 0) { + ++i; + if (i != children.end()) { + c = _click_gain->set_state (**i, Stateful::loading_state_version); } } + } - if (c == 0) { - _clicking = Config->get_clicking (); + if (c == 0) { + _clicking = Config->get_clicking (); - } else { + } else { - error << _("could not setup Click I/O") << endmsg; - _clicking = false; - } + error << _("could not setup Click I/O") << endmsg; + _clicking = false; + } - } else { + } else { - /* default state for Click: dual-mono to first 2 physical outputs */ + /* default state for Click: dual-mono to first 2 physical outputs */ - vector outs; - _engine.get_physical_outputs (DataType::AUDIO, outs); + vector outs; + _engine.get_physical_outputs (DataType::AUDIO, outs); - 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 - } + 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; - } - BootMessage (_("Compute I/O Latencies")); - - if (_clicking) { - // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO? + if (_click_io->n_ports () > ChanCount::ZERO) { + _clicking = Config->get_clicking (); + } } +} - BootMessage (_("Set up standard connections")); - +void +Session::setup_bundles () +{ vector inputs[DataType::num_types]; vector outputs[DataType::num_types]; for (uint32_t i = 0; i < DataType::num_types; ++i) { @@ -527,52 +762,6 @@ Session::when_engine_running () add_bundle (c); } - BootMessage (_("Setup signal flow and plugins")); - - /* Reset all panners */ - - Delivery::reset_panners (); - - /* this will cause the CPM to instantiate any protocols that are in use - * (or mandatory), which will pass it this Session, and then call - * set_state() on each instantiated protocol to match stored state. - */ - - ControlProtocolManager::instance().set_session (this); - - /* This must be done after the ControlProtocolManager set_session above, - as it will set states for ports which the ControlProtocolManager creates. - */ - - MIDI::Manager::instance()->set_port_states (Config->midi_port_states ()); - - /* And this must be done after the MIDI::Manager::set_port_states as - * it will try to make connections whose details are loaded by set_port_states. - */ - - hookup_io (); - - /* Let control protocols know that we are now all connected, so they - * could start talking to surfaces if they want to. - */ - - ControlProtocolManager::instance().midi_connectivity_established (); - - if (_is_new && !no_auto_connect()) { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock()); - auto_connect_master_bus (); - } - - _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); - - /* update latencies */ - - initialize_latencies (); - - /* hook us up to the engine */ - - BootMessage (_("Connect to engine")); - _engine.set_session (this); } void @@ -619,12 +808,18 @@ Session::remove_monitor_section () /* force reversion to Solo-In-Place */ Config->set_solo_control_is_listen_control (false); + /* if we are auditioning, cancel it ... this is a workaround + to a problem (auditioning does not execute the process graph, + which is needed to remove routes when using >1 core for processing) + */ + cancel_audition (); + { /* Hold process lock while doing this so that we don't hear bits and * pieces of audio as we work on each route. */ - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); /* Connect tracks to monitor section. Note that in an existing session, the internal sends will already exist, but we want the @@ -649,6 +844,10 @@ Session::remove_monitor_section () remove_route (_monitor_out); auto_connect_master_bus (); + + if (auditioner) { + auditioner->connect (); + } } void @@ -670,7 +869,7 @@ Session::add_monitor_section () // boost_debug_shared_ptr_mark_interesting (r.get(), "Route"); #endif { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); r->input()->ensure_io (_master_out->output()->n_ports(), false, this); r->output()->ensure_io (_master_out->output()->n_ports(), false, this); } @@ -723,7 +922,7 @@ Session::add_monitor_section () boost::shared_ptr b = bundle_by_name (Config->get_monitor_bus_preferred_bundle()); if (b) { - _monitor_out->output()->connect_ports_to_bundle (b, this); + _monitor_out->output()->connect_ports_to_bundle (b, true, this); } else { warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"), Config->get_monitor_bus_preferred_bundle()) @@ -734,14 +933,14 @@ Session::add_monitor_section () /* Monitor bus is audio only */ - uint32_t mod = n_physical_outputs.get (DataType::AUDIO); - uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO); vector outputs[DataType::num_types]; for (uint32_t i = 0; i < DataType::num_types; ++i) { _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]); } - + + uint32_t mod = outputs[DataType::AUDIO].size(); + uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO); if (mod != 0) { @@ -771,7 +970,7 @@ Session::add_monitor_section () * pieces of audio as we work on each route. */ - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); /* Connect tracks to monitor section. Note that in an existing session, the internal sends will already exist, but we want the @@ -793,6 +992,10 @@ Session::add_monitor_section () (*x)->enable_monitor_send (); } } + + if (auditioner) { + auditioner->connect (); + } } void @@ -833,7 +1036,12 @@ Session::hookup_io () /* Tell all IO objects to connect themselves together */ IO::enable_connecting (); - MIDI::JackMIDIPort::MakeConnections (); + + /* Now tell all "floating" ports to connect to whatever + they should be connected to. + */ + + AudioEngine::instance()->reconnect_ports (); /* Anyone who cares about input state, wake up and do something */ @@ -895,7 +1103,7 @@ Session::set_track_monitor_input_status (bool yn) boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); if (tr && tr->record_enabled ()) { //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; - tr->request_jack_monitors_input (yn); + tr->request_input_monitoring (yn); } } } @@ -929,10 +1137,25 @@ Session::auto_punch_changed (Location* location) replace_event (SessionEvent::PunchOut, when_to_stop); } +/** @param loc A loop location. + * @param pos Filled in with the start time of the required fade-out (in session frames). + * @param length Filled in with the length of the required fade-out. + */ +void +Session::auto_loop_declick_range (Location* loc, framepos_t & pos, framepos_t & length) +{ + pos = max (loc->start(), loc->end() - 64); + length = loc->end() - pos; +} + void Session::auto_loop_changed (Location* location) { replace_event (SessionEvent::AutoLoop, location->end(), location->start()); + framepos_t dcp; + framecnt_t dcl; + auto_loop_declick_range (location, dcp, dcl); + replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl); if (transport_rolling() && play_loop) { @@ -1010,6 +1233,10 @@ Session::set_auto_loop_location (Location* location) loop_connections.drop_connections (); existing->set_auto_loop (false, this); remove_event (existing->end(), SessionEvent::AutoLoop); + framepos_t dcp; + framecnt_t dcl; + auto_loop_declick_range (existing, dcp, dcl); + remove_event (dcp, SessionEvent::AutoLoopDeclick); auto_loop_location_changed (0); } @@ -1020,7 +1247,7 @@ Session::set_auto_loop_location (Location* location) } if (location->end() <= location->start()) { - error << _("Session: you can't use a mark for auto loop") << endmsg; + error << _("You cannot use this location for auto-loop because it has zero or negative length") << endmsg; return; } @@ -1109,7 +1336,7 @@ Session::enable_record () 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)); + _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe)); if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { set_track_monitor_input_status (true); @@ -1130,7 +1357,7 @@ 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)); + _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit)); } else { if (rs == Recording) { g_atomic_int_set (&_record_status, Enabled); @@ -1184,7 +1411,7 @@ Session::maybe_enable_record () enable_record (); } } else { - MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause)); + _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause)); RecordStateChanged (); /* EMIT SIGNAL */ } @@ -1221,7 +1448,7 @@ Session::audible_frame () const offset = current_block_size; } - if (synced_to_jack()) { + if (synced_to_engine()) { tf = _engine.transport_frame(); } else { tf = _transport_frame; @@ -1239,7 +1466,7 @@ Session::audible_frame () const of audible frames, we have not moved yet. `Start position' in this context means the time we last - either started or changed transport direction. + either started, located, or changed transport direction. */ if (_transport_speed > 0.0f) { @@ -1281,11 +1508,10 @@ Session::set_frame_rate (framecnt_t frames_per_second) */ _base_frame_rate = frames_per_second; + _nominal_frame_rate = frames_per_second; sync_time_vars(); - Automatable::set_automation_interval (ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval()))); - clear_clicks (); // XXX we need some equivalent to this, somehow @@ -1485,13 +1711,13 @@ Session::resort_routes_using (boost::shared_ptr r) trace_terminal (*i, *i); } - r = sorted_routes; + *r = *sorted_routes; #ifndef NDEBUG 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 ())); } #endif @@ -1541,7 +1767,7 @@ Session::find_route_name (string const & base, uint32_t& id, char* name, size_t } ++id; - + } while (id < (UINT_MAX-1)); return false; @@ -1558,7 +1784,7 @@ Session::count_existing_track_channels (ChanCount& in, ChanCount& out) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && !tr->is_hidden()) { + if (tr && !tr->is_auditioner()) { in += tr->n_inputs(); out += tr->n_outputs(); } @@ -1570,16 +1796,14 @@ Session::count_existing_track_channels (ChanCount& in, ChanCount& out) * @param instrument plugin info for the instrument to insert pre-fader, if any */ list > -Session::new_midi_track (boost::shared_ptr instrument, TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) +Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost::shared_ptr instrument, + TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template) { char track_name[32]; uint32_t track_id = 0; string port; RouteList new_routes; list > ret; - uint32_t control_id; - - control_id = next_control_id (); bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("MIDI"); @@ -1604,14 +1828,14 @@ Session::new_midi_track (boost::shared_ptr instrument, TrackMode mod // 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; + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + if (track->input()->ensure_io (input, false, this)) { + error << "cannot configure " << input << " 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; + if (track->output()->ensure_io (output, false, this)) { + error << "cannot configure " << output << " out configuration for new midi track" << endmsg; goto failed; } } @@ -1623,7 +1847,10 @@ Session::new_midi_track (boost::shared_ptr instrument, TrackMode mod } track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); - track->set_remote_control_id (control_id); + + if (Config->get_remote_model() == UserOrdered) { + track->set_remote_control_id (next_control_id()); + } new_routes.push_back (track); ret.push_back (track); @@ -1636,7 +1863,7 @@ Session::new_midi_track (boost::shared_ptr instrument, TrackMode mod catch (AudioEngine::PortRegistrationFailure& pfe) { - error << string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with ports if you need this many tracks."), PROGRAM_NAME) << endmsg; + error << string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with more ports if you need this many tracks."), PROGRAM_NAME) << endmsg; goto failed; } @@ -1701,7 +1928,7 @@ Session::auto_connect_route (boost::shared_ptr route, ChanCount& existing return; } - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock (), Glib::NOT_LOCK); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK); if (with_lock) { lm.acquire (); @@ -1775,9 +2002,9 @@ Session::auto_connect_route (boost::shared_ptr route, ChanCount& existing for (uint32_t i = output_start.get(*t); i < route->n_outputs().get(*t); ++i) { string port; - if ((*t) == DataType::MIDI || Config->get_output_auto_connect() & AutoConnectPhysical) { + if ((*t) == DataType::MIDI && (Config->get_output_auto_connect() & AutoConnectPhysical)) { port = physoutputs[(out_offset.get(*t) + i) % nphysical_out]; - } else if ((*t) == DataType::AUDIO && Config->get_output_auto_connect() & AutoConnectMaster) { + } else if ((*t) == DataType::AUDIO && (Config->get_output_auto_connect() & AutoConnectMaster)) { /* master bus is audio only */ if (_master_out && _master_out->n_inputs().get(*t) > 0) { port = _master_out->input()->ports().port(*t, @@ -1812,9 +2039,6 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod string port; RouteList new_routes; list > ret; - uint32_t control_id; - - control_id = next_control_id (); bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Audio"); @@ -1839,7 +2063,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod // boost_debug_shared_ptr_mark_interesting (track.get(), "Track"); #endif { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) { error << string_compose ( @@ -1865,8 +2089,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod track->non_realtime_input_change(); track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); - track->set_remote_control_id (control_id); - ++control_id; + if (Config->get_remote_model() == UserOrdered) { + track->set_remote_control_id (next_control_id()); + } new_routes.push_back (track); ret.push_back (track); @@ -1894,33 +2119,6 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod return ret; } -void -Session::set_remote_control_ids () -{ - RemoteModel m = Config->get_remote_model(); - bool emit_signal = false; - - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (MixerOrdered == m) { - int32_t order = (*i)->order_key(N_("signal")); - (*i)->set_remote_control_id (order+1, false); - emit_signal = true; - } else if (EditorOrdered == m) { - int32_t order = (*i)->order_key(N_("editor")); - (*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(); - } -} - /** Caller must not hold process lock. * @param name_template string to use for the start of the name, or "" to use "Bus". */ @@ -1931,9 +2129,6 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r uint32_t bus_id = 0; string port; RouteList ret; - uint32_t control_id; - - control_id = next_control_id (); bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Bus"); @@ -1954,7 +2149,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route"); #endif { - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); 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"), @@ -1975,12 +2170,15 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r if (route_group) { route_group->add (bus); } - bus->set_remote_control_id (control_id); - ++control_id; + if (Config->get_remote_model() == UserOrdered) { + bus->set_remote_control_id (next_control_id()); + } bus->add_internal_return (); ret.push_back (bus); + + ARDOUR::GUIIdle (); } @@ -2008,12 +2206,13 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r } RouteList -Session::new_route_from_template (uint32_t how_many, const std::string& template_path) +Session::new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name_base) { RouteList ret; uint32_t control_id; XMLTree tree; uint32_t number = 0; + const uint32_t being_added = how_many; if (!tree.read (template_path.c_str())) { return ret; @@ -2033,13 +2232,29 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template node_copy.remove_property_recursively (X_("id")); try { - string const route_name = node_copy.property(X_("name"))->value (); - - /* generate a new name by adding a number to the end of the template name */ char name[32]; - if (!find_route_name (route_name.c_str(), ++number, name, sizeof(name), true)) { - fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; - /*NOTREACHED*/ + + if (!name_base.empty()) { + + /* if we're adding more than one routes, force + * all the names of the new routes to be + * numbered, via the final parameter. + */ + + if (!find_route_name (name_base.c_str(), ++number, name, sizeof(name), (being_added > 1))) { + fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; + /*NOTREACHDE*/ + } + + } else { + + string const route_name = node_copy.property(X_("name"))->value (); + + /* generate a new name by adding a number to the end of the template name */ + if (!find_route_name (route_name.c_str(), ++number, name, sizeof(name), true)) { + fatal << _("Session: UINT_MAX routes? impossible!") << endmsg; + /*NOTREACHED*/ + } } /* set this name in the XML description that we are about to use */ @@ -2069,7 +2284,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template loading this normally happens in a different way. */ - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); IOChange change (IOChange::Type (IOChange::ConfigurationChanged | IOChange::ConnectionsChanged)); change.after = route->input()->n_ports(); @@ -2108,9 +2323,40 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template void Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, bool save) +{ + try { + PBD::Unwinder aip (_adding_routes_in_progress, true); + add_routes_inner (new_routes, input_auto_connect, output_auto_connect); + + } catch (...) { + error << _("Adding new tracks/busses failed") << endmsg; + } + + graph_reordered (); + + update_latency (true); + update_latency (false); + + set_dirty(); + + if (save) { + save_state (_current_snapshot_name); + } + + RouteAdded (new_routes); /* EMIT SIGNAL */ +} + +void +Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect) { ChanCount existing_inputs; ChanCount existing_outputs; + uint32_t order = next_control_id(); + + if (_order_hint != 0) { + order = _order_hint; + _order_hint = 0; + } count_existing_track_channels (existing_inputs, existing_outputs); @@ -2141,7 +2387,6 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output 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->order_key_changed.connect_same_thread (*this, boost::bind (&Session::route_order_key_changed, this)); if (r->is_master()) { _master_out = r; @@ -2164,38 +2409,43 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output } } + if (input_auto_connect || output_auto_connect) { auto_connect_route (r, existing_inputs, existing_outputs, true, input_auto_connect); } - } - if (_monitor_out && IO::connecting_legal) { + /* order keys are a GUI responsibility but we need to set up + reasonable defaults because they also affect the remote control + ID in most situations. + */ - { - Glib::Mutex::Lock lm (_engine.process_lock()); - - for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { - if ((*x)->is_monitor()) { - /* relax */ - } else if ((*x)->is_master()) { - /* relax */ - } else { - (*x)->enable_monitor_send (); - } + if (!r->has_order_key ()) { + if (r->is_auditioner()) { + /* use an arbitrarily high value */ + r->set_order_key (UINT_MAX); + } else { + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("while adding, set %1 to order key %2\n", r->name(), order)); + r->set_order_key (order); + order++; } } - resort_routes (); + ARDOUR::GUIIdle (); } - set_dirty(); - - if (save) { - save_state (_current_snapshot_name); + if (_monitor_out && IO::connecting_legal) { + Glib::Threads::Mutex::Lock lm (_engine.process_lock()); + + for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) { + if ((*x)->is_monitor()) { + /* relax */ + } else if ((*x)->is_master()) { + /* relax */ + } else { + (*x)->enable_monitor_send (); + } + } } - - RouteAdded (new_routes); /* EMIT SIGNAL */ - Route::RemoteControlIDChange (); /* EMIT SIGNAL */ } void @@ -2363,8 +2613,6 @@ Session::remove_route (boost::shared_ptr route) route->drop_references (); - sync_order_keys (N_("session")); - Route::RemoteControlIDChange(); /* EMIT SIGNAL */ /* save the new state of the world */ @@ -2395,7 +2643,7 @@ Session::route_listen_changed (void* /*src*/, boost::weak_ptr wpr) /* 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()) { + if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { continue; } (*i)->set_listen (false, this); @@ -2477,7 +2725,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p /* new solo: disable all other solos, but not the group if its solo-enabled */ 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() || + if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || (leave_group_alone && ((*i)->route_group() == rg))) { continue; } @@ -2497,7 +2745,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p bool via_sends_only; bool in_signal_flow; - if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden() || + if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || (leave_group_alone && ((*i)->route_group() == rg))) { continue; } @@ -2507,14 +2755,17 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed from %1\n", (*i)->name())); if ((*i)->feeds (route, &via_sends_only)) { + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name())); if (!via_sends_only) { if (!route->soloed_by_others_upstream()) { (*i)->mod_solo_by_others_downstream (delta); } + } else { + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a send-only feed from %1\n", (*i)->name())); } in_signal_flow = true; } else { - DEBUG_TRACE (DEBUG::Solo, "\tno feed from\n"); + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tno feed from %1\n", (*i)->name())); } DEBUG_TRACE (DEBUG::Solo, string_compose ("check feed to %1\n", (*i)->name())); @@ -2535,7 +2786,11 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p if (!route->soloed_by_others_downstream()) { DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta)); (*i)->mod_solo_by_others_upstream (delta); + } else { + DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others downstream\n"); } + } else { + DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name())); } in_signal_flow = true; } else { @@ -2557,7 +2812,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p */ for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1\n", (*i)->name())); + DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1, which neither feeds or is fed by %2\n", (*i)->name(), route->name())); (*i)->mute_changed (this); } @@ -2579,11 +2834,11 @@ Session::update_route_solo_state (boost::shared_ptr r) } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_hidden() && (*i)->self_soloed()) { + if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner() && (*i)->self_soloed()) { something_soloed = true; } - if (!(*i)->is_hidden() && (*i)->listening_via_monitor()) { + if (!(*i)->is_auditioner() && (*i)->listening_via_monitor()) { if (Config->get_solo_control_is_listen_control()) { listeners++; } else { @@ -2607,6 +2862,9 @@ Session::update_route_solo_state (boost::shared_ptr r) _solo_isolated_cnt = isolated; IsolatedChanged (); /* EMIT SIGNAL */ } + + DEBUG_TRACE (DEBUG::Solo, string_compose ("solo state updated by session, soloed? %1 listeners %2 isolated %3\n", + something_soloed, listeners, isolated)); } boost::shared_ptr @@ -2642,44 +2900,68 @@ Session::io_name_is_legal (const std::string& name) } void -Session::set_exclusive_input_active (boost::shared_ptr rt, bool /*others_on*/) +Session::set_exclusive_input_active (boost::shared_ptr rl, bool onoff, bool flip_others) { - RouteList rl; + RouteList rl2; vector connections; - PortSet& ps (rt->input()->ports()); - - for (PortSet::iterator p = ps.begin(); p != ps.end(); ++p) { - p->get_connections (connections); - } + /* if we are passed only a single route and we're not told to turn + * others off, then just do the simple thing. + */ - for (vector::iterator s = connections.begin(); s != connections.end(); ++s) { - routes_using_input_from (*s, rl); + if (flip_others == false && rl->size() == 1) { + boost::shared_ptr mt = boost::dynamic_pointer_cast (rl->front()); + if (mt) { + mt->set_input_active (onoff); + return; + } } - /* scan all relevant routes to see if others are on or off */ + for (RouteList::iterator rt = rl->begin(); rt != rl->end(); ++rt) { - bool others_are_already_on = false; + PortSet& ps ((*rt)->input()->ports()); + + for (PortSet::iterator p = ps.begin(); p != ps.end(); ++p) { + p->get_connections (connections); + } + + for (vector::iterator s = connections.begin(); s != connections.end(); ++s) { + routes_using_input_from (*s, rl2); + } + + /* scan all relevant routes to see if others are on or off */ + + bool others_are_already_on = false; + + for (RouteList::iterator r = rl2.begin(); r != rl2.end(); ++r) { - for (RouteList::iterator r = rl.begin(); r != rl.end(); ++r) { - if ((*r) != rt) { boost::shared_ptr mt = boost::dynamic_pointer_cast (*r); - if (mt) { + + if (!mt) { + continue; + } + + if ((*r) != (*rt)) { if (mt->input_active()) { others_are_already_on = true; - break; } + } else { + /* this one needs changing */ + mt->set_input_active (onoff); } } - } - - /* globally reverse other routes */ + + if (flip_others) { - for (RouteList::iterator r = rl.begin(); r != rl.end(); ++r) { - if ((*r) != rt) { - boost::shared_ptr mt = boost::dynamic_pointer_cast (*r); - if (mt) { - mt->set_input_active (!others_are_already_on); + /* globally reverse other routes */ + + for (RouteList::iterator r = rl2.begin(); r != rl2.end(); ++r) { + if ((*r) != (*rt)) { + boost::shared_ptr mt = boost::dynamic_pointer_cast (*r); + if (mt) { + mt->set_input_active (!others_are_already_on); + } + } } } } @@ -2688,7 +2970,7 @@ Session::set_exclusive_input_active (boost::shared_ptr rt, bool /*others_ void Session::routes_using_input_from (const string& str, RouteList& rl) { - boost::shared_ptr r = routes.reader (); + boost::shared_ptr r = routes.reader(); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->input()->connected_to (str)) { @@ -2846,7 +3128,7 @@ Session::find_whole_file_parent (boost::shared_ptr child) const RegionFactory::RegionMap::const_iterator i; boost::shared_ptr region; - Glib::Mutex::Lock lm (region_lock); + Glib::Threads::Mutex::Lock lm (region_lock); for (i = regions.begin(); i != regions.end(); ++i) { @@ -2892,7 +3174,7 @@ Session::destroy_sources (list > srcs) for (list >::iterator s = srcs.begin(); s != srcs.end(); ) { { - Glib::Mutex::Lock ls (source_lock); + Glib::Threads::Mutex::Lock ls (source_lock); /* remove from the main source list */ sources.erase ((*s)->id()); } @@ -2945,7 +3227,7 @@ Session::add_source (boost::shared_ptr source) entry.second = source; { - Glib::Mutex::Lock lm (source_lock); + Glib::Threads::Mutex::Lock lm (source_lock); result = sources.insert (entry); } @@ -2990,7 +3272,7 @@ Session::remove_source (boost::weak_ptr src) } { - Glib::Mutex::Lock lm (source_lock); + Glib::Threads::Mutex::Lock lm (source_lock); if ((i = sources.find (source->id())) != sources.end()) { sources.erase (i); @@ -3010,7 +3292,7 @@ Session::remove_source (boost::weak_ptr src) boost::shared_ptr Session::source_by_id (const PBD::ID& id) { - Glib::Mutex::Lock lm (source_lock); + Glib::Threads::Mutex::Lock lm (source_lock); SourceMap::iterator i; boost::shared_ptr source; @@ -3024,7 +3306,7 @@ Session::source_by_id (const PBD::ID& id) boost::shared_ptr Session::source_by_path_and_channel (const string& path, uint16_t chn) { - Glib::Mutex::Lock lm (source_lock); + Glib::Threads::Mutex::Lock lm (source_lock); for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) { boost::shared_ptr afs @@ -3041,7 +3323,7 @@ uint32_t Session::count_sources_by_origin (const string& path) { uint32_t cnt = 0; - Glib::Mutex::Lock lm (source_lock); + Glib::Threads::Mutex::Lock lm (source_lock); for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) { boost::shared_ptr fs @@ -3171,7 +3453,7 @@ Session::new_source_path_from_name (DataType type, const string& name) SessionDirectory sdir(get_best_session_directory_for_new_source()); - sys::path p; + std::string p; if (type == DataType::AUDIO) { p = sdir.sound_path(); } else if (type == DataType::MIDI) { @@ -3181,16 +3463,13 @@ Session::new_source_path_from_name (DataType type, const string& name) return ""; } - p /= name; - return p.to_string(); + return Glib::build_filename (p, name); } string Session::peak_path (string base) const { - sys::path peakfile_path(_session_dir->peak_path()); - peakfile_path /= base + peakfile_suffix; - return peakfile_path.to_string(); + return Glib::build_filename (_session_dir->peak_path(), base + peakfile_suffix); } /** Return a unique name based on \a base for a new internal audio source */ @@ -3254,7 +3533,7 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha SessionDirectory sdir((*i).path); - string spath = sdir.sound_path().to_string(); + string spath = sdir.sound_path(); /* note that we search *without* the extension so that we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf" @@ -3292,7 +3571,7 @@ Session::create_audio_source_for_session (size_t n_chans, string const & n, uint const string path = new_source_path_from_name(DataType::AUDIO, name); return boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, path, string(), destructive, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate())); } /** Return a unique name based on \a base for a new internal MIDI source */ @@ -3317,12 +3596,11 @@ Session::new_midi_source_name (const string& base) SessionDirectory sdir((*i).path); - sys::path p = sdir.midi_path(); - p /= legalized; + std::string p = Glib::build_filename (sdir.midi_path(), legalized); - snprintf (buf, sizeof(buf), "%s-%u.mid", p.to_string().c_str(), cnt); + snprintf (buf, sizeof(buf), "%s-%u.mid", p.c_str(), cnt); - if (sys::exists (buf)) { + if (Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) { existing++; } } @@ -3369,7 +3647,7 @@ Session::create_midi_source_for_session (Track* track, string const & n) return boost::dynamic_pointer_cast ( SourceFactory::createWritable ( - DataType::MIDI, *this, path, string(), false, frame_rate())); + DataType::MIDI, *this, path, false, frame_rate())); } @@ -3458,7 +3736,7 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost:: if (b->is_monitor()) { return false; } - return a->order_key(N_("signal")) < b->order_key(N_("signal")); + return a->order_key () < b->order_key (); } bool @@ -3479,7 +3757,7 @@ Session::graph_reordered () from a set_state() call or creating new tracks. Ditto for deletion. */ - if (_state_of_the_state & (InitialConnecting|Deletion)) { + if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress) { return; } @@ -3510,6 +3788,8 @@ Session::graph_reordered () boost::optional Session::available_capture_duration () { + Glib::Threads::Mutex::Lock lm (space_lock); + if (_total_free_4k_blocks_uncertain) { return boost::optional (); } @@ -3794,56 +4074,6 @@ Session::unmark_insert_id (uint32_t id) } } - -/* Named Selection management */ - -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 boost::shared_ptr(); -} - -void -Session::add_named_selection (boost::shared_ptr named_selection) -{ - { - Glib::Mutex::Lock lm (named_selection_lock); - named_selections.insert (named_selections.begin(), named_selection); - } - - set_dirty(); - - NamedSelectionAdded (); /* EMIT SIGNAL */ -} - -void -Session::remove_named_selection (boost::shared_ptr named_selection) -{ - bool removed = false; - - { - Glib::Mutex::Lock lm (named_selection_lock); - - NamedSelectionList::iterator i = find (named_selections.begin(), named_selections.end(), named_selection); - - if (i != named_selections.end()) { - named_selections.erase (i); - set_dirty(); - removed = true; - } - } - - if (removed) { - NamedSelectionRemoved (); /* EMIT SIGNAL */ - } -} - void Session::reset_native_file_format () { @@ -3927,7 +4157,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, framepos_t to_do; BufferSet buffers; SessionDirectory sdir(get_best_session_directory_for_new_source ()); - const string sound_dir = sdir.sound_path().to_string(); + const string sound_dir = sdir.sound_path(); framepos_t len = end - start; bool need_block_size_reset = false; string ext; @@ -3969,7 +4199,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, try { fsource = boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, buf, string(), false, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); } catch (failed_constructor& err) { @@ -4111,11 +4341,18 @@ Session::get_silent_buffers (ChanCount count) } BufferSet& -Session::get_scratch_buffers (ChanCount count) +Session::get_scratch_buffers (ChanCount count, bool silence) { - return ProcessThread::get_scratch_buffers (count); + return ProcessThread::get_scratch_buffers (count, silence); } +BufferSet& +Session::get_route_buffers (ChanCount count, bool silence) +{ + return ProcessThread::get_route_buffers (count, silence); +} + + BufferSet& Session::get_mix_buffers (ChanCount count) { @@ -4158,36 +4395,11 @@ Session::add_automation_list(AutomationList *al) automation_lists[al->id()] = al; } -void -Session::sync_order_keys (std::string const & base) -{ - if (deletion_in_progress()) { - return; - } - - if (!Config->get_sync_all_route_ordering()) { - /* leave order keys as they are */ - return; - } - - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->sync_order_keys (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 track, otherwise false */ bool Session::have_rec_enabled_track () const { - return g_atomic_int_get (&_have_rec_enabled_track) == 1; + return g_atomic_int_get (const_cast(&_have_rec_enabled_track)) == 1; } /** Update the state of our rec-enabled tracks flag */ @@ -4258,18 +4470,6 @@ Session::route_removed_from_route_group (RouteGroup* rg, boost::weak_ptr RouteRemovedFromRouteGroup (rg, r); } -vector -Session::get_available_sync_options () const -{ - vector ret; - - ret.push_back (JACK); - ret.push_back (MTC); - ret.push_back (MIDIClock); - - return ret; -} - boost::shared_ptr Session::get_routes_with_regions_at (framepos_t const p) const { @@ -4334,13 +4534,6 @@ Session::add_session_range_location (framepos_t start, framepos_t end) _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) { @@ -4413,10 +4606,10 @@ Session::source_search_path (DataType type) const if (session_dirs.size() == 1) { switch (type) { case DataType::AUDIO: - s.push_back ( _session_dir->sound_path().to_string()); + s.push_back (_session_dir->sound_path()); break; case DataType::MIDI: - s.push_back (_session_dir->midi_path().to_string()); + s.push_back (_session_dir->midi_path()); break; } } else { @@ -4424,15 +4617,24 @@ Session::source_search_path (DataType type) const SessionDirectory sdir (i->path); switch (type) { case DataType::AUDIO: - s.push_back (sdir.sound_path().to_string()); + s.push_back (sdir.sound_path()); break; case DataType::MIDI: - s.push_back (sdir.midi_path().to_string()); + s.push_back (sdir.midi_path()); break; } } } + if (type == DataType::AUDIO) { + const string sound_path_2X = _session_dir->sound_path_2X(); + if (Glib::file_test (sound_path_2X, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) { + if (find (s.begin(), s.end(), sound_path_2X) == s.end()) { + s.push_back (sound_path_2X); + } + } + } + /* now check the explicit (possibly user-specified) search path */ @@ -4448,16 +4650,7 @@ Session::source_search_path (DataType type) const } for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { - - vector::iterator si; - - for (si = s.begin(); si != s.end(); ++si) { - if ((*si) == *i) { - break; - } - } - - if (si == s.end()) { + if (find (s.begin(), s.end(), *i) == s.end()) { s.push_back (*i); } } @@ -4502,7 +4695,7 @@ Session::ensure_search_path_includes (const string& path, DataType type) On Windows, I think we could just do if (*i == path) here. */ - if (PBD::sys::inodes_same (*i, path)) { + if (PBD::equivalent_paths (*i, path)) { return; } } @@ -4551,7 +4744,7 @@ Session::update_latency (bool playback) { DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE"))); - if (_state_of_the_state & (InitialConnecting|Deletion)) { + if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress) { return; } @@ -4607,7 +4800,7 @@ Session::post_playback_latency () boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden() && ((*i)->active())) { + if (!(*i)->is_auditioner() && ((*i)->active())) { _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ()); } } @@ -4638,7 +4831,7 @@ void Session::initialize_latencies () { { - Glib::Mutex::Lock lm (_engine.process_lock()); + Glib::Threads::Mutex::Lock lm (_engine.process_lock()); update_latency (false); update_latency (true); } @@ -4713,7 +4906,7 @@ Session::update_latency_compensation (bool force_whole_graph) boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden() && ((*i)->active())) { + if (!(*i)->is_auditioner() && ((*i)->active())) { framecnt_t tl; if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency ())) { some_track_latency_changed = true; @@ -4758,7 +4951,54 @@ Session::session_name_is_legal (const string& path) uint32_t Session::next_control_id () const { - return ntracks() + nbusses() + 1; + int subtract = 0; + + /* the monitor bus remote ID is in a different + * "namespace" than regular routes. its existence doesn't + * affect normal (low) numbered routes. + */ + + if (_monitor_out) { + subtract++; + } + + return nroutes() - subtract; +} + +void +Session::notify_remote_id_change () +{ + if (deletion_in_progress()) { + return; + } + + switch (Config->get_remote_model()) { + case MixerOrdered: + Route::RemoteControlIDChange (); /* EMIT SIGNAL */ + break; + default: + break; + } +} + +void +Session::sync_order_keys () +{ + if (deletion_in_progress()) { + return; + } + + /* tell everyone that something has happened to the sort keys + and let them sync up with the change(s) + this will give objects that manage the sort order keys the + opportunity to keep them in sync if they wish to. + */ + + DEBUG_TRACE (DEBUG::OrderKeys, "Sync Order Keys.\n"); + + Route::SyncOrderKeys (); /* EMIT SIGNAL */ + + DEBUG_TRACE (DEBUG::OrderKeys, "\tsync done\n"); } bool @@ -4766,3 +5006,47 @@ Session::operation_in_progress (GQuark op) const { return (find (_current_trans_quarks.begin(), _current_trans_quarks.end(), op) != _current_trans_quarks.end()); } + +boost::shared_ptr +Session::ltc_input_port () const +{ + return _ltc_input->nth (0); +} + +boost::shared_ptr +Session::ltc_output_port () const +{ + return _ltc_output->nth (0); +} + +void +Session::reconnect_ltc_input () +{ + if (_ltc_input) { + + string src = Config->get_ltc_source_port(); + + _ltc_input->disconnect (this); + + if (src != _("None") && !src.empty()) { + _ltc_input->nth (0)->connect (src); + } + } +} + +void +Session::reconnect_ltc_output () +{ + if (_ltc_output) { + +#if 0 + string src = Config->get_ltc_sink_port(); + + _ltc_output->disconnect (this); + + if (src != _("None") && !src.empty()) { + _ltc_output->nth (0)->connect (src); + } +#endif + } +}