X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=24a531b4245a9bf1fea6c68f848ce5200462e1a3;hb=7a36ce49256a5aa96084b1f94b4c88bceb083fa5;hp=6b8c7f3ed86ce468f85adaf0b80530e8f2bb0c42;hpb=2c0396c9aa762be226d154cc3ed201c39ff6a2ec;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 6b8c7f3ed8..24a531b424 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -37,7 +37,6 @@ #include "pbd/basename.h" #include "pbd/convert.h" -#include "pbd/convert.h" #include "pbd/error.h" #include "pbd/file_utils.h" #include "pbd/md5.h" @@ -82,6 +81,7 @@ #include "ardour/midi_ui.h" #include "ardour/operations.h" #include "ardour/playlist.h" +#include "ardour/playlist_factory.h" #include "ardour/plugin.h" #include "ardour/plugin_insert.h" #include "ardour/process_thread.h" @@ -114,7 +114,7 @@ #include "LuaBridge/LuaBridge.h" -#include "i18n.h" +#include "pbd/i18n.h" #include @@ -178,6 +178,7 @@ Session::Session (AudioEngine &eng, , _record_status (Disabled) , _transport_frame (0) , _session_range_location (0) + , _session_range_end_is_free (true) , _slave (0) , _silent (false) , _transport_speed (0) @@ -212,13 +213,15 @@ Session::Session (AudioEngine &eng, , post_export_position (0) , _exporting (false) , _export_rolling (false) + , _realtime_export (false) + , _region_export (false) , _export_preroll (0) + , _export_latency (0) , _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) @@ -237,7 +240,7 @@ Session::Session (AudioEngine &eng, , pending_locate_flush (false) , pending_abort (false) , pending_auto_loop (false) - , _mempool ("Session", 1048576) + , _mempool ("Session", 2097152) , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool)) , _n_lua_scripts (0) , _butler (new Butler (*this)) @@ -248,11 +251,12 @@ Session::Session (AudioEngine &eng, , _ignore_skips_updates (false) , _rt_thread_active (false) , _rt_emit_pending (false) - , _ac_thread_active (false) + , _ac_thread_active (0) + , _latency_recompute_pending (0) , step_speed (0) , outbound_mtc_timecode_frame (0) , next_quarter_frame_to_send (-1) - , _frames_per_timecode_frame (0) + , _samples_per_timecode_frame (0) , _frames_per_hour (0) , _timecode_frames_per_hour (0) , last_timecode_valid (false) @@ -296,9 +300,12 @@ Session::Session (AudioEngine &eng, , click_length (0) , click_emphasis_length (0) , _clicks_cleared (0) + , _count_in_samples (0) , _play_range (false) , _range_selection (-1,-1) , _object_selection (-1,-1) + , _preroll_record_punch_pos (-1) + , _preroll_record_trim_len (0) , main_outs (0) , first_file_data_format_reset (true) , first_file_header_format_reset (true) @@ -308,7 +315,7 @@ Session::Session (AudioEngine &eng, , _step_editors (0) , _suspend_timecode_transmission (0) , _speakers (new Speakers) - , ignore_route_processor_changes (false) + , _ignore_route_processor_changes (0) , midi_clock (0) , _scene_changer (0) , _midi_ports (0) @@ -328,7 +335,7 @@ Session::Session (AudioEngine &eng, init_name_id_counter (1); // reset for new sessions, start at 1 VCA::set_next_vca_number (1); // reset for new sessions, start at 1 - pre_engine_init (fullpath); + pre_engine_init (fullpath); // sets _is_new setup_lua (); @@ -474,6 +481,8 @@ Session::Session (AudioEngine &eng, } #endif + ensure_subdirs (); // archived or zipped sessions may lack peaks/ analysis/ etc + _is_new = false; session_loaded (); @@ -602,9 +611,6 @@ Session::destroy () _state_of_the_state = StateOfTheState (CannotSave|Deletion); - /* stop autoconnecting */ - auto_connect_thread_terminate (); - /* disconnect from any and all signals that we are connected to */ Port::PortSignalDrop (); /* EMIT SIGNAL */ @@ -616,6 +622,9 @@ Session::destroy () ControlProtocolManager::instance().drop_protocols (); + /* stop autoconnecting */ + auto_connect_thread_terminate (); + MIDI::Name::MidiPatchManager::instance().remove_search_path(session_directory().midi_patch_path()); _engine.remove_session (); @@ -774,6 +783,7 @@ Session::destroy () case SessionEvent::Skip: case SessionEvent::PunchIn: case SessionEvent::PunchOut: + case SessionEvent::RecordStart: case SessionEvent::StopOnce: case SessionEvent::RangeStop: case SessionEvent::RangeLocate: @@ -813,8 +823,10 @@ Session::setup_ltc () { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this); + // TODO use auto-connect thread somehow (needs a route currently) + // see note in Session::auto_connect_thread_run() why process lock is needed. + reconnect_ltc_input (); } - reconnect_ltc_input (); } if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC Out"))) != 0) { @@ -823,8 +835,9 @@ Session::setup_ltc () { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); _ltc_output->ensure_io (ChanCount (DataType::AUDIO, 1), true, this); + // TODO use auto-connect thread + reconnect_ltc_output (); } - reconnect_ltc_output (); } /* fix up names of LTC ports because we don't want the normal @@ -907,6 +920,14 @@ Session::setup_click_state (const XMLNode* node) } } +void +Session::get_physical_ports (vector& inputs, vector& outputs, DataType type, + MidiPortFlags include, MidiPortFlags exclude) +{ + _engine.get_physical_inputs (type, inputs, include, exclude); + _engine.get_physical_outputs (type, outputs, include, exclude); +} + void Session::setup_bundles () { @@ -925,9 +946,12 @@ Session::setup_bundles () 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]); + get_physical_ports (inputs[i], outputs[i], DataType (DataType::Symbol (i)), + MidiPortFlags (0), /* no specific inclusions */ + MidiPortFlags (MidiPortControl|MidiPortVirtual) /* exclude control & virtual ports */ + ); } /* Create a set of Bundle objects that map @@ -1011,6 +1035,7 @@ Session::setup_bundles () for (uint32_t np = 0; np < inputs[DataType::MIDI].size(); ++np) { string n = inputs[DataType::MIDI][np]; + std::string pn = _engine.get_pretty_name_by_name (n); if (!pn.empty()) { n = pn; @@ -1113,7 +1138,7 @@ Session::remove_monitor_section () boost::shared_ptr r = routes.reader (); - PBD::Unwinder uw (ignore_route_processor_changes, true); + ProcessorChangeBlocker pcb (this, false); for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { @@ -1273,7 +1298,7 @@ Session::add_monitor_section () boost::shared_ptr rls = routes.reader (); - PBD::Unwinder uw (ignore_route_processor_changes, true); + ProcessorChangeBlocker pcb (this, false /* XXX */); for (RouteList::iterator x = rls->begin(); x != rls->end(); ++x) { @@ -1314,8 +1339,12 @@ Session::reset_monitor_section () _master_out->output()->disconnect (this); _monitor_out->output()->disconnect (this); - _monitor_out->input()->ensure_io (_master_out->output()->n_ports(), false, this); - _monitor_out->output()->ensure_io (_master_out->output()->n_ports(), false, this); + // monitor section follow master bus - except midi + ChanCount mon_chn (_master_out->output()->n_ports()); + mon_chn.set_midi (0); + + _monitor_out->input()->ensure_io (mon_chn, false, this); + _monitor_out->output()->ensure_io (mon_chn, false, this); for (uint32_t n = 0; n < limit; ++n) { boost::shared_ptr p = _monitor_out->input()->ports().nth_audio_port (n); @@ -1393,7 +1422,7 @@ Session::reset_monitor_section () boost::shared_ptr rls = routes.reader (); - PBD::Unwinder uw (ignore_route_processor_changes, true); + ProcessorChangeBlocker pcb (this, false); for (RouteList::iterator x = rls->begin(); x != rls->end(); ++x) { @@ -1656,7 +1685,7 @@ Session::set_session_extents (framepos_t start, framepos_t end) Location* existing; if ((existing = _locations->session_range_location()) == 0) { //if there is no existing session, we need to make a new session location (should never happen) - existing = new Location (*this, 0, 0, _("session"), Location::IsSessionRange); + existing = new Location (*this, 0, 0, _("session"), Location::IsSessionRange, 0); } if (end <= start) { @@ -1973,6 +2002,7 @@ Session::disable_record (bool rt_context, bool force) if (!rt_context) { remove_pending_capture_state (); } + unset_preroll_record_punch (); } } @@ -1990,7 +2020,7 @@ Session::step_back_from_record () } void -Session::maybe_enable_record () +Session::maybe_enable_record (bool rt_context) { if (_step_editors > 0) { return; @@ -1999,15 +2029,18 @@ Session::maybe_enable_record () 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. 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. - */ + * (except maybe lua scripts, which can use rt_context = true) + * 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 (!rt_context) { + save_state ("", true); + } if (_transport_speed) { - if (!config.get_punch_in()) { + if (!config.get_punch_in() && !preroll_record_punch_enabled ()) { enable_record (); } } else { @@ -2022,59 +2055,71 @@ framepos_t Session::audible_frame () const { framepos_t ret; - framepos_t tf; - framecnt_t offset; - offset = worst_playback_latency (); + frameoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start (); + offset *= transport_speed (); if (synced_to_engine()) { /* Note: this is basically just sync-to-JACK */ - tf = _engine.transport_frame(); + ret = _engine.transport_frame(); } else { - tf = _transport_frame; + ret = _transport_frame; } - ret = tf; - - if (!non_realtime_work_pending()) { - - /* MOVING */ + if (transport_rolling()) { + ret -= offset; /* Check to see if we have passed the first guaranteed - audible frame past our last start position. if not, - return that last start point because in terms - of audible frames, we have not moved yet. - - `Start position' in this context means the time we last - either started, located, or changed transport direction. - */ + * audible frame past our last start position. if not, + * return that last start point because in terms + * of audible frames, we have not moved yet. + * + * `Start position' in this context means the time we last + * either started, located, or changed transport direction. + */ if (_transport_speed > 0.0f) { if (!play_loop || !have_looped) { - if (tf < _last_roll_or_reversal_location + offset) { + if (ret < _last_roll_or_reversal_location) { return _last_roll_or_reversal_location; } + } else { + // latent loops + Location *location = _locations->auto_loop_location(); + frameoffset_t lo = location->start() - ret; + if (lo > 0) { + ret = location->end () - lo; + } } - - /* forwards */ - ret -= offset; - } else if (_transport_speed < 0.0f) { /* XXX wot? no backward looping? */ - if (tf > _last_roll_or_reversal_location - offset) { + if (ret > _last_roll_or_reversal_location) { return _last_roll_or_reversal_location; - } else { - /* backwards */ - ret += offset; } } } - return ret; + return std::max ((framepos_t)0, ret); +} + + +framecnt_t +Session::preroll_samples (framepos_t pos) const +{ + const float pr = Config->get_preroll_seconds(); + if (pos >= 0 && pr < 0) { + const Tempo& tempo = _tempo_map->tempo_at_frame (pos); + const Meter& meter = _tempo_map->meter_at_frame (pos); + return meter.frames_per_bar (tempo, frame_rate()) * -pr; + } + if (pr < 0) { + return 0; + } + return pr * frame_rate(); } void @@ -2349,12 +2394,12 @@ Session::find_route_name (string const & base, uint32_t& id, string& name, bool before anything else. */ - for (vector::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) { - if (base == *reserved) { + for (map::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) { + if (base == reserved->first) { /* Check if this reserved name already exists, and if so, disallow it without a numeric suffix. */ - if (route_by_name (*reserved)) { + if (!reserved->second || route_by_name (reserved->first)) { definitely_add_number = true; if (id < 1) { id = 1; @@ -2364,8 +2409,11 @@ Session::find_route_name (string const & base, uint32_t& id, string& name, bool } } - if (!definitely_add_number && route_by_name (base) == 0) { - /* juse use the base */ + /* if we have "base 1" already, it doesn't make sense to add "base" + * if "base 1" has been deleted, adding "base" is no worse than "base 1" + */ + if (!definitely_add_number && route_by_name (base) == 0 && (route_by_name (string_compose("%1 1", base)) == 0)) { + /* just use the base */ name = base; return true; } @@ -2426,9 +2474,10 @@ Session::default_track_name_pattern (DataType t) * @param instrument plugin info for the instrument to insert pre-fader, if any */ list > -Session::new_midi_track (const ChanCount& input, const ChanCount& output, +Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool strict_io, boost::shared_ptr instrument, Plugin::PresetRecord* pset, - RouteGroup* route_group, uint32_t how_many, string name_template, PresentationInfo::order_t order, + RouteGroup* route_group, uint32_t how_many, + string name_template, PresentationInfo::order_t order, TrackMode mode) { string track_name; @@ -2455,7 +2504,7 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, goto failed; } - if (Profile->get_mixbus ()) { + if (strict_io) { track->set_strict_io (true); } @@ -2514,12 +2563,23 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, if (instrument) { for (RouteList::iterator r = new_routes.begin(); r != new_routes.end(); ++r) { PluginPtr plugin = instrument->load (*this); + if (!plugin) { + warning << "Failed to add Synth Plugin to newly created track." << endmsg; + continue; + } if (pset) { plugin->load_preset (*pset); } - boost::shared_ptr p (new PluginInsert (*this, plugin)); - (*r)->add_processor (p, PreFader); + boost::shared_ptr pi (new PluginInsert (*this, plugin)); + if (strict_io) { + pi->set_strict_io (true); + } + (*r)->add_processor (pi, PreFader); + + if (Profile->get_mixbus () && pi->configured () && pi->output_streams().n_audio() > 2) { + (*r)->move_instrument_down (false); + } } } } @@ -2528,7 +2588,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, } RouteList -Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr instrument, Plugin::PresetRecord* pset, +Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, bool strict_io, + boost::shared_ptr instrument, Plugin::PresetRecord* pset, PresentationInfo::Flag flag, PresentationInfo::order_t order) { string bus_name; @@ -2551,7 +2612,7 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name goto failure; } - if (Profile->get_mixbus ()) { + if (strict_io) { bus->set_strict_io (true); } @@ -2576,6 +2637,7 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name route_group->add (bus); } + bus->add_internal_return (); ret.push_back (bus); } @@ -2601,11 +2663,23 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name if (instrument) { for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) { PluginPtr plugin = instrument->load (*this); + if (!plugin) { + warning << "Failed to add Synth Plugin to newly created track." << endmsg; + continue; + } if (pset) { plugin->load_preset (*pset); } - boost::shared_ptr p (new PluginInsert (*this, plugin)); - (*r)->add_processor (p, PreFader); + boost::shared_ptr pi (new PluginInsert (*this, plugin)); + if (strict_io) { + pi->set_strict_io (true); + } + + (*r)->add_processor (pi, PreFader); + + if (Profile->get_mixbus () && pi->configured () && pi->output_streams().n_audio() > 2) { + (*r)->move_instrument_down (false); + } } } } @@ -2934,17 +3008,18 @@ Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new /* create a gap in the presentation info to accomodate @param how_many * new objects. */ - boost::shared_ptr rd = routes.reader(); + StripableList sl; + get_stripables (sl); - for (RouteList::iterator ri = rd->begin(); ri != rd->end(); ++ri) { - boost::shared_ptr rt (*ri); + for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) { + boost::shared_ptr s (*si); - if (rt->presentation_info().special()) { + if (s->is_monitor() || s->is_auditioner()) { continue; } - if (rt->presentation_info().order () >= first_new_order) { - rt->set_presentation_order (rt->presentation_info().order () + how_many); + if (s->presentation_info().order () >= first_new_order) { + s->set_presentation_order (s->presentation_info().order () + how_many); } } } @@ -3152,7 +3227,8 @@ 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, const std::string& name_base, PlaylistDisposition pd) +Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t insert_at, const std::string& template_path, const std::string& name_base, + PlaylistDisposition pd) { XMLTree tree; @@ -3160,11 +3236,11 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template return RouteList(); } - return new_route_from_template (how_many, *tree.root(), name_base, pd); + return new_route_from_template (how_many, insert_at, *tree.root(), name_base, pd); } RouteList -Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::string& name_base, PlaylistDisposition pd) +Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t insert_at, XMLNode& node, const std::string& name_base, PlaylistDisposition pd) { RouteList ret; uint32_t number = 0; @@ -3211,17 +3287,27 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s /* set this name in the XML description that we are about to use */ - bool rename_playlist; - switch (pd) { - case NewPlaylist: - rename_playlist = true; - break; - default: - case CopyPlaylist: - case SharePlaylist: - rename_playlist = false; + if (pd == CopyPlaylist) { + XMLNode* ds_node = find_named_node (node_copy, "Diskstream"); + if (ds_node) { + const std::string playlist_name = ds_node->property (X_("playlist"))->value (); + boost::shared_ptr playlist = playlists->by_name (playlist_name); + // Use same name as Route::set_name_in_state so playlist copy + // is picked up when creating the Route in XMLRouteFactory below + playlist = PlaylistFactory::create (playlist, string_compose ("%1.1", name)); + playlist->reset_shares (); + } + } else if (pd == SharePlaylist) { + XMLNode* ds_node = find_named_node (node_copy, "Diskstream"); + if (ds_node) { + const std::string playlist_name = ds_node->property (X_("playlist"))->value (); + boost::shared_ptr playlist = playlists->by_name (playlist_name); + playlist->share_with ((node_copy.property (X_("id")))->value()); + } } + bool rename_playlist = (pd == CopyPlaylist || pd == NewPlaylist); + Route::set_name_in_state (node_copy, name, rename_playlist); /* trim bitslots from listen sends so that new ones are used */ @@ -3299,21 +3385,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s route->output()->changed (change, this); } - boost::shared_ptr track; - - if ((track = boost::dynamic_pointer_cast (route))) { - switch (pd) { - case NewPlaylist: - track->use_new_playlist (); - break; - case CopyPlaylist: - track->use_copy_playlist (); - break; - case SharePlaylist: - break; - } - }; - ret.push_back (route); } @@ -3334,9 +3405,9 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s if (!ret.empty()) { StateProtector sp (this); if (Profile->get_trx()) { - add_routes (ret, false, false, false, PresentationInfo::max_order); + add_routes (ret, false, false, false, insert_at); } else { - add_routes (ret, true, true, false, PresentationInfo::max_order); + add_routes (ret, true, true, false, insert_at); } IO::enable_connecting (); } @@ -3412,6 +3483,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool 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->processor_latency_changed.connect_same_thread (*this, boost::bind (&Session::queue_latency_recompute, this)); if (r->is_master()) { _master_out = r; @@ -3431,6 +3503,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool if (mt) { mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1)); mt->output()->changed.connect_same_thread (*this, boost::bind (&Session::midi_output_change_handler, this, _1, _2, boost::weak_ptr(mt))); + mt->presentation_info().PropertyChanged.connect_same_thread (*this, boost::bind (&Session::midi_track_presentation_info_changed, this, _1, boost::weak_ptr(mt))); } } @@ -3444,7 +3517,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool if (order == PresentationInfo::max_order) { /* just add to the end */ - r->set_presentation_order_explicit (n_routes + added); + r->set_presentation_order (n_routes + added, false); DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to NR %1 + %2 = %3\n", n_routes, added, n_routes + added)); } else { r->set_presentation_order (order + added); @@ -3455,7 +3528,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool } } -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__FreeBSD__) /* clang complains: 'operator<<' should be declared prior to the call site or in an associated namespace of one of its * arguments std::ostream& operator<<(std::ostream& o, ARDOUR::PresentationInfo const& rid)" */ @@ -3594,7 +3667,10 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) continue; } - (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup); + /* speed up session deletion, don't do the solo dance */ + if (0 == (_state_of_the_state & Deletion)) { + (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup); + } rs->remove (*iter); @@ -3631,7 +3707,7 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) /* if the monitoring section had a pointer to this route, remove it */ if (_monitor_out && !(*iter)->is_master() && !(*iter)->is_monitor()) { Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - PBD::Unwinder uw (ignore_route_processor_changes, true); + ProcessorChangeBlocker pcb (this, false); (*iter)->remove_aux_or_listen (_monitor_out); } @@ -3677,6 +3753,7 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) */ for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) { + cerr << "Drop references to " << (*iter)->name() << endl; (*iter)->drop_references (); } @@ -3758,8 +3835,6 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr _listen_cnt--; } - - update_route_solo_state (); } void @@ -3793,7 +3868,7 @@ Session::route_solo_isolated_changed (boost::weak_ptr wpr) void Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlDisposition group_override, boost::weak_ptr wpr) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_changed)); + DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1, update\n", self_solo_changed)); boost::shared_ptr route (wpr.lock()); @@ -3809,21 +3884,13 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: self %2 masters %3 transition %4\n", route->name(), route->self_soloed(), route->solo_control()->get_masters_value(), route->solo_control()->transitioned_into_solo())); if (route->solo_control()->transitioned_into_solo() == 0) { - /* route solo changed by upstream/downstream; not interesting + /* route solo changed by upstream/downstream or clear all solo state; not interesting to Session. */ DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 not self-soloed nor soloed by master (%2), ignoring\n", route->name(), route->solo_control()->get_masters_value())); return; } - if (route->solo_control()->transitioned_into_solo() == 0) { - /* reason for being soloed changed (e.g. master went away, we - * took over the master state), but actual status did - * not. nothing to do. - */ - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: solo change was change in reason, not status\n", route->name())); - } - boost::shared_ptr r = routes.reader (); int32_t delta = route->solo_control()->transitioned_into_solo (); @@ -3846,6 +3913,8 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD RouteGroup* rg = route->route_group (); const bool group_already_accounted_for = (group_override == Controllable::ForGroup); + DEBUG_TRACE (DEBUG::Solo, string_compose ("propagate to session, group accounted for ? %1\n", group_already_accounted_for)); + if (delta == 1 && Config->get_exclusive_solo()) { /* new solo: disable all other solos, but not the group if its solo-enabled */ @@ -3892,6 +3961,8 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 excluded from solo because iso = %2 can_solo = %3\n", (*i)->name(), (*i)->solo_isolate_control()->solo_isolated(), + (*i)->can_solo())); continue; } @@ -3957,8 +4028,6 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD DEBUG_TRACE (DEBUG::Solo, "propagation complete\n"); - 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. */ @@ -3966,11 +4035,10 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) { DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1, which neither feeds or is fed by %2\n", (*i)->name(), route->name())); (*i)->act_on_mute (); - (*i)->mute_control()->Changed (false, Controllable::NoGroup); + /* Session will emit SoloChanged() after all solo changes are + * complete, which should be used by UIs to update mute status + */ } - - SoloChanged (); /* EMIT SIGNAL */ - set_dirty(); } void @@ -3990,13 +4058,13 @@ Session::update_route_solo_state (boost::shared_ptr r) for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if ((*i)->can_solo()) { if (Config->get_solo_control_is_listen_control()) { - if ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value()) { + if ((*i)->solo_control()->soloed_by_self_or_masters()) { listeners++; something_listening = true; } } else { (*i)->set_listen (false); - if ((*i)->can_solo() && ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value())) { + if ((*i)->can_solo() && (*i)->solo_control()->soloed_by_self_or_masters()) { something_soloed = true; } } @@ -4026,6 +4094,10 @@ Session::update_route_solo_state (boost::shared_ptr r) DEBUG_TRACE (DEBUG::Solo, string_compose ("solo state updated by session, soloed? %1 listeners %2 isolated %3\n", something_soloed, listeners, isolated)); + + + SoloChanged (); /* EMIT SIGNAL */ + set_dirty(); } void @@ -4057,9 +4129,9 @@ Session::io_name_is_legal (const std::string& name) const { boost::shared_ptr r = routes.reader (); - for (vector::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) { - if (name == *reserved) { - if (!route_by_name (*reserved)) { + for (map::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) { + if (name == reserved->first) { + if (!route_by_name (reserved->first)) { /* first instance of a reserved name is allowed */ return true; } @@ -4225,12 +4297,6 @@ Session::get_remote_nth_route (PresentationInfo::order_t n) const return boost::dynamic_pointer_cast (get_remote_nth_stripable (n, PresentationInfo::Route)); } -struct GlobalPresentationOrderSorter { - bool operator() (boost::shared_ptr a, boost::shared_ptr b) { - return a->presentation_info() < b->presentation_info(); - } -}; - boost::shared_ptr Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo::Flag flags) const { @@ -4238,9 +4304,27 @@ Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo PresentationInfo::order_t match_cnt = 0; get_stripables (sl); - sl.sort (GlobalPresentationOrderSorter()); + sl.sort (Stripable::PresentationOrderSorter()); for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) { + + if ((*s)->presentation_info().hidden()) { + /* if the caller didn't explicitly ask for hidden + stripables, ignore hidden ones. This matches + the semantics of the pre-PresentationOrder + "get by RID" logic of Ardour 4.x and earlier. + + XXX at some point we should likely reverse + the logic of the flags, because asking for "the + hidden stripables" is not going to be common, + whereas asking for visible ones is normal. + */ + + if (! (flags & PresentationInfo::Hidden)) { + continue; + } + } + if ((*s)->presentation_info().flag_match (flags)) { if (match_cnt++ == n) { return *s; @@ -4386,12 +4470,18 @@ Session::maybe_update_session_range (framepos_t a, framepos_t b) _session_range_location->set_start (a); } - if (b > _session_range_location->end()) { + if (_session_range_end_is_free && (b > _session_range_location->end())) { _session_range_location->set_end (b); } } } +void +Session::set_end_is_free (bool yn) +{ + _session_range_end_is_free = yn; +} + void Session::playlist_ranges_moved (list > const & ranges) { @@ -5170,6 +5260,7 @@ Session::try_run_lua (pframes_t nframes) Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK); if (tm.locked ()) { try { (*_lua_run)(nframes); } catch (luabridge::LuaException const& e) { } + lua.collect_garbage_step (); } } @@ -5179,6 +5270,7 @@ Session::setup_lua () #ifndef NDEBUG lua.Print.connect (&_lua_print); #endif + lua.tweak_rt_gc (); lua.do_command ( "function ArdourSession ()" " local self = { scripts = {}, instances = {} }" @@ -5196,7 +5288,7 @@ Session::setup_lua () " assert(self.scripts[n] == nil, 'Callback \"'.. n ..'\" already exists.')" " self.scripts[n] = { ['f'] = f, ['a'] = a }" " local env = _ENV; env.f = nil env.io = nil env.os = nil env.loadfile = nil env.require = nil env.dofile = nil env.package = nil env.debug = nil" - " local env = { print = print, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall, Session = Session, PBD = PBD, Timecode = Timecode, Evoral = Evoral, C = C, ARDOUR = ARDOUR }" + " local env = { print = print, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall, bit32=bit32, Session = Session, PBD = PBD, Timecode = Timecode, Evoral = Evoral, C = C, ARDOUR = ARDOUR }" " self.instances[n] = load (string.dump(f, true), nil, nil, env)(a)" " Session:scripts_changed()" // call back " end" @@ -5370,7 +5462,7 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost:: if (b->is_monitor()) { return false; } - return a->presentation_info() < b->presentation_info(); + return a->presentation_info().order() < b->presentation_info().order(); } bool @@ -5539,7 +5631,7 @@ void Session::update_locations_after_tempo_map_change (const Locations::LocationList& loc) { for (Locations::LocationList::const_iterator i = loc.begin(); i != loc.end(); ++i) { - (*i)->recompute_frames_from_bbt (); + (*i)->recompute_frames_from_beat (); } } @@ -6169,8 +6261,8 @@ Session::update_route_record_state () void Session::listen_position_changed () { + ProcessorChangeBlocker pcb (this); boost::shared_ptr r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->listen_position_changed (); } @@ -6180,14 +6272,18 @@ void Session::solo_control_mode_changed () { if (soloing() || listening()) { - /* We can't use ::clear_all_solo_state() here because during - session loading at program startup, that will queue a call - to rt_clear_all_solo_state() that will not execute until - AFTER solo states have been established (thus throwing away - the session's saved solo state). So just explicitly turn - them all off. - */ - set_controls (route_list_to_control_list (get_routes(), &Stripable::solo_control), 0.0, Controllable::NoGroup); + if (loading()) { + /* We can't use ::clear_all_solo_state() here because during + session loading at program startup, that will queue a call + to rt_clear_all_solo_state() that will not execute until + AFTER solo states have been established (thus throwing away + the session's saved solo state). So just explicitly turn + them all off. + */ + set_controls (route_list_to_control_list (get_routes(), &Stripable::solo_control), 0.0, Controllable::NoGroup); + } else { + clear_all_solo_state (get_routes()); + } } } @@ -6211,6 +6307,10 @@ Session::route_removed_from_route_group (RouteGroup* rg, boost::weak_ptr { update_route_record_state (); RouteRemovedFromRouteGroup (rg, r); /* EMIT SIGNAL */ + + if (!rg->has_control_master () && !rg->has_subgroup () && rg->empty()) { + remove_route_group (*rg); + } } boost::shared_ptr @@ -6265,12 +6365,12 @@ Session::goto_end () } void -Session::goto_start () +Session::goto_start (bool and_roll) { if (_session_range_location) { - request_locate (_session_range_location->start(), false); + request_locate (_session_range_location->start(), and_roll); } else { - request_locate (0, false); + request_locate (0, and_roll); } } @@ -6289,7 +6389,7 @@ Session::current_end_frame () const void Session::set_session_range_location (framepos_t start, framepos_t end) { - _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange); + _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange, 0); _locations->add (_session_range_location); } @@ -6336,6 +6436,7 @@ Session::start_time_changed (framepos_t old) if (l && l->start() == old) { l->set_start (s->start(), true); } + set_dirty (); } void @@ -6355,6 +6456,7 @@ Session::end_time_changed (framepos_t old) if (l && l->end() == old) { l->set_end (s->end(), true); } + set_dirty (); } std::vector @@ -6830,12 +6932,25 @@ Session::auto_connect_route (boost::shared_ptr route, bool connect_inputs input_start, output_start, input_offset, output_offset)); + auto_connect_thread_wakeup (); +} + +void +Session::auto_connect_thread_wakeup () +{ if (pthread_mutex_trylock (&_auto_connect_mutex) == 0) { pthread_cond_signal (&_auto_connect_cond); pthread_mutex_unlock (&_auto_connect_mutex); } } +void +Session::queue_latency_recompute () +{ + g_atomic_int_inc (&_latency_recompute_pending); + auto_connect_thread_wakeup (); +} + void Session::auto_connect (const AutoConnectRequest& ar) { @@ -6872,8 +6987,12 @@ Session::auto_connect (const AutoConnectRequest& ar) vector physinputs; vector physoutputs; - _engine.get_physical_outputs (*t, physoutputs); - _engine.get_physical_inputs (*t, physinputs); + + /* for connecting track inputs we only want MIDI ports marked + * for "music". + */ + + get_physical_ports (physinputs, physoutputs, *t, MidiPortMusic); if (!physinputs.empty() && ar.connect_inputs) { uint32_t nphysical_in = physinputs.size(); @@ -6927,7 +7046,7 @@ Session::auto_connect (const AutoConnectRequest& ar) void Session::auto_connect_thread_start () { - if (_ac_thread_active) { + if (g_atomic_int_get (&_ac_thread_active)) { return; } @@ -6935,19 +7054,18 @@ Session::auto_connect_thread_start () _auto_connect_queue.pop (); } - _ac_thread_active = true; + g_atomic_int_set (&_ac_thread_active, 1); if (pthread_create (&_auto_connect_thread, NULL, auto_connect_thread, this)) { - _ac_thread_active = false; + g_atomic_int_set (&_ac_thread_active, 0); } } void Session::auto_connect_thread_terminate () { - if (!_ac_thread_active) { + if (!g_atomic_int_get (&_ac_thread_active)) { return; } - _ac_thread_active = false; { Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock); @@ -6956,10 +7074,14 @@ Session::auto_connect_thread_terminate () } } - if (pthread_mutex_lock (&_auto_connect_mutex) == 0) { - pthread_cond_signal (&_auto_connect_cond); - pthread_mutex_unlock (&_auto_connect_mutex); - } + /* cannot use auto_connect_thread_wakeup() because that is allowed to + * fail to wakeup the thread. + */ + + pthread_mutex_lock (&_auto_connect_mutex); + g_atomic_int_set (&_ac_thread_active, 0); + pthread_cond_signal (&_auto_connect_cond); + pthread_mutex_unlock (&_auto_connect_mutex); void *status; pthread_join (_auto_connect_thread, &status); @@ -6981,7 +7103,7 @@ Session::auto_connect_thread_run () SessionEvent::create_per_thread_pool (X_("autoconnect"), 1024); PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("autoconnect"), 1024); pthread_mutex_lock (&_auto_connect_mutex); - while (_ac_thread_active) { + while (g_atomic_int_get (&_ac_thread_active)) { if (!_auto_connect_queue.empty ()) { // Why would we need the process lock ?? @@ -7001,7 +7123,39 @@ Session::auto_connect_thread_run () } } + if (!actively_recording ()) { // might not be needed, + /* this is only used for updating plugin latencies, the + * graph does not change. so it's safe in general. + * BUT.. + * .. update_latency_compensation () entails set_capture_offset() + * which calls Diskstream::set_capture_offset () which + * modifies the capture offset... which can be a proplem + * in "prepare_to_stop" + */ + while (g_atomic_int_and (&_latency_recompute_pending, 0)) { + update_latency_compensation (); + } + } + + { + // this may call ARDOUR::Port::drop ... jack_port_unregister () + // jack1 cannot cope with removing ports while processing + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + AudioEngine::instance()->clear_pending_port_deletions (); + } + pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex); } pthread_mutex_unlock (&_auto_connect_mutex); } + +void +Session::cancel_all_solo () +{ + StripableList sl; + + get_stripables (sl); + + set_controls (stripable_list_to_control_list (sl, &Stripable::solo_control), 0.0, Controllable::NoGroup); + clear_all_solo_state (routes.reader()); +}