X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=6d2ca75719a9c98b91b45b61763f92fead8ed4c1;hb=2fa6314fb44f5136ce726408dc907dc5ff3b6cea;hp=5332650052afeba15c09e6a69166f164e705f097;hpb=7b9784fff4e36eb541e1423013590bc31bde69d5;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 5332650052..6d2ca75719 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -51,7 +51,6 @@ #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" #include "ardour/audio_track.h" #include "ardour/audioengine.h" @@ -66,6 +65,7 @@ #include "ardour/control_protocol_manager.h" #include "ardour/data_type.h" #include "ardour/debug.h" +#include "ardour/disk_reader.h" #include "ardour/directory_names.h" #ifdef USE_TRACKS_CODE_FEATURES #include "ardour/engine_state_controller.h" @@ -93,17 +93,22 @@ #include "ardour/revision.h" #include "ardour/route_graph.h" #include "ardour/route_group.h" +#include "ardour/rt_tasklist.h" #include "ardour/send.h" +#include "ardour/selection.h" #include "ardour/session.h" #include "ardour/session_directory.h" #include "ardour/session_playlists.h" +#include "ardour/slave.h" #include "ardour/smf_source.h" +#include "ardour/slave.h" #include "ardour/solo_isolate_control.h" #include "ardour/source_factory.h" #include "ardour/speakers.h" #include "ardour/tempo.h" #include "ardour/ticker.h" #include "ardour/track.h" +#include "ardour/types_convert.h" #include "ardour/user_bundle.h" #include "ardour/utils.h" #include "ardour/vca_manager.h" @@ -135,13 +140,13 @@ guint Session::_name_id_counter = 0; PBD::Signal1 Session::AudioEngineSetupRequired; PBD::Signal1 Session::Dialog; PBD::Signal0 Session::AskAboutPendingState; -PBD::Signal2 Session::AskAboutSampleRateMismatch; -PBD::Signal2 Session::NotifyAboutSampleRateMismatch; +PBD::Signal2 Session::AskAboutSampleRateMismatch; +PBD::Signal2 Session::NotifyAboutSampleRateMismatch; PBD::Signal0 Session::SendFeedback; PBD::Signal3 Session::MissingFile; -PBD::Signal1 Session::StartTimeChanged; -PBD::Signal1 Session::EndTimeChanged; +PBD::Signal1 Session::StartTimeChanged; +PBD::Signal1 Session::EndTimeChanged; PBD::Signal2 Session::Exported; PBD::Signal1 > Session::AskAboutPlaylistDeletion; PBD::Signal0 Session::Quit; @@ -149,7 +154,7 @@ PBD::Signal0 Session::FeedbackDetected; PBD::Signal0 Session::SuccessfulGraphSort; PBD::Signal2 Session::VersionMismatch; -const framecnt_t Session::bounce_chunk_size = 8192; +const samplecnt_t Session::bounce_chunk_size = 8192; static void clean_up_session_event (SessionEvent* ev) { delete ev; } const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event); @@ -171,28 +176,32 @@ Session::Session (AudioEngine &eng, , process_function (&Session::process_with_events) , _bounce_processing_active (false) , waiting_for_sync_offset (false) - , _base_frame_rate (0) - , _nominal_frame_rate (0) - , _current_frame_rate (0) + , _base_sample_rate (0) + , _nominal_sample_rate (0) + , _current_sample_rate (0) , transport_sub_state (0) , _record_status (Disabled) - , _transport_frame (0) + , _transport_sample (0) + , _seek_counter (0) , _session_range_location (0) , _session_range_end_is_free (true) , _slave (0) , _silent (false) + , _remaining_latency_preroll (0) + , _engine_speed (1.0) , _transport_speed (0) , _default_transport_speed (1.0) , _last_transport_speed (0) + , _signalled_varispeed (0) , _target_transport_speed (0.0) , auto_play_legal (false) - , _last_slave_transport_frame (0) - , maximum_output_latency (0) - , _requested_return_frame (-1) + , _last_slave_transport_sample (0) + , _requested_return_sample (-1) , current_block_size (0) , _worst_output_latency (0) , _worst_input_latency (0) - , _worst_track_latency (0) + , _worst_route_latency (0) + , _send_latency_changes (0) , _have_captured (false) , _non_soloed_outs_muted (false) , _listening (false) @@ -216,7 +225,6 @@ Session::Session (AudioEngine &eng, , _realtime_export (false) , _region_export (false) , _export_preroll (0) - , _export_latency (0) , _pre_export_mmc_enabled (false) , _name (snapshot_name) , _is_new (true) @@ -236,11 +244,11 @@ Session::Session (AudioEngine &eng, , _last_roll_or_reversal_location (0) , _last_record_location (0) , pending_locate_roll (false) - , pending_locate_frame (0) + , pending_locate_sample (0) , pending_locate_flush (false) , pending_abort (false) , pending_auto_loop (false) - , _mempool ("Session", 2097152) + , _mempool ("Session", 3145728) , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool)) , _n_lua_scripts (0) , _butler (new Butler (*this)) @@ -295,6 +303,7 @@ Session::Session (AudioEngine &eng, , _bundle_xml_node (0) , _current_trans (0) , _clicking (false) + , _click_rec_only (false) , click_data (0) , click_emphasis_data (0) , click_length (0) @@ -304,8 +313,8 @@ Session::Session (AudioEngine &eng, , _play_range (false) , _range_selection (-1,-1) , _object_selection (-1,-1) - , _preroll_record_punch_pos (-1) , _preroll_record_trim_len (0) + , _count_in_once (false) , main_outs (0) , first_file_data_format_reset (true) , first_file_header_format_reset (true) @@ -321,6 +330,8 @@ Session::Session (AudioEngine &eng, , _midi_ports (0) , _mmc (0) , _vca_manager (new VCAManager (*this)) + , _selection (new CoreSelection (*this)) + , _global_locate_pending (false) { uint32_t sr = 0; @@ -354,7 +365,7 @@ Session::Session (AudioEngine &eng, // set samplerate for plugins added early // e.g from templates or MB channelstrip set_block_size (_engine.samples_per_cycle()); - set_frame_rate (_engine.sample_rate()); + set_sample_rate (_engine.sample_rate()); if (create (mix_template, bus_profile)) { destroy (); @@ -373,8 +384,12 @@ Session::Session (AudioEngine &eng, */ if (!mix_template.empty()) { - if (load_state (_current_snapshot_name)) { - throw SessionException (_("Failed to load template/snapshot state")); + try { + if (load_state (_current_snapshot_name)) { + throw SessionException (_("Failed to load template/snapshot state")); + } + } catch (PBD::unknown_enumeration& e) { + throw SessionException (_("Failed to parse template/snapshot state")); } store_recent_templates (mix_template); } @@ -407,9 +422,27 @@ Session::Session (AudioEngine &eng, } } - if (post_engine_init ()) { + int err = post_engine_init (); + if (err) { destroy (); - throw SessionException (_("Cannot configure audio/midi engine with session parameters")); + switch (err) { + case -1: + throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Failed to create background threads."))); + break; + case -2: + case -3: + throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Invalid TempoMap in session-file."))); + break; + case -4: + throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Invalid or corrupt session state."))); + break; + case -5: + throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Port registration failed."))); + break; + default: + throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Unexpected exception during session setup, possibly invalid audio/midi engine parameters. Please see stdout/stderr for details"))); + break; + } } store_recent_sessions (_name, _path); @@ -418,6 +451,8 @@ Session::Session (AudioEngine &eng, _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); + PresentationInfo::Change.connect_same_thread (*this, boost::bind (&Session::notify_presentation_info_change, this)); + Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, false)); config.ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, true)); @@ -428,6 +463,8 @@ Session::Session (AudioEngine &eng, StartTimeChanged.connect_same_thread (*this, boost::bind (&Session::start_time_changed, this, _1)); EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1)); + Send::ChangedLatency.connect_same_thread (*this, boost::bind (&Session::send_latency_compensation_change, this)); + emit_thread_start (); auto_connect_thread_start (); @@ -483,8 +520,16 @@ Session::Session (AudioEngine &eng, ensure_subdirs (); // archived or zipped sessions may lack peaks/ analysis/ etc - _is_new = false; + if (!mix_template.empty ()) { + /* ::create() unsets _is_new after creating the session. + * But for templated sessions, the sample-rate is initially unset + * (not read from template), so we need to save it (again). + */ + _is_new = true; + } + session_loaded (); + _is_new = false; BootMessage (_("Session loading complete")); } @@ -556,6 +601,8 @@ Session::immediately_post_engine () * session or set state for an existing one. */ + _rt_tasklist.reset (new RTTaskList ()); + 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. @@ -572,11 +619,10 @@ Session::immediately_post_engine () } if (config.get_jack_time_master()) { - _engine.transport_locate (_transport_frame); + _engine.transport_locate (_transport_sample); } try { - LocaleGuard lg; BootMessage (_("Set up LTC")); setup_ltc (); BootMessage (_("Set up Click")); @@ -611,18 +657,30 @@ Session::destroy () _state_of_the_state = StateOfTheState (CannotSave|Deletion); + { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + ltc_tx_cleanup(); + delete _slave; + _slave = 0; + } + /* disconnect from any and all signals that we are connected to */ Port::PortSignalDrop (); /* EMIT SIGNAL */ drop_connections (); /* shutdown control surface protocols while we still have ports - and the engine to move data to any devices. - */ + * and the engine to move data to any devices. + */ + + /* remove I/O objects before unsetting the engine session */ + _click_io.reset (); + _ltc_input.reset (); + _ltc_output.reset (); ControlProtocolManager::instance().drop_protocols (); - /* stop autoconnecting */ + /* stop auto dis/connecting */ auto_connect_thread_terminate (); MIDI::Name::MidiPatchManager::instance().remove_search_path(session_directory().midi_patch_path()); @@ -633,14 +691,18 @@ Session::destroy () EngineStateController::instance()->remove_session(); #endif + /* drop slave, if any. We don't use use_sync_source (0) because + * there's no reason to do all the other stuff that may happen + * when calling that method. + */ + delete _slave; + /* 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 (); @@ -650,17 +712,20 @@ Session::destroy () delete state_tree; state_tree = 0; - // unregister all lua functions, drop held references (if any) - (*_lua_cleanup)(); - lua.do_command ("Session = nil"); - delete _lua_run; - delete _lua_add; - delete _lua_del; - delete _lua_list; - delete _lua_save; - delete _lua_load; - delete _lua_cleanup; - lua.collect_garbage (); + { + /* unregister all lua functions, drop held references (if any) */ + Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK); + (*_lua_cleanup)(); + lua.do_command ("Session = nil"); + delete _lua_run; + delete _lua_add; + delete _lua_del; + delete _lua_list; + delete _lua_save; + delete _lua_load; + delete _lua_cleanup; + lua.collect_garbage (); + } /* reset dynamic state version back to default */ Stateful::loading_state_version = 0; @@ -687,7 +752,13 @@ Session::destroy () clear_clicks (); /* need to remove auditioner before monitoring section - * otherwise it is re-connected */ + * otherwise it is re-connected. + * Note: If a session was never successfully loaded, there + * may not yet be an auditioner. + */ + if (auditioner) { + auditioner->drop_references (); + } auditioner.reset (); /* drop references to routes held by the monitoring section @@ -699,7 +770,7 @@ Session::destroy () routes.flush (); _bundles.flush (); - AudioDiskstream::free_working_buffers(); + DiskReader::free_working_buffers(); /* tell everyone who is still standing that we're about to die */ drop_references (); @@ -783,7 +854,6 @@ Session::destroy () case SessionEvent::Skip: case SessionEvent::PunchIn: case SessionEvent::PunchOut: - case SessionEvent::RecordStart: case SessionEvent::StopOnce: case SessionEvent::RangeStop: case SessionEvent::RangeLocate: @@ -804,6 +874,18 @@ Session::destroy () } } + { + /* unregister all dropped ports, process pending port deletion. */ + // 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 (); + } + + DEBUG_TRACE (DEBUG::Destruction, "delete selection\n"); + delete _selection; + _selection = 0; + DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n"); BOOST_SHOW_POINTERS (); @@ -1436,6 +1518,33 @@ Session::reset_monitor_section () } } +int +Session::add_master_bus (ChanCount const& count) +{ + if (master_out ()) { + return -1; + } + + RouteList rl; + + boost::shared_ptr r (new Route (*this, _("Master"), PresentationInfo::MasterOut, DataType::AUDIO)); + if (r->init ()) { + return -1; + } + + BOOST_MARK_ROUTE(r); + + { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + r->input()->ensure_io (count, false, this); + r->output()->ensure_io (count, false, this); + } + + rl.push_back (r); + add_routes (rl, false, false, false, PresentationInfo::max_order); + return 0; +} + void Session::hookup_io () { @@ -1456,7 +1565,6 @@ Session::hookup_io () if (a->init()) { throw failed_constructor (); } - a->use_new_diskstream (); auditioner = a; } @@ -1551,7 +1659,7 @@ Session::auto_punch_start_changed (Location* location) { replace_event (SessionEvent::PunchIn, location->start()); - if (get_record_enabled() && config.get_punch_in()) { + if (get_record_enabled() && config.get_punch_in() && !actively_recording ()) { /* capture start has been changed, so save new pending state */ save_state ("", true); } @@ -1560,27 +1668,22 @@ Session::auto_punch_start_changed (Location* location) void Session::auto_punch_end_changed (Location* location) { - framepos_t when_to_stop = location->end(); - // when_to_stop += _worst_output_latency + _worst_input_latency; - replace_event (SessionEvent::PunchOut, when_to_stop); + replace_event (SessionEvent::PunchOut, location->end()); } void Session::auto_punch_changed (Location* location) { - framepos_t when_to_stop = location->end(); - - replace_event (SessionEvent::PunchIn, location->start()); - //when_to_stop += _worst_output_latency + _worst_input_latency; - replace_event (SessionEvent::PunchOut, when_to_stop); + auto_punch_start_changed (location); + auto_punch_end_changed (location); } /** @param loc A loop location. - * @param pos Filled in with the start time of the required fade-out (in session frames). + * @param pos Filled in with the start time of the required fade-out (in session samples). * @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) +Session::auto_loop_declick_range (Location* loc, samplepos_t & pos, samplepos_t & length) { pos = max (loc->start(), loc->end() - 64); length = loc->end() - pos; @@ -1590,17 +1693,17 @@ void Session::auto_loop_changed (Location* location) { replace_event (SessionEvent::AutoLoop, location->end(), location->start()); - framepos_t dcp; - framecnt_t dcl; + samplepos_t dcp; + samplecnt_t dcl; auto_loop_declick_range (location, dcp, dcl); if (transport_rolling() && play_loop) { replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl); - // if (_transport_frame > location->end()) { + // if (_transport_sample > location->end()) { - if (_transport_frame < location->start() || _transport_frame > location->end()) { + if (_transport_sample < location->start() || _transport_sample > location->end()) { // relocate to beginning of loop clear_events (SessionEvent::LocateRoll); @@ -1609,7 +1712,7 @@ Session::auto_loop_changed (Location* location) } else if (Config->get_seamless_loop() && !loop_changing) { - // schedule a locate-roll to refill the diskstreams at the + // schedule a locate-roll to refill the disk readers at the // previous loop end loop_changing = true; @@ -1629,7 +1732,7 @@ Session::auto_loop_changed (Location* location) to the loop start on stop if that is appropriate. */ - framepos_t pos; + samplepos_t pos; if (!transport_rolling() && select_playhead_priority_target (pos)) { if (pos == location->start()) { @@ -1650,7 +1753,7 @@ Session::set_auto_punch_location (Location* location) if ((existing = _locations->auto_punch_location()) != 0 && existing != location) { punch_connections.drop_connections(); existing->set_auto_punch (false, this); - remove_event (existing->start(), SessionEvent::PunchIn); + clear_events (SessionEvent::PunchIn); clear_events (SessionEvent::PunchOut); auto_punch_location_changed (0); } @@ -1680,7 +1783,7 @@ Session::set_auto_punch_location (Location* location) } void -Session::set_session_extents (framepos_t start, framepos_t end) +Session::set_session_extents (samplepos_t start, samplepos_t end) { Location* existing; if ((existing = _locations->session_range_location()) == 0) { @@ -1707,8 +1810,8 @@ 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; + samplepos_t dcp; + samplecnt_t dcl; auto_loop_declick_range (existing, dcp, dcl); remove_event (dcp, SessionEvent::AutoLoopDeclick); auto_loop_location_changed (0); @@ -1741,7 +1844,7 @@ Session::set_auto_loop_location (Location* location) boost::shared_ptr rl = routes.reader (); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && !tr->hidden()) { + if (tr && !tr->is_private_route()) { tr->set_loop (location); } } @@ -1874,6 +1977,17 @@ Session::location_added (Location *location) location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + } + + if (location->is_range_marker()) { + /* listen for per-location signals that require us to do any * global updates for marks */ + + location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); + location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); } if (location->is_skip()) { @@ -1883,6 +1997,7 @@ Session::location_added (Location *location) location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true)); location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true)); location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, false)); + location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location)); update_skips (location, true); } @@ -1957,7 +2072,7 @@ Session::enable_record () if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) { - _last_record_location = _transport_frame; + _last_record_location = _transport_sample; send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe)); if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { @@ -2002,7 +2117,6 @@ Session::disable_record (bool rt_context, bool force) if (!rt_context) { remove_pending_capture_state (); } - unset_preroll_record_punch (); } } @@ -2040,7 +2154,7 @@ Session::maybe_enable_record (bool rt_context) } if (_transport_speed) { - if (!config.get_punch_in() && !preroll_record_punch_enabled ()) { + if (!config.get_punch_in()) { enable_record (); } } else { @@ -2051,94 +2165,85 @@ Session::maybe_enable_record (bool rt_context) set_dirty(); } -framepos_t -Session::audible_frame () const +samplepos_t +Session::audible_sample (bool* latent_locate) const { - framepos_t ret; + if (latent_locate) { + *latent_locate = false; + } - frameoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start (); - offset *= transport_speed (); + samplepos_t ret; if (synced_to_engine()) { /* Note: this is basically just sync-to-JACK */ - ret = _engine.transport_frame(); + ret = _engine.transport_sample(); } else { - ret = _transport_frame; + ret = _transport_sample; } - 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. - */ + assert (ret >= 0); - if (_transport_speed > 0.0f) { + if (!transport_rolling()) { + return ret; + } - if (!play_loop || !have_looped) { - 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; +#if 0 // TODO looping + if (_transport_speed > 0.0f) { + if (play_loop && have_looped) { + /* the play-position wrapped at the loop-point + * ardour is already playing the beginning of the loop, + * but due to playback latency, the "audible frame" + * is still at the end of the loop. + */ + Location *location = _locations->auto_loop_location(); + sampleoffset_t lo = location->start() - ret; + if (lo > 0) { + ret = location->end () - lo; + if (latent_locate) { + *latent_locate = true; } } - - } else if (_transport_speed < 0.0f) { - - /* XXX wot? no backward looping? */ - - if (ret > _last_roll_or_reversal_location) { - return _last_roll_or_reversal_location; - } } + } else if (_transport_speed < 0.0f) { + /* XXX wot? no backward looping? */ } +#endif - return std::max ((framepos_t)0, ret); + return std::max ((samplepos_t)0, ret); } - -framecnt_t -Session::preroll_samples (framepos_t pos) const +samplecnt_t +Session::preroll_samples (samplepos_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; + const Tempo& tempo = _tempo_map->tempo_at_sample (pos); + const Meter& meter = _tempo_map->meter_at_sample (pos); + return meter.samples_per_bar (tempo, sample_rate()) * -pr; } if (pr < 0) { return 0; } - return pr * frame_rate(); + return pr * sample_rate(); } void -Session::set_frame_rate (framecnt_t frames_per_second) +Session::set_sample_rate (samplecnt_t frames_per_second) { - /** \fn void Session::set_frame_size(framecnt_t) + /** \fn void Session::set_sample_size(samplecnt_t) the AudioEngine object that calls this guarantees that it will not be called while we are also in ::process(). Its fine to do things that block here. */ - if (_base_frame_rate == 0) { - _base_frame_rate = frames_per_second; + if (_base_sample_rate == 0) { + _base_sample_rate = frames_per_second; } - else if (_base_frame_rate != frames_per_second && frames_per_second != _nominal_frame_rate) { - NotifyAboutSampleRateMismatch (_base_frame_rate, frames_per_second); + else if (_base_sample_rate != frames_per_second && frames_per_second != _nominal_sample_rate) { + NotifyAboutSampleRateMismatch (_base_sample_rate, frames_per_second); } - _nominal_frame_rate = frames_per_second; + _nominal_sample_rate = frames_per_second; sync_time_vars(); @@ -2442,11 +2547,13 @@ Session::count_existing_track_channels (ChanCount& in, ChanCount& out) boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && !tr->is_auditioner()) { - in += tr->n_inputs(); - out += tr->n_outputs(); + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (!tr) { + continue; } + assert (!tr->is_auditioner()); // XXX remove me + in += tr->n_inputs(); + out += tr->n_outputs(); } } @@ -2508,8 +2615,6 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s track->set_strict_io (true); } - track->use_new_diskstream(); - BOOST_MARK_TRACK (track); { @@ -2525,14 +2630,10 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s } } - track->non_realtime_input_change(); - if (route_group) { route_group->add (track); } - track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); - new_routes.push_back (track); ret.push_back (track); } @@ -2997,9 +3098,36 @@ Session::reconnect_mmc_ports(bool inputs) #endif +bool +Session::ensure_stripable_sort_order () +{ + StripableList sl; + get_stripables (sl); + sl.sort (Stripable::Sorter ()); + + bool change = false; + PresentationInfo::order_t order = 0; + + for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) { + boost::shared_ptr s (*si); + assert (!s->is_auditioner ()); // XXX remove me + if (s->is_monitor ()) { + continue; + } + if (order != s->presentation_info().order()) { + s->set_presentation_order (order); + change = true; + } + ++order; + } + return change; +} + void Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new_order, uint32_t how_many) { + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("ensure order gap starting at %1 for %2\n", first_new_order, how_many)); + if (first_new_order == PresentationInfo::max_order) { /* adding at end, no worries */ return; @@ -3014,7 +3142,11 @@ Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) { boost::shared_ptr s (*si); - if (s->is_monitor() || s->is_auditioner()) { + if (s->presentation_info().special (false)) { + continue; + } + + if (!s->presentation_info().order_set()) { continue; } @@ -3075,8 +3207,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r } } - track->use_new_diskstream(); - BOOST_MARK_TRACK (track); { @@ -3103,10 +3233,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r route_group->add (track); } - track->non_realtime_input_change(); - - track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this)); - new_routes.push_back (track); ret.push_back (track); } @@ -3286,7 +3412,7 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i } /* set this name in the XML description that we are about to use */ - +#warning fixme -- no more Diskstream if (pd == CopyPlaylist) { XMLNode* ds_node = find_named_node (node_copy, "Diskstream"); if (ds_node) { @@ -3323,12 +3449,12 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i */ XMLProperty const * target = (*i)->property (X_("target")); if (!target) { - (*i)->add_property ("type", "dangling-aux-send"); + (*i)->set_property ("type", "dangling-aux-send"); continue; } boost::shared_ptr r = route_by_id (target->value()); if (!r || boost::dynamic_pointer_cast(r)) { - (*i)->add_property ("type", "dangling-aux-send"); + (*i)->set_property ("type", "dangling-aux-send"); continue; } } @@ -3336,20 +3462,18 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i (*i)->remove_property (X_("bitslot")); } else if (role && (role->value() == X_("Send") || role->value() == X_("Aux"))) { - char buf[32]; Delivery::Role xrole; uint32_t bitslot = 0; xrole = Delivery::Role (string_2_enum (role->value(), xrole)); std::string name = Send::name_and_id_new_send(*this, xrole, bitslot, false); - snprintf (buf, sizeof (buf), "%" PRIu32, bitslot); (*i)->remove_property (X_("bitslot")); (*i)->remove_property (X_("name")); - (*i)->add_property ("bitslot", buf); - (*i)->add_property ("name", name); + (*i)->set_property ("bitslot", bitslot); + (*i)->set_property ("name", name); } else if (type && type->value() == X_("intreturn")) { (*i)->remove_property (X_("bitslot")); - (*i)->add_property ("ignore-bitslot", "1"); + (*i)->set_property ("ignore-bitslot", "1"); } else if (type && type->value() == X_("return")) { // Return::set_state() generates a new one @@ -3358,11 +3482,15 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i else if (type && type->value() == X_("port")) { // PortInsert::set_state() handles the bitslot (*i)->remove_property (X_("bitslot")); - (*i)->add_property ("ignore-name", "1"); + (*i)->set_property ("ignore-name", "1"); } } } + /* new routes start off unsoloed to avoid issues related to + upstream / downstream buses. */ + node_copy.remove_node_and_delete (X_("Controllable"), X_("name"), X_("solo")); + boost::shared_ptr route (XMLRouteFactory (node_copy, 3000)); if (route == 0) { @@ -3428,8 +3556,8 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output graph_reordered (); - update_latency (true); update_latency (false); + update_latency (true); set_dirty(); @@ -3455,8 +3583,8 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool { RCUWriter writer (routes); boost::shared_ptr r = writer.get_copy (); - r->insert (r->end(), new_routes.begin(), new_routes.end()); n_routes = r->size(); + r->insert (r->end(), new_routes.begin(), new_routes.end()); /* if there is no control out and we're not in the middle of loading, * resort the graph here. if there is a control out, we will resort @@ -3469,84 +3597,92 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool } } - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("ensure order gap starting at %1 for %2\n", order, new_routes.size())); - ensure_route_presentation_info_gap (order, new_routes.size()); + /* monitor is not part of the order */ + if (_monitor_out) { + assert (n_routes > 0); + --n_routes; + } - for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) { + { + PresentationInfo::ChangeSuspender cs; + ensure_route_presentation_info_gap (order, new_routes.size()); - boost::weak_ptr wpr (*x); - boost::shared_ptr r (*x); + for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) { - r->solo_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2,wpr)); - r->solo_isolate_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr)); - r->mute_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this)); + boost::weak_ptr wpr (*x); + boost::shared_ptr r (*x); - 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)); + r->solo_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2,wpr)); + r->solo_isolate_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr)); + r->mute_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this)); - if (r->is_master()) { - _master_out = r; - } + 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_monitor()) { - _monitor_out = r; - } + if (r->is_master()) { + _master_out = r; + } - boost::shared_ptr tr = boost::dynamic_pointer_cast (r); - if (tr) { - tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr (tr))); - track_playlist_changed (boost::weak_ptr (tr)); - tr->rec_enable_control()->Changed.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this)); - - boost::shared_ptr mt = boost::dynamic_pointer_cast (tr); - if (mt) { - mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1)); - 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))); + if (r->is_monitor()) { + _monitor_out = r; } - } - if (!r->presentation_info().special()) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (r); + if (tr) { + tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr (tr))); + track_playlist_changed (boost::weak_ptr (tr)); + tr->rec_enable_control()->Changed.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this)); + + boost::shared_ptr mt = boost::dynamic_pointer_cast (tr); + if (mt) { + mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1)); + 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))); + } + } - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name())); + if (!r->presentation_info().special (false)) { - /* presentation info order may already have been set from XML */ + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name())); - if (!r->presentation_info().order_set()) { + /* presentation info order may already have been set from XML */ - if (order == PresentationInfo::max_order) { - /* just add to the end */ - 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)); + if (!r->presentation_info().order_set()) { + if (order == PresentationInfo::max_order) { + /* just add to the end */ + r->set_presentation_order (n_routes + added); + 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); + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to %1 + %2 = %3\n", order, added, order + added)); + } } else { - r->set_presentation_order (order + added); - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to %1 + %2 = %3\n", order, added, order + added)); + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order already set to %1\n", r->presentation_info().order())); } - } else { - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order already set to %1\n", r->presentation_info().order())); } - } #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)" - */ - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("added route %1, group order %2 type %3 (summary: %4)\n", - r->name(), - r->presentation_info().order(), - enum_2_string (r->presentation_info().flags()), - r->presentation_info())); + /* 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)" + */ + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("added route %1, group order %2 type %3 (summary: %4)\n", + r->name(), + r->presentation_info().order(), + enum_2_string (r->presentation_info().flags()), + r->presentation_info())); #endif - if (input_auto_connect || output_auto_connect) { - auto_connect_route (r, input_auto_connect, ChanCount (), ChanCount (), existing_inputs, existing_outputs); - existing_inputs += r->n_inputs(); - existing_outputs += r->n_outputs(); - } + if (input_auto_connect || output_auto_connect) { + auto_connect_route (r, input_auto_connect, ChanCount (), ChanCount (), existing_inputs, existing_outputs); + existing_inputs += r->n_inputs(); + existing_outputs += r->n_outputs(); + } - ARDOUR::GUIIdle (); + ARDOUR::GUIIdle (); + } + ensure_stripable_sort_order (); } if (_monitor_out && IO::connecting_legal) { @@ -3655,14 +3791,20 @@ Session::add_internal_send (boost::shared_ptr dest, boost::shared_ptr routes_to_remove) { + bool mute_changed = false; + bool send_selected = false; + { // RCU Writer scope PBD::Unwinder uw_flag (_route_deletion_in_progress, true); RCUWriter writer (routes); boost::shared_ptr rs = writer.get_copy (); - for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) { + if (_selection->selected (*iter)) { + send_selected = true; + } + if (*iter == _master_out) { continue; } @@ -3672,6 +3814,10 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup); } + if ((*iter)->mute_control()->muted ()) { + mute_changed = true; + } + rs->remove (*iter); /* deleting the master out seems like a dumb @@ -3723,6 +3869,10 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) } // end of RCU Writer scope + if (mute_changed) { + MuteChanged (); /* EMIT SIGNAL */ + } + update_route_solo_state (); update_latency_compensation (); set_dirty(); @@ -3748,20 +3898,34 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) routes.flush (); + /* remove these routes from the selection if appropriate, and signal + * the change *before* we call DropReferences for them. + */ + + if (send_selected && !deletion_in_progress()) { + for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) { + _selection->remove_stripable_by_id ((*iter)->id()); + } + PropertyChange pc; + pc.add (Properties::selected); + PresentationInfo::Change (pc); + } + /* try to cause everyone to drop their references * and unregister ports from the backend */ for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) { - cerr << "Drop references to " << (*iter)->name() << endl; (*iter)->drop_references (); } - if (_state_of_the_state & Deletion) { + if (deletion_in_progress()) { return; } - PresentationInfo::Change(); /* EMIT SIGNAL */ + PropertyChange pc; + pc.add (Properties::order); + PresentationInfo::Change (pc); /* save the new state of the world */ @@ -3783,6 +3947,7 @@ Session::remove_route (boost::shared_ptr route) void Session::route_mute_changed () { + MuteChanged (); /* EMIT SIGNAL */ set_dirty (); } @@ -4100,6 +4265,61 @@ Session::update_route_solo_state (boost::shared_ptr r) set_dirty(); } +bool +Session::muted () const +{ + // TODO consider caching the value on every MuteChanged signal, + // Note that API users may also subscribe to MuteChanged and hence + // this method needs to be called first. + bool muted = false; + StripableList all; + get_stripables (all); + for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) { + assert (!(*i)->is_auditioner()); // XXX remove me + if ((*i)->is_monitor()) { + continue; + } + boost::shared_ptr r = boost::dynamic_pointer_cast(*i); + if (r && !r->active()) { + continue; + } + boost::shared_ptr mc = (*i)->mute_control(); + if (mc && mc->muted ()) { + muted = true; + break; + } + } + return muted; +} + +std::vector > +Session::cancel_all_mute () +{ + StripableList all; + get_stripables (all); + std::vector > muted; + boost::shared_ptr cl (new ControlList); + for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) { + assert (!(*i)->is_auditioner()); + if ((*i)->is_monitor()) { + continue; + } + boost::shared_ptr r = boost::dynamic_pointer_cast (*i); + if (r && !r->active()) { + continue; + } + boost::shared_ptr ac = (*i)->mute_control(); + if (ac && ac->get_value () > 0) { + cl->push_back (ac); + muted.push_back (boost::weak_ptr(ac)); + } + } + if (!cl->empty ()) { + set_controls (cl, 0.0, PBD::Controllable::UseGroup); + } + return muted; +} + void Session::get_stripables (StripableList& sl) const { @@ -4110,6 +4330,15 @@ Session::get_stripables (StripableList& sl) const sl.insert (sl.end(), v.begin(), v.end()); } +StripableList +Session::get_stripables () const +{ + StripableList rv; + Session::get_stripables (rv); + rv.sort (Stripable::Sorter ()); + return rv; +} + boost::shared_ptr Session::get_routes_with_internal_returns() const { @@ -4132,8 +4361,8 @@ Session::io_name_is_legal (const std::string& name) const 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; + /* first instance of a reserved name is allowed for some */ + return reserved->second; } /* all other instances of a reserved name are not allowed */ return false; @@ -4261,34 +4490,35 @@ Session::route_by_id (PBD::ID id) const return boost::shared_ptr ((Route*) 0); } -boost::shared_ptr -Session::processor_by_id (PBD::ID id) const + +boost::shared_ptr +Session::stripable_by_id (PBD::ID id) const { - boost::shared_ptr r = routes.reader (); + StripableList sl; + get_stripables (sl); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr p = (*i)->Route::processor_by_id (id); - if (p) { - return p; + for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) { + if ((*s)->id() == id) { + return *s; } } - return boost::shared_ptr (); + return boost::shared_ptr(); } -boost::shared_ptr -Session::track_by_diskstream_id (PBD::ID id) const +boost::shared_ptr +Session::processor_by_id (PBD::ID id) const { boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr t = boost::dynamic_pointer_cast (*i); - if (t && t->using_diskstream_id (id)) { - return t; + boost::shared_ptr p = (*i)->Route::processor_by_id (id); + if (p) { + return p; } } - return boost::shared_ptr (); + return boost::shared_ptr (); } boost::shared_ptr @@ -4304,7 +4534,7 @@ Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo PresentationInfo::order_t match_cnt = 0; get_stripables (sl); - sl.sort (Stripable::PresentationOrderSorter()); + sl.sort (Stripable::Sorter()); for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) { @@ -4336,31 +4566,16 @@ Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo return boost::shared_ptr(); } -struct PresentationOrderSorter { - bool operator() (boost::shared_ptr a, boost::shared_ptr b) { - if (a->presentation_info().special() && !b->presentation_info().special()) { - /* a is not ordered, b is; b comes before a */ - return false; - } else if (!b->presentation_info().order_set() && a->presentation_info().order_set()) { - /* b is not ordered, a is; a comes before b */ - return true; - } else { - return a->presentation_info().order() < b->presentation_info().order(); - } - } -}; - boost::shared_ptr Session::route_by_selected_count (uint32_t id) const { RouteList r (*(routes.reader ())); - PresentationOrderSorter sorter; - r.sort (sorter); + r.sort (Stripable::Sorter()); RouteList::iterator i; for (i = r.begin(); i != r.end(); ++i) { - if ((*i)->presentation_info().selected()) { + if ((*i)->is_selected()) { if (id == 0) { return *i; } @@ -4377,16 +4592,16 @@ Session::reassign_track_numbers () int64_t tn = 0; int64_t bn = 0; RouteList r (*(routes.reader ())); - PresentationOrderSorter sorter; - r.sort (sorter); + r.sort (Stripable::Sorter()); StateProtector sp (this); for (RouteList::iterator i = r.begin(); i != r.end(); ++i) { + assert (!(*i)->is_auditioner()); if (boost::dynamic_pointer_cast (*i)) { (*i)->set_track_number(++tn); } - else if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) { + else if (!(*i)->is_master() && !(*i)->is_monitor()) { (*i)->set_track_number(--bn); } } @@ -4453,7 +4668,7 @@ Session::playlist_region_added (boost::weak_ptr w) /* If so, update the session range markers */ if (!in.empty ()) { - maybe_update_session_range (r->position (), r->last_frame ()); + maybe_update_session_range (r->position (), r->last_sample ()); } } @@ -4461,13 +4676,13 @@ Session::playlist_region_added (boost::weak_ptr w) * b is after the current end. */ void -Session::maybe_update_session_range (framepos_t a, framepos_t b) +Session::maybe_update_session_range (samplepos_t a, samplepos_t b) { if (_state_of_the_state & Loading) { return; } - framepos_t session_end_marker_shift_samples = session_end_shift * _nominal_frame_rate; + samplepos_t session_end_marker_shift_samples = session_end_shift * _nominal_sample_rate; if (_session_range_location == 0) { @@ -4492,17 +4707,17 @@ Session::set_end_is_free (bool yn) } void -Session::playlist_ranges_moved (list > const & ranges) +Session::playlist_ranges_moved (list > const & ranges) { - for (list >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) { + for (list >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) { maybe_update_session_range (i->to, i->to + i->length); } } void -Session::playlist_regions_extended (list > const & ranges) +Session::playlist_regions_extended (list > const & ranges) { - for (list >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) { + for (list >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) { maybe_update_session_range (i->from, i->to); } } @@ -4713,13 +4928,16 @@ Session::audio_source_by_path_and_channel (const string& path, uint16_t chn) con } boost::shared_ptr -Session::midi_source_by_path (const std::string& path) const +Session::midi_source_by_path (const std::string& path, bool need_source_lock) const { /* Restricted to MIDI files because audio sources require a channel for unique identification, in addition to a path. */ - Glib::Threads::Mutex::Lock lm (source_lock); + Glib::Threads::Mutex::Lock lm (source_lock, Glib::Threads::NOT_LOCK); + if (need_source_lock) { + lm.acquire (); + } for (SourceMap::const_iterator s = sources.begin(); s != sources.end(); ++s) { boost::shared_ptr ms @@ -5020,17 +5238,12 @@ Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t cha /** Return a unique name based on `base` for a new internal MIDI source */ string -Session::new_midi_source_path (const string& base) +Session::new_midi_source_path (const string& base, bool need_lock) { - uint32_t cnt; - char buf[PATH_MAX+1]; - const uint32_t limit = 10000; - string legalized; string possible_path; string possible_name; - buf[0] = '\0'; - legalized = legalize_for_path (base); + possible_name = legalize_for_path (base); // Find a "version" of the file name that doesn't exist in any of the possible directories. std::vector sdirs = source_search_path(DataType::MIDI); @@ -5045,38 +5258,36 @@ Session::new_midi_source_path (const string& base) */ std::reverse(sdirs.begin(), sdirs.end()); - for (cnt = 1; cnt <= limit; ++cnt) { + while (true) { + possible_name = bump_name_once (possible_name, '-'); vector::iterator i; uint32_t existing = 0; for (vector::const_iterator i = sdirs.begin(); i != sdirs.end(); ++i) { - snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt); - possible_name = buf; - - possible_path = Glib::build_filename (*i, possible_name); + possible_path = Glib::build_filename (*i, possible_name + ".mid"); if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) { existing++; } - if (midi_source_by_path (possible_path)) { + if (midi_source_by_path (possible_path, need_lock)) { existing++; } } - if (existing == 0) { - break; - } - - if (cnt > limit) { + if (possible_path.size () >= PATH_MAX) { error << string_compose( - _("There are already %1 recordings for %2, which I consider too many."), - limit, base) << endmsg; + _("There are already many recordings for %1, resulting in a too long file-path %2."), + base, possible_path) << endmsg; destroy (); return 0; } + + if (existing == 0) { + break; + } } /* No need to "find best location" for software/app-based RAID, because @@ -5095,7 +5306,7 @@ Session::create_audio_source_for_session (size_t n_chans, string const & base, u if (!path.empty()) { return boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate(), true, true)); + SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, sample_rate(), true, true)); } else { throw failed_constructor (); } @@ -5110,7 +5321,7 @@ Session::create_midi_source_for_session (string const & basic_name) if (!path.empty()) { return boost::dynamic_pointer_cast ( SourceFactory::createWritable ( - DataType::MIDI, *this, path, false, frame_rate())); + DataType::MIDI, *this, path, false, sample_rate())); } else { throw failed_constructor (); } @@ -5155,7 +5366,7 @@ Session::create_midi_source_by_stealing_name (boost::shared_ptr track) return boost::dynamic_pointer_cast ( SourceFactory::createWritable ( - DataType::MIDI, *this, path, false, frame_rate())); + DataType::MIDI, *this, path, false, sample_rate())); } @@ -5228,6 +5439,9 @@ Session::register_lua_function ( tbl_arg[(*i)->name] = (*i)->value; } (*_lua_add)(name, bytecode, tbl_arg); // throws luabridge::LuaException + lm.release(); + + LuaScriptsChanged (); /* EMIT SIGNAL */ set_dirty(); } @@ -5237,6 +5451,9 @@ Session::unregister_lua_function (const std::string& name) Glib::Threads::Mutex::Lock lm (lua_lock); (*_lua_del)(name); // throws luabridge::LuaException lua.collect_garbage (); + lm.release(); + + LuaScriptsChanged (); /* EMIT SIGNAL */ set_dirty(); } @@ -5252,7 +5469,7 @@ Session::registered_lua_functions () if (!i.key ().isString ()) { assert(0); continue; } rv.push_back (i.key ().cast ()); } - } catch (luabridge::LuaException const& e) { } + } catch (...) { } return rv; } @@ -5268,7 +5485,7 @@ Session::try_run_lua (pframes_t nframes) if (_n_lua_scripts == 0) return; Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK); if (tm.locked ()) { - try { (*_lua_run)(nframes); } catch (luabridge::LuaException const& e) { } + try { (*_lua_run)(nframes); } catch (...) { } lua.collect_garbage_step (); } } @@ -5279,7 +5496,7 @@ Session::setup_lua () #ifndef NDEBUG lua.Print.connect (&_lua_print); #endif - lua.tweak_rt_gc (); + lua.sandbox (true); lua.do_command ( "function ArdourSession ()" " local self = { scripts = {}, instances = {} }" @@ -5296,7 +5513,6 @@ Session::setup_lua () " assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')" " 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, 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 @@ -5400,14 +5616,21 @@ Session::setup_lua () _lua_cleanup = new luabridge::LuaRef(lua_sess["cleanup"]); } catch (luabridge::LuaException const& e) { fatal << string_compose (_("programming error: %1"), - X_("Failed to setup Lua interpreter")) + std::string ("Failed to setup session Lua interpreter") + e.what ()) + << endmsg; + abort(); /*NOTREACHED*/ + } catch (...) { + fatal << string_compose (_("programming error: %1"), + X_("Failed to setup session Lua interpreter")) << endmsg; abort(); /*NOTREACHED*/ } + lua_mlock (L, 1); LuaBindings::stddef (L); LuaBindings::common (L); LuaBindings::dsp (L); + lua_mlock (L, 0); luabridge::push (L, this); lua_setglobal (L, "Session"); } @@ -5426,6 +5649,11 @@ Session::scripts_changed () } _n_lua_scripts = cnt; } catch (luabridge::LuaException const& e) { + fatal << string_compose (_("programming error: %1"), + std::string ("Indexing Lua Session Scripts failed.") + e.what ()) + << endmsg; + abort(); /*NOTREACHED*/ + } catch (...) { fatal << string_compose (_("programming error: %1"), X_("Indexing Lua Session Scripts failed.")) << endmsg; @@ -5462,18 +5690,6 @@ Session::cancel_audition () } } -bool -Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost::shared_ptr b) -{ - if (a->is_monitor()) { - return true; - } - if (b->is_monitor()) { - return false; - } - return a->presentation_info().order() < b->presentation_info().order(); -} - bool Session::is_auditioning () const { @@ -5496,37 +5712,24 @@ Session::graph_reordered () return; } - /* every track/bus asked for this to be handled but it was deferred because - we were connecting. do it now. - */ - - request_input_change_handling (); - resort_routes (); /* force all diskstreams to update their capture offset values to - reflect any changes in latencies within the graph. - */ - - boost::shared_ptr rl = routes.reader (); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr) { - tr->set_capture_offset (); - } - } + * reflect any changes in latencies within the graph. + */ + update_route_latency (false, true); } -/** @return Number of frames that there is disk space available to write, +/** @return Number of samples that there is disk space available to write, * if known. */ -boost::optional +boost::optional Session::available_capture_duration () { Glib::Threads::Mutex::Lock lm (space_lock); if (_total_free_4k_blocks_uncertain) { - return boost::optional (); + return boost::optional (); } float sample_bytes_on_disk = 4.0; // keep gcc happy @@ -5554,11 +5757,11 @@ Session::available_capture_duration () double scale = 4096.0 / sample_bytes_on_disk; - if (_total_free_4k_blocks * scale > (double) max_framecnt) { - return max_framecnt; + if (_total_free_4k_blocks * scale > (double) max_samplecnt) { + return max_samplecnt; } - return (framecnt_t) floor (_total_free_4k_blocks * scale); + return (samplecnt_t) floor (_total_free_4k_blocks * scale); } void @@ -5630,7 +5833,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_beat (); + (*i)->recompute_samples_from_beat (); } } @@ -5878,7 +6081,7 @@ Session::freeze_all (InterThreadInfo& itt) } boost::shared_ptr -Session::write_one_track (Track& track, framepos_t start, framepos_t end, +Session::write_one_track (Track& track, samplepos_t start, samplepos_t end, bool /*overwrite*/, vector >& srcs, InterThreadInfo& itt, boost::shared_ptr endpoint, bool include_endpoint, @@ -5888,12 +6091,12 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end, boost::shared_ptr playlist; boost::shared_ptr source; ChanCount diskstream_channels (track.n_channels()); - framepos_t position; - framecnt_t this_chunk; - framepos_t to_do; - framepos_t latency_skip; + samplepos_t position; + samplecnt_t this_chunk; + samplepos_t to_do; + samplepos_t latency_skip; BufferSet buffers; - framepos_t len = end - start; + samplepos_t len = end - start; bool need_block_size_reset = false; ChanCount const max_proc = track.max_processor_streams (); string legal_playlist_name; @@ -5947,7 +6150,7 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end, } try { - source = SourceFactory::createWritable (track.data_type(), *this, path, false, frame_rate()); + source = SourceFactory::createWritable (track.data_type(), *this, path, false, sample_rate()); } catch (failed_constructor& err) { @@ -6005,7 +6208,7 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end, continue; } - const framecnt_t current_chunk = this_chunk - latency_skip; + const samplecnt_t current_chunk = this_chunk - latency_skip; uint32_t n = 0; for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { @@ -6021,9 +6224,9 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end, const MidiBuffer& buf = buffers.get_midi(0); for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) { - Evoral::Event ev = *i; + Evoral::Event ev = *i; ev.set_time(ev.time() - position); - ms->append_event_frames(lock, ev, ms->timeline_position()); + ms->append_event_samples(lock, ev, ms->timeline_position()); } } } @@ -6130,6 +6333,12 @@ Session::send_gain_automation_buffer() const return ProcessThread::send_gain_automation_buffer (); } +gain_t* +Session::scratch_automation_buffer() const +{ + return ProcessThread::scratch_automation_buffer (); +} + pan_t** Session::pan_automation_buffer() const { @@ -6197,6 +6406,23 @@ Session::nbusses () const return n; } +uint32_t +Session::nstripables (bool with_monitor) const +{ + uint32_t rv = routes.reader()->size (); + rv += _vca_manager->vcas ().size (); + + if (with_monitor) { + return rv; + } + + if (_monitor_out) { + assert (rv > 0); + --rv; + } + return rv; +} + void Session::add_automation_list(AutomationList *al) { @@ -6320,16 +6546,15 @@ Session::get_tracks () const for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) { if (boost::dynamic_pointer_cast (*r)) { - if (!(*r)->is_auditioner()) { - tl->push_back (*r); - } + assert (!(*r)->is_auditioner()); // XXX remove me + tl->push_back (*r); } } return tl; } boost::shared_ptr -Session::get_routes_with_regions_at (framepos_t const p) const +Session::get_routes_with_regions_at (samplepos_t const p) const { boost::shared_ptr r = routes.reader (); boost::shared_ptr rl (new RouteList); @@ -6373,20 +6598,20 @@ Session::goto_start (bool and_roll) } } -framepos_t -Session::current_start_frame () const +samplepos_t +Session::current_start_sample () const { return _session_range_location ? _session_range_location->start() : 0; } -framepos_t -Session::current_end_frame () const +samplepos_t +Session::current_end_sample () const { return _session_range_location ? _session_range_location->end() : 0; } void -Session::set_session_range_location (framepos_t start, framepos_t end) +Session::set_session_range_location (samplepos_t start, samplepos_t end) { _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange, 0); _locations->add (_session_range_location); @@ -6419,7 +6644,7 @@ Session::step_edit_status_change (bool yn) void -Session::start_time_changed (framepos_t old) +Session::start_time_changed (samplepos_t old) { /* Update the auto loop range to match the session range (unless the auto loop range has been changed by the user) @@ -6439,7 +6664,7 @@ Session::start_time_changed (framepos_t old) } void -Session::end_time_changed (framepos_t old) +Session::end_time_changed (samplepos_t old) { /* Update the auto loop range to match the session range (unless the auto loop range has been changed by the user) @@ -6602,115 +6827,132 @@ Session::unknown_processors () const } void -Session::update_latency (bool playback) +Session::set_worst_io_latencies_x (IOChange, void *) { + set_worst_io_latencies (); +} - DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE"))); - - if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) { - return; - } +void +Session::send_latency_compensation_change () +{ + /* As a result of Send::set_output_latency() + * or InternalReturn::set_playback_offset () + * the send's own latency can change (source track + * is aligned with target bus). + * + * This can only happen be triggered by + * Route::update_signal_latency () + * when updating the processor latency. + * + * We need to walk the graph again to take those changes into account + * (we should probably recurse or process the graph in a 2 step process). + */ + ++_send_latency_changes; +} +bool +Session::update_route_latency (bool playback, bool apply_to_delayline) +{ + /* Note: RouteList is process-graph sorted */ boost::shared_ptr r = routes.reader (); - framecnt_t max_latency = 0; if (playback) { - /* reverse the list so that we work backwards from the last route to run to the first */ - RouteList* rl = routes.reader().get(); - r.reset (new RouteList (*rl)); + /* reverse the list so that we work backwards from the last route to run to the first, + * this is not needed, but can help to reduce the iterations for aux-sends. + */ + RouteList* rl = routes.reader().get(); + r.reset (new RouteList (*rl)); reverse (r->begin(), r->end()); } - /* compute actual latency values for the given direction and store them all in per-port - structures. this will also publish the same values (to JACK) so that computation of latency - for routes can consistently use public latency values. - */ + bool changed = false; + int bailout = 0; +restart: + _send_latency_changes = 0; + _worst_route_latency = 0; for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - max_latency = max (max_latency, (*i)->set_private_port_latencies (playback)); + // if (!(*i)->active()) { continue ; } // TODO + samplecnt_t l; + if ((*i)->signal_latency () != (l = (*i)->update_signal_latency (apply_to_delayline))) { + changed = true; + } + _worst_route_latency = std::max (l, _worst_route_latency); } - /* because we latency compensate playback, our published playback latencies should - be the same for all output ports - all material played back by ardour has - the same latency, whether its caused by plugins or by latency compensation. since - these may differ from the values computed above, reset all playback port latencies - to the same value. - */ - - DEBUG_TRACE (DEBUG::Latency, string_compose ("Set public port latencies to %1\n", max_latency)); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->set_public_port_latencies (max_latency, playback); - } - - if (playback) { - - post_playback_latency (); - - } else { - - post_capture_latency (); + if (_send_latency_changes > 0) { + // only 1 extra iteration is needed (we allow only 1 level of aux-sends) + // BUT.. jack'n'sends'n'bugs + if (++bailout < 5) { + cerr << "restarting Session::update_latency. # of send changes: " << _send_latency_changes << " iteration: " << bailout << endl; + goto restart; + } } - DEBUG_TRACE (DEBUG::Latency, "JACK latency callback: DONE\n"); + DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_route_latency, (changed ? "yes" : "no"))); + + return changed; } void -Session::post_playback_latency () +Session::update_latency (bool playback) { - set_worst_playback_latency (); + DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE"))); + if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) { + return; + } + if (!_engine.running()) { + return; + } + + /* Note; RouteList is sorted as process-graph */ boost::shared_ptr r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_auditioner() && ((*i)->active())) { - _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ()); - } + if (playback) { + /* reverse the list so that we work backwards from the last route to run to the first */ + RouteList* rl = routes.reader().get(); + r.reset (new RouteList (*rl)); + reverse (r->begin(), r->end()); } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->set_latency_compensation (_worst_track_latency); + samplecnt_t latency = (*i)->set_private_port_latencies (playback); + (*i)->set_public_port_latencies (latency, playback); } -} - -void -Session::post_capture_latency () -{ - set_worst_capture_latency (); - /* reflect any changes in capture latencies into capture offsets - */ - - boost::shared_ptr rl = routes.reader(); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr) { - tr->set_capture_offset (); - } + if (playback) { + set_worst_output_latency (); + update_route_latency (true, true); + } else { + set_worst_input_latency (); + update_route_latency (false, false); } + + DEBUG_TRACE (DEBUG::Latency, "JACK latency callback: DONE\n"); } void Session::initialize_latencies () { - { - Glib::Threads::Mutex::Lock lm (_engine.process_lock()); - update_latency (false); - update_latency (true); - } + { + Glib::Threads::Mutex::Lock lm (_engine.process_lock()); + update_latency (false); + update_latency (true); + } - set_worst_io_latencies (); + set_worst_io_latencies (); } void Session::set_worst_io_latencies () { - set_worst_playback_latency (); - set_worst_capture_latency (); + set_worst_output_latency (); + set_worst_input_latency (); } void -Session::set_worst_playback_latency () +Session::set_worst_output_latency () { if (_state_of_the_state & (InitialConnecting|Deletion)) { return; @@ -6728,11 +6970,13 @@ Session::set_worst_playback_latency () _worst_output_latency = max (_worst_output_latency, (*i)->output()->latency()); } + _worst_output_latency = max (_worst_output_latency, _click_io->latency()); + DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst output latency: %1\n", _worst_output_latency)); } void -Session::set_worst_capture_latency () +Session::set_worst_input_latency () { if (_state_of_the_state & (InitialConnecting|Deletion)) { return; @@ -6750,50 +6994,29 @@ Session::set_worst_capture_latency () _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency()); } - DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency)); + DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency)); } void Session::update_latency_compensation (bool force_whole_graph) { - bool some_track_latency_changed = false; - if (_state_of_the_state & (InitialConnecting|Deletion)) { return; } - DEBUG_TRACE(DEBUG::Latency, "---------------------------- update latency compensation\n\n"); - - _worst_track_latency = 0; - - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_auditioner() && ((*i)->active())) { - framecnt_t tl; - if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency ())) { - some_track_latency_changed = true; - } - _worst_track_latency = max (tl, _worst_track_latency); - } - } - - DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_track_latency, - (some_track_latency_changed ? "yes" : "no"))); - - DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n"); + bool some_track_latency_changed = update_route_latency (false, false); if (some_track_latency_changed || force_whole_graph) { _engine.update_latencies (); - } - - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (!tr) { - continue; + /* above call will ask the backend up update its latencies, which + * eventually will trigger AudioEngine::latency_callback () and + * call Session::update_latency () + */ + } else { + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->apply_latency_compensation (); } - tr->set_capture_offset (); } } @@ -6818,7 +7041,6 @@ Session::notify_presentation_info_change () return; } - PresentationInfo::Change (); /* EMIT SIGNAL */ reassign_track_numbers(); #ifdef USE_TRACKS_CODE_FEATURES @@ -6839,13 +7061,14 @@ Session::operation_in_progress (GQuark op) const boost::shared_ptr Session::ltc_input_port () const { + assert (_ltc_input); return _ltc_input->nth (0); } boost::shared_ptr Session::ltc_output_port () const { - return _ltc_output->nth (0); + return _ltc_output ? _ltc_output->nth (0) : boost::shared_ptr (); } void @@ -6884,18 +7107,18 @@ Session::reconnect_ltc_output () } void -Session::set_range_selection (framepos_t start, framepos_t end) +Session::set_range_selection (samplepos_t start, samplepos_t end) { - _range_selection = Evoral::Range (start, end); + _range_selection = Evoral::Range (start, end); #ifdef USE_TRACKS_CODE_FEATURES follow_playhead_priority (); #endif } void -Session::set_object_selection (framepos_t start, framepos_t end) +Session::set_object_selection (samplepos_t start, samplepos_t end) { - _object_selection = Evoral::Range (start, end); + _object_selection = Evoral::Range (start, end); #ifdef USE_TRACKS_CODE_FEATURES follow_playhead_priority (); #endif @@ -6904,7 +7127,7 @@ Session::set_object_selection (framepos_t start, framepos_t end) void Session::clear_range_selection () { - _range_selection = Evoral::Range (-1,-1); + _range_selection = Evoral::Range (-1,-1); #ifdef USE_TRACKS_CODE_FEATURES follow_playhead_priority (); #endif @@ -6913,7 +7136,7 @@ Session::clear_range_selection () void Session::clear_object_selection () { - _object_selection = Evoral::Range (-1,-1); + _object_selection = Evoral::Range (-1,-1); #ifdef USE_TRACKS_CODE_FEATURES follow_playhead_priority (); #endif @@ -7126,12 +7349,12 @@ Session::auto_connect_thread_run () /* 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" + * update_latency_compensation () + * calls DiskWriter::set_capture_offset () which + * modifies the capture-offset, which can be a problem. */ while (g_atomic_int_and (&_latency_recompute_pending, 0)) { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); update_latency_compensation (); } }