X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=654234c51566d7a58618b48701ad3baa9db7cae9;hb=32b73439275dbe2cccaa2a71026a951ea46d24b9;hp=1c72b8042ad07b2778fe1a2e9ddb692aaae6290e;hpb=7b2063d62d5bbcace893d9b53586afe57f21f7fb;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 1c72b8042a..654234c515 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,6 +93,7 @@ #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" @@ -104,6 +105,7 @@ #include "ardour/speakers.h" #include "ardour/tempo.h" #include "ardour/ticker.h" +#include "ardour/transport_master.h" #include "ardour/track.h" #include "ardour/types_convert.h" #include "ardour/user_bundle.h" @@ -137,13 +139,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; @@ -151,7 +153,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); @@ -173,28 +175,30 @@ 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) + , _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) @@ -204,13 +208,8 @@ Session::Session (AudioEngine &eng, , _was_seamless (Config->get_seamless_loop ()) , _under_nsm_control (false) , _xrun_count (0) - , delta_accumulator_cnt (0) - , average_slave_delta (1800) // !!! why 1800 ??? - , average_dir (0) - , have_first_delta_accumulator (false) - , _slave_state (Stopped) - , _mtc_active (false) - , _ltc_active (false) + , transport_master_tracking_state (Stopped) + , master_wait_end (0) , post_export_sync (false) , post_export_position (0) , _exporting (false) @@ -218,7 +217,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) @@ -237,10 +235,6 @@ Session::Session (AudioEngine &eng, , _last_roll_location (0) , _last_roll_or_reversal_location (0) , _last_record_location (0) - , pending_locate_roll (false) - , pending_locate_frame (0) - , pending_locate_flush (false) - , pending_abort (false) , pending_auto_loop (false) , _mempool ("Session", 3145728) , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool)) @@ -307,7 +301,6 @@ 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) @@ -326,6 +319,7 @@ Session::Session (AudioEngine &eng, , _mmc (0) , _vca_manager (new VCAManager (*this)) , _selection (new CoreSelection (*this)) + , _global_locate_pending (false) { uint32_t sr = 0; @@ -359,7 +353,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 (); @@ -378,8 +372,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); } @@ -412,9 +410,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); @@ -435,6 +451,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 (); @@ -490,8 +508,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")); } @@ -563,6 +589,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. @@ -579,11 +607,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")); @@ -618,14 +645,23 @@ Session::destroy () _state_of_the_state = StateOfTheState (CannotSave|Deletion); + { + Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + ltc_tx_cleanup(); + } + /* 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_output.reset (); ControlProtocolManager::instance().drop_protocols (); @@ -646,8 +682,6 @@ Session::destroy () Port::PortDrop (); /* EMIT SIGNAL */ - ltc_tx_cleanup(); - /* clear history so that no references to objects are held any more */ _history.clear (); @@ -657,17 +691,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; @@ -694,7 +731,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 @@ -706,7 +749,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 (); @@ -786,11 +829,9 @@ Session::destroy () bool del = true; switch (ev->type) { case SessionEvent::AutoLoop: - case SessionEvent::AutoLoopDeclick: case SessionEvent::Skip: case SessionEvent::PunchIn: case SessionEvent::PunchOut: - case SessionEvent::RecordStart: case SessionEvent::StopOnce: case SessionEvent::RangeStop: case SessionEvent::RangeLocate: @@ -819,6 +860,10 @@ Session::destroy () 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 (); @@ -829,21 +874,8 @@ Session::setup_ltc () { XMLNode* child = 0; - _ltc_input.reset (new IO (*this, X_("LTC In"), IO::Input)); _ltc_output.reset (new IO (*this, X_("LTC Out"), IO::Output)); - if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC In"))) != 0) { - _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version); - } else { - { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this); - // 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 (); - } - } - if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC Out"))) != 0) { _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version); } else { @@ -859,7 +891,6 @@ Session::setup_ltc () * IO style of NAME/TYPE-{in,out}N */ - _ltc_input->nth (0)->set_name (X_("LTC-in")); _ltc_output->nth (0)->set_name (X_("LTC-out")); } @@ -1451,6 +1482,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 () { @@ -1471,7 +1529,6 @@ Session::hookup_io () if (a->init()) { throw failed_constructor (); } - a->use_new_diskstream (); auditioner = a; } @@ -1566,7 +1623,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); } @@ -1575,27 +1632,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; @@ -1605,38 +1657,21 @@ 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) { + bool rolling = transport_rolling (); - replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl); + if (rolling && play_loop) { - // if (_transport_frame > 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); - request_locate (location->start(), true); } - else if (Config->get_seamless_loop() && !loop_changing) { - - // schedule a locate-roll to refill the diskstreams at the - // previous loop end - loop_changing = true; - - if (location->end() > last_loopend) { - clear_events (SessionEvent::LocateRoll); - SessionEvent *ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, last_loopend, last_loopend, 0, true); - queue_event (ev); - } - - } } else { - clear_events (SessionEvent::AutoLoopDeclick); clear_events (SessionEvent::AutoLoop); } @@ -1644,15 +1679,14 @@ 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 (!rolling && select_playhead_priority_target (pos)) { if (pos == location->start()) { request_locate (pos); } } - last_loopend = location->end(); set_dirty (); } @@ -1665,7 +1699,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); } @@ -1695,7 +1729,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) { @@ -1722,10 +1756,9 @@ 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); } @@ -1756,7 +1789,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); } } @@ -1920,24 +1953,24 @@ Session::location_added (Location *location) void Session::location_removed (Location *location) { - if (location->is_auto_loop()) { - set_auto_loop_location (0); - set_track_loop (false); - } + if (location->is_auto_loop()) { + set_auto_loop_location (0); + set_track_loop (false); + } - if (location->is_auto_punch()) { - set_auto_punch_location (0); - } + if (location->is_auto_punch()) { + set_auto_punch_location (0); + } - if (location->is_session_range()) { - /* this is never supposed to happen */ - error << _("programming error: session range removed!") << endl; - } + if (location->is_session_range()) { + /* this is never supposed to happen */ + error << _("programming error: session range removed!") << endl; + } - if (location->is_skip()) { + if (location->is_skip()) { - update_skips (location, false); - } + update_skips (location, false); + } set_dirty (); } @@ -1984,7 +2017,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()) { @@ -2029,7 +2062,6 @@ Session::disable_record (bool rt_context, bool force) if (!rt_context) { remove_pending_capture_state (); } - unset_preroll_record_punch (); } } @@ -2067,7 +2099,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 { @@ -2078,107 +2110,85 @@ Session::maybe_enable_record (bool rt_context) set_dirty(); } -framepos_t -Session::audible_frame (bool* latent_locate) const +samplepos_t +Session::audible_sample (bool* latent_locate) const { - framepos_t ret; - - frameoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start (); - offset *= transport_speed (); if (latent_locate) { *latent_locate = false; } + 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) { - if (latent_locate) { - *latent_locate = true; - } - return _last_roll_or_reversal_location; - } - } else { - /* 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(); - frameoffset_t lo = location->start() - ret; - if (lo > 0) { - ret = location->end () - lo; - if (latent_locate) { - *latent_locate = true; - } +#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(); @@ -2482,11 +2492,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(); } } @@ -2548,8 +2560,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); { @@ -2565,14 +2575,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); } @@ -2966,40 +2972,6 @@ Session::reconnect_midi_scene_ports(bool inputs) } } -void -Session::reconnect_mtc_ports () -{ - boost::shared_ptr mtc_in_ptr = _midi_ports->mtc_input_port(); - - if (!mtc_in_ptr) { - return; - } - - mtc_in_ptr->disconnect_all (); - - std::vector midi_port_states; - EngineStateController::instance()->get_physical_midi_input_states (midi_port_states); - - std::vector::iterator state_iter = midi_port_states.begin(); - - for (; state_iter != midi_port_states.end(); ++state_iter) { - if (state_iter->available && state_iter->mtc_in) { - mtc_in_ptr->connect (state_iter->name); - } - } - - if (!_midi_ports->mtc_input_port ()->connected () && - config.get_external_sync () && - (Config->get_sync_source () == MTC) ) { - config.set_external_sync (false); - } - - if ( ARDOUR::Profile->get_trx () ) { - // Tracks need this signal to update timecode_source_dropdown - MtcOrLtcInputPortChanged (); //emit signal - } -} - void Session::reconnect_mmc_ports(bool inputs) { @@ -3037,9 +3009,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; @@ -3054,7 +3053,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; } @@ -3115,8 +3118,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r } } - track->use_new_diskstream(); - BOOST_MARK_TRACK (track); { @@ -3143,10 +3144,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); } @@ -3326,7 +3323,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) { @@ -3394,13 +3391,15 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i (*i)->remove_property (X_("bitslot")); } else if (type && type->value() == X_("port")) { - // PortInsert::set_state() handles the bitslot - (*i)->remove_property (X_("bitslot")); - (*i)->set_property ("ignore-name", "1"); + IOProcessor::prepare_for_reset (**i, name); } } } + /* 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) { @@ -3466,8 +3465,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(); @@ -3493,8 +3492,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 @@ -3507,21 +3506,15 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool } } - /* auditioner and monitor routes are not part of the order */ - if (auditioner) { - assert (n_routes > 0); - --n_routes; - } + /* monitor is not part of the order */ if (_monitor_out) { assert (n_routes > 0); --n_routes; } - 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()); - { PresentationInfo::ChangeSuspender cs; + ensure_route_presentation_info_gap (order, new_routes.size()); for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) { @@ -3558,25 +3551,13 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool } } - if (!r->presentation_info().special()) { + if (!r->presentation_info().special (false)) { DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name())); /* presentation info order may already have been set from XML */ if (!r->presentation_info().order_set()) { - /* this is only useful for headless sessions, - * Editor::add_routes() and Mixer_UI::add_routes() will - * override it following the RouteAdded signal. - * - * Also routes should be sorted before VCAs (like the GUI does). - * Session::ensure_route_presentation_info_gap() does not special case VCAs either. - * - * ... but not to worry, the GUI's - * gtk2_ardour/route_sorter.h and various ::sync_presentation_info_from_treeview() - * handle this :) - */ - if (order == PresentationInfo::max_order) { /* just add to the end */ r->set_presentation_order (n_routes + added); @@ -3610,6 +3591,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool ARDOUR::GUIIdle (); } + ensure_stripable_sort_order (); } if (_monitor_out && IO::connecting_legal) { @@ -3825,22 +3807,23 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) routes.flush (); - /* try to cause everyone to drop their references - * and unregister ports from the backend + /* 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 so; - so.add (Properties::order); - if (send_selected) { - so.add (Properties::selected); - } - PresentationInfo::Change (so); + 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) { (*iter)->drop_references (); } @@ -3849,6 +3832,9 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) return; } + PropertyChange pc; + pc.add (Properties::order); + PresentationInfo::Change (pc); /* save the new state of the world */ @@ -4198,7 +4184,8 @@ Session::muted () const StripableList all; get_stripables (all); for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) { - if ((*i)->is_auditioner() || (*i)->is_monitor()) { + assert (!(*i)->is_auditioner()); // XXX remove me + if ((*i)->is_monitor()) { continue; } boost::shared_ptr r = boost::dynamic_pointer_cast(*i); @@ -4222,7 +4209,8 @@ Session::cancel_all_mute () std::vector > muted; boost::shared_ptr cl (new ControlList); for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) { - if ((*i)->is_auditioner() || (*i)->is_monitor()) { + assert (!(*i)->is_auditioner()); + if ((*i)->is_monitor()) { continue; } boost::shared_ptr r = boost::dynamic_pointer_cast (*i); @@ -4251,6 +4239,25 @@ 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; +} + +RouteList +Session::get_routelist (bool mixer_order) const +{ + boost::shared_ptr r = routes.reader (); + RouteList rv; + rv.insert (rv.end(), r->begin(), r->end()); + rv.sort (Stripable::Sorter (mixer_order)); + return rv; +} + boost::shared_ptr Session::get_routes_with_internal_returns() const { @@ -4273,8 +4280,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; @@ -4433,21 +4440,6 @@ Session::processor_by_id (PBD::ID id) const return boost::shared_ptr (); } -boost::shared_ptr -Session::track_by_diskstream_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; - } - } - - return boost::shared_ptr (); -} - boost::shared_ptr Session::get_remote_nth_route (PresentationInfo::order_t n) const { @@ -4461,7 +4453,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) { @@ -4493,26 +4485,11 @@ 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; @@ -4534,16 +4511,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); } } @@ -4610,7 +4587,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 ()); } } @@ -4618,13 +4595,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) { @@ -4649,17 +4626,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); } } @@ -4870,13 +4847,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 @@ -5177,17 +5157,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); @@ -5202,38 +5177,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 @@ -5252,7 +5225,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 (); } @@ -5267,7 +5240,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 (); } @@ -5312,7 +5285,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())); } @@ -5415,7 +5388,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; } @@ -5431,7 +5404,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 (); } } @@ -5442,7 +5415,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 = {} }" @@ -5459,7 +5432,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 @@ -5563,14 +5535,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"); } @@ -5589,6 +5568,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; @@ -5625,18 +5609,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 { @@ -5659,37 +5631,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 @@ -5717,11 +5676,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 @@ -5793,7 +5752,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 (); } } @@ -6041,7 +6000,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, @@ -6051,12 +6010,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; @@ -6110,7 +6069,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) { @@ -6168,7 +6127,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) { @@ -6184,9 +6143,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()); } } } @@ -6293,6 +6252,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 { @@ -6360,6 +6325,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) { @@ -6483,16 +6465,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); @@ -6536,20 +6517,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); @@ -6582,7 +6563,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) @@ -6602,7 +6583,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) @@ -6765,115 +6746,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; @@ -6891,11 +6889,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; @@ -6913,50 +6913,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 (); } } @@ -6998,36 +6977,10 @@ Session::operation_in_progress (GQuark op) const return (find (_current_trans_quarks.begin(), _current_trans_quarks.end(), op) != _current_trans_quarks.end()); } -boost::shared_ptr -Session::ltc_input_port () const -{ - return _ltc_input->nth (0); -} - boost::shared_ptr Session::ltc_output_port () const { - return _ltc_output->nth (0); -} - -void -Session::reconnect_ltc_input () -{ - if (_ltc_input) { - - string src = Config->get_ltc_source_port(); - - _ltc_input->disconnect (this); - - if (src != _("None") && !src.empty()) { - _ltc_input->nth (0)->connect (src); - } - - if ( ARDOUR::Profile->get_trx () ) { - // Tracks need this signal to update timecode_source_dropdown - MtcOrLtcInputPortChanged (); //emit signal - } - } + return _ltc_output ? _ltc_output->nth (0) : boost::shared_ptr (); } void @@ -7046,18 +6999,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 @@ -7066,7 +7019,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 @@ -7075,7 +7028,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 @@ -7288,12 +7241,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 (); } }