diff options
-rw-r--r-- | libs/ardour/ardour/route.h | 22 | ||||
-rw-r--r-- | libs/ardour/ardour/session.h | 8 | ||||
-rw-r--r-- | libs/ardour/auditioner.cc | 2 | ||||
-rw-r--r-- | libs/ardour/disk_reader.cc | 18 | ||||
-rw-r--r-- | libs/ardour/disk_writer.cc | 61 | ||||
-rw-r--r-- | libs/ardour/route.cc | 328 | ||||
-rw-r--r-- | libs/ardour/session.cc | 139 | ||||
-rw-r--r-- | libs/ardour/session_process.cc | 41 | ||||
-rw-r--r-- | libs/ardour/session_transport.cc | 34 | ||||
-rw-r--r-- | libs/ardour/track.cc | 2 |
10 files changed, 421 insertions, 234 deletions
diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index e726e2c77b..c0f2048a18 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -203,7 +203,6 @@ public: boost::shared_ptr<PeakMeter> peak_meter() { return _meter; } boost::shared_ptr<const PeakMeter> peak_meter() const { return _meter; } boost::shared_ptr<PeakMeter> shared_peak_meter() const { return _meter; } - boost::shared_ptr<DelayLine> delay_line() const { return _delayline; } void flush_processors (); @@ -341,14 +340,15 @@ public: */ bool remove_sidechain (boost::shared_ptr<Processor> proc) { return add_remove_sidechain (proc, false); } + samplecnt_t update_signal_latency (bool apply_to_delayline = false); + virtual void apply_latency_compensation (); samplecnt_t set_private_port_latencies (bool playback) const; void set_public_port_latencies (samplecnt_t, bool playback) const; - samplecnt_t update_signal_latency (bool set_initial_delay = false); - virtual void set_latency_compensation (samplecnt_t); void set_user_latency (samplecnt_t); samplecnt_t signal_latency() const { return _signal_latency; } + samplecnt_t playback_latency (bool incl_downstream = false) const; PBD::Signal0<void> active_changed; PBD::Signal0<void> denormal_protection_changed; @@ -598,18 +598,17 @@ public: void curve_reallocate (); virtual void set_block_size (pframes_t nframes); - virtual samplecnt_t check_initial_delay (samplecnt_t nframes, samplepos_t&) { return nframes; } - void fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pframes_t nframes); - void passthru (BufferSet&, samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes, int declick, bool gain_automation_ok); + void passthru (BufferSet&, samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes, int declick, bool gain_automation_ok, bool run_disk_reader); virtual void write_out_of_band_data (BufferSet& /* bufs */, samplepos_t /* start_sample */, samplepos_t /* end_sample */, samplecnt_t /* nframes */) {} - virtual void process_output_buffers (BufferSet& bufs, - samplepos_t start_sample, samplepos_t end_sample, - pframes_t nframes, int declick, - bool gain_automation_ok); + void process_output_buffers (BufferSet& bufs, + samplepos_t start_sample, samplepos_t end_sample, + pframes_t nframes, int declick, + bool gain_automation_ok, + bool run_disk_processors); void flush_processor_buffers_locked (samplecnt_t nframes); @@ -623,7 +622,6 @@ public: bool _active; samplecnt_t _signal_latency; - samplecnt_t _initial_delay; // remove me ProcessorList _processors; mutable Glib::Threads::RWLock _processor_lock; @@ -749,6 +747,8 @@ private: void setup_invisible_processors (); + void no_roll_unlocked (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample); + pframes_t latency_preroll (pframes_t nframes, samplepos_t& start_sample, samplepos_t& end_sample); void reset_instrument_info (); void solo_control_changed (bool self, PBD::Controllable::GroupControlDisposition); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 69c823720c..e04e99de88 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -478,7 +478,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop samplecnt_t worst_input_latency () const { return _worst_input_latency; } samplecnt_t worst_track_latency () const { return _worst_track_latency; } samplecnt_t worst_track_out_latency () const { return _worst_track_out_latency; } - samplecnt_t worst_playback_latency () const { return _worst_output_latency + _worst_track_latency; } + samplecnt_t worst_playback_latency () const { return std::max (_worst_output_latency, _worst_track_latency); } + samplecnt_t worst_latency_preroll () const; struct SaveAs { std::string new_parent_folder; /* parent folder where new session folder will be created */ @@ -684,6 +685,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop samplepos_t requested_return_sample() const { return _requested_return_sample; } void set_requested_return_sample(samplepos_t return_to); + samplecnt_t remaining_latency_preroll () const { return _remaining_latency_preroll; } + enum PullupFormat { pullup_Plus4Plus1, pullup_Plus4, @@ -721,7 +724,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop double transport_speed() const { return _count_in_samples > 0 ? 0. : _transport_speed; } bool transport_stopped() const { return _transport_speed == 0.0; } - bool transport_rolling() const { return _transport_speed != 0.0 && _count_in_samples == 0; } + bool transport_rolling() const { return _transport_speed != 0.0 && _count_in_samples == 0 && _remaining_latency_preroll == 0; } bool silent () { return _silent; } @@ -1248,6 +1251,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop bool _session_range_end_is_free; Slave* _slave; bool _silent; + samplecnt_t _remaining_latency_preroll; // varispeed playback double _transport_speed; diff --git a/libs/ardour/auditioner.cc b/libs/ardour/auditioner.cc index 344e92b2f4..3c9b784ae1 100644 --- a/libs/ardour/auditioner.cc +++ b/libs/ardour/auditioner.cc @@ -246,7 +246,7 @@ Auditioner::roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_s } } - process_output_buffers (bufs, start_sample, end_sample, nframes, declick, !_session.transport_stopped()); + process_output_buffers (bufs, start_sample, end_sample, nframes, declick, !_session.transport_stopped(), true); /* note: auditioner never writes to disk, so we don't care about the * disk writer status (it's buffers will always have no data in them). diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc index a9f403d40b..624a9ac17d 100644 --- a/libs/ardour/disk_reader.cc +++ b/libs/ardour/disk_reader.cc @@ -253,11 +253,17 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp } } - if (speed == 0.0 && (ms == MonitoringDisk)) { - /* stopped. Don't accidentally pass any data from disk - * into our outputs (e.g. via interpolation) - */ - bufs.silence (nframes, 0); + if (speed == 0.0) { + /* stopped. Don't accidentally pass any data from disk + * into our outputs (e.g. via interpolation) + * nor jump ahead playback_sample when not rolling + */ + if (ms == MonitoringDisk) { + /* when monitoring disk, clear input data so far, + * everything before the disk processor is not relevant. + */ + bufs.silence (nframes, 0); + } return; } @@ -625,6 +631,8 @@ DiskReader::seek (samplepos_t sample, bool complete_refill) ChannelList::iterator chan; boost::shared_ptr<ChannelList> c = channels.reader(); + //sample = std::max ((samplecnt_t)0, sample -_session.worst_output_latency ()); + for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { (*chan)->buf->reset (); } diff --git a/libs/ardour/disk_writer.cc b/libs/ardour/disk_writer.cc index c05b19a12c..a3cfc69b1b 100644 --- a/libs/ardour/disk_writer.cc +++ b/libs/ardour/disk_writer.cc @@ -96,11 +96,9 @@ DiskWriter::check_record_status (samplepos_t transport_sample, double speed, boo const int global_rec_enabled = 0x1; const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled); - /* merge together the 3 factors that affect record status, and compute - * what has changed. - */ + /* merge together the 3 factors that affect record status, and compute what has changed. */ - rolling = _session.transport_speed() != 0.0f; + rolling = speed != 0.0f; possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record; change = possibly_recording ^ last_possibly_recording; @@ -114,20 +112,28 @@ DiskWriter::check_record_status (samplepos_t transport_sample, double speed, boo return; } - capture_start_sample = transport_sample; - first_recordable_sample = capture_start_sample + _input_latency; + capture_start_sample = _session.transport_sample (); + first_recordable_sample = capture_start_sample; + if (_alignment_style == ExistingMaterial) { - // XXX + first_recordable_sample += _capture_offset + _playback_offset; } - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %7 (%9) FRF = %2 CSF = %4 CO = %5, EMO = %6 RD = %8 WOL %10 WTL %11\n", - name(), first_recordable_sample, last_recordable_sample, capture_start_sample, - 0, - 0, + last_recordable_sample = max_samplepos; + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %2 (STS: %3) CS:%4 FRS: %5 IL: %7, OL: %8 CO: %r9 PO: %10 WOL: %11 WIL: %12\n", + name(), transport_sample, _session.transport_sample(), + capture_start_sample, + first_recordable_sample, + last_recordable_sample, + _input_latency, + _output_latency, + _capture_offset, + _playback_offset, _session.worst_output_latency(), - _session.worst_track_latency())); + _session.worst_input_latency())); prepare_record_status (capture_start_sample); @@ -161,8 +167,7 @@ DiskWriter::check_record_status (samplepos_t transport_sample, double speed, boo } void -DiskWriter::calculate_record_range (Evoral::OverlapType ot, samplepos_t transport_sample, samplecnt_t nframes, - samplecnt_t & rec_nframes, samplecnt_t & rec_offset) +DiskWriter::calculate_record_range (Evoral::OverlapType ot, samplepos_t transport_sample, samplecnt_t nframes, samplecnt_t & rec_nframes, samplecnt_t & rec_offset) { switch (ot) { case Evoral::OverlapNone: @@ -347,31 +352,31 @@ void DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool result_required) { + if (!_active && !_pending_active) { + return; + } + _active = _pending_active; + uint32_t n; boost::shared_ptr<ChannelList> c = channels.reader(); ChannelList::iterator chan; + samplecnt_t rec_offset = 0; samplecnt_t rec_nframes = 0; bool nominally_recording; + bool re = record_enabled (); bool can_record = _session.actively_recording (); - if (_active) { - if (!_pending_active) { - _active = false; - return; - } - } else { - if (_pending_active) { - _active = true; - } else { - return; - } - } - _need_butler = false; - check_record_status (start_sample, 1, can_record); +#ifndef NDEBUG + if (speed != 0 && re) { + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: run() start: %2 end: %3 NF: %4\n", _name, start_sample, end_sample, nframes)); + } +#endif + + check_record_status (start_sample, speed, can_record); if (nframes == 0) { return; diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 75380b2414..ee4068dda1 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -95,7 +95,6 @@ Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType , Muteable (sess, name) , _active (true) , _signal_latency (0) - , _initial_delay (0) , _disk_io_point (DiskIOPreFader) , _pending_process_reorder (0) , _pending_signals (0) @@ -193,12 +192,9 @@ Route::init () _amp->set_display_name (_("Monitor")); } -#if 0 // not used - just yet if (!is_master() && !is_monitor() && !is_auditioner()) { - _delayline.reset (new DelayLine (_session, _name)); - add_processor (_delayline, PreFader); + _delayline.reset (new DelayLine (_session, name () + ":in")); } -#endif /* and input trim */ @@ -311,19 +307,27 @@ Route::maybe_declick (BufferSet&, samplecnt_t, int) void Route::process_output_buffers (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes, - int declick, bool gain_automation_ok) + int declick, bool gain_automation_ok, bool run_disk_reader) { /* Caller must hold process lock */ assert (!AudioEngine::instance()->process_lock().trylock()); Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked()) { - // can this actually happen? functions calling process_output_buffers() - // already take a reader-lock. + // can this actually happen? + // Places that need a WriterLock on (_processor_lock) must also take the process-lock. bufs.silence (nframes, 0); + assert (0); // ...one way to find out. return; } + /* We should offset the route-owned ctrls by the given latency, however + * this only affects Mute. Other route-owned controls (solo, polarity..) + * are not automatable. + * + * Mute has its own issues since there's not a single mute-point, + * but in general + */ automation_run (start_sample, nframes); /* figure out if we're going to use gain automation */ @@ -341,13 +345,53 @@ Route::process_output_buffers (BufferSet& bufs, nframes); } - /* Tell main outs what to do about monitoring. We do this so that - on a transition between monitoring states we get a de-clicking gain - change in the _main_outs delivery, if config.get_use_monitor_fades() - is true. + /* We align the playhead to output. The user hears what the clock says: + * When the playhead/clock says 1:00:00:00 the user will hear the audio sample + * at 1:00:00:00. sample_start will be [sample at] 1:00:00:00 + * + * e.g. clock says Time T = 0, sample_start = 0 + * Disk-read(play) -> latent-plugin (+10) -> fader-automation -> output (+5) + * -> total playback latency "disk -> out" is 15. + * -> at Time T= -15, the disk-reader reads sample T=0. + * By the Time T=0 is reached (dt=15 later) that sample is audible. + */ - We override this in the case where we have an internal generator. - */ + start_sample += _signal_latency; + end_sample += _signal_latency; + + start_sample += _output->latency (); + end_sample += _output->latency (); + + /* Note: during intial pre-roll 'start_sample' as passed as argument can be negative. + * Functions calling process_output_buffers() will set "run_disk_reader" + * to false if the pre-roll count-down is larger than playback_latency (). + * + * playback_latency() is guarnteed to be <= _signal_latency + _output->latency () + */ + assert (!_disk_reader || !run_disk_reader || start_sample >= 0); + + /* however the disk-writer may need to pick up output from other tracks + * during pre-roll (in particular if this route has latent effects after the disk). + * + * e.g. track 1 play -> latency A --port--> track2 capture -> latency B ---> out + * total pre-roll = A + B. + * + * Note the disk-writer has built-in overlap detection (it's safe to run it early) + * given that + */ + bool run_disk_writer = false; + if (_disk_writer) { + samplecnt_t latency_preroll = _session.remaining_latency_preroll (); + run_disk_writer = latency_preroll < nframes + (_signal_latency + _output->latency ()); + } + + /* Tell main outs what to do about monitoring. We do this so that + * on a transition between monitoring states we get a de-clicking gain + * change in the _main_outs delivery, if config.get_use_monitor_fades() + * is true. + * + * We override this in the case where we have an internal generator. + */ bool silence = _have_internal_generator ? false : (monitoring_state () == MonitoringSilence); _main_outs->no_outs_cuz_we_no_monitor (silence); @@ -356,6 +400,8 @@ Route::process_output_buffers (BufferSet& bufs, GLOBAL DECLICK (for transport changes etc.) ----------------------------------------------------------------------------------------- */ + // XXX not latency compensated. calls Amp::declick, but there may be + // plugins between disk and Fader. maybe_declick (bufs, nframes, declick); _pending_declick = 0; @@ -363,6 +409,14 @@ Route::process_output_buffers (BufferSet& bufs, DENORMAL CONTROL/PHASE INVERT ----------------------------------------------------------------------------------------- */ + /* TODO phase-control should become a processor, or rather a Stub-processor: + * a point in the chain which calls a special-cased private Route method. + * _phase_control is route-owned and dynamic.) + * and we should rename it to polarity. + * + * denormals: we'll need to protect silent inputs as well as silent disk + * (when not monitoring input). Or simply drop that feature. + */ if (!_phase_control->none()) { int chn = 0; @@ -407,8 +461,8 @@ Route::process_output_buffers (BufferSet& bufs, sp[nx] += 1.0e-27f; } } - } + } /* ------------------------------------------------------------------------------------------- @@ -423,6 +477,12 @@ Route::process_output_buffers (BufferSet& bufs, for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + /* TODO check for split cycles here. + * + * start_frame, end_frame is adjusted by latency and may + * cross loop points. + */ + if (meter_already_run && boost::dynamic_pointer_cast<PeakMeter> (*i)) { /* don't ::run() the meter, otherwise it will have its previous peak corrupted */ continue; @@ -442,26 +502,55 @@ Route::process_output_buffers (BufferSet& bufs, } #endif - /* should we NOT run plugins here if the route is inactive? - do we catch route != active somewhere higher? - */ - if (boost::dynamic_pointer_cast<Send>(*i) != 0) { - boost::dynamic_pointer_cast<Send>(*i)->set_delay_in(_signal_latency - latency); + // inform the reader that we're sending a late signal, + // relative to original (output aligned) start_sample + boost::dynamic_pointer_cast<Send>(*i)->set_delay_in (latency); } if (boost::dynamic_pointer_cast<PluginInsert>(*i) != 0) { - const samplecnt_t longest_session_latency = _initial_delay + _signal_latency; + /* set potential sidechain ports, capture and playback latency. + * This effectively sets jack port latency which should include + * up/downstream latencies. + * + * However, the value is not used by Ardour (2017-09-20) and calling + * IO::latency() is expensive, so we punt. + * + * capture should be + * input()->latenct + latency, + * playback should be + * output->latency() + _signal_latency - latency + * + * Also see note below, _signal_latency may be smaller than latency + * if a plugin's latency increases while it's running. + */ + const samplecnt_t playback_latency = std::max ((samplecnt_t)0, _signal_latency - latency); boost::dynamic_pointer_cast<PluginInsert>(*i)->set_sidechain_latency ( - _initial_delay + latency, longest_session_latency - latency); + /* input->latency() + */ latency, /* output->latency() + */ playback_latency); } - //cerr << name() << " run " << (*i)->name() << endl; - (*i)->run (bufs, start_sample - latency, end_sample - latency, speed, nframes, *i != _processors.back()); + double pspeed = speed; + if ((!run_disk_reader && (*i) == _disk_reader) || (!run_disk_writer && (*i) == _disk_writer)) { + /* run with speed 0, no-roll */ + pspeed = 0; + } + + (*i)->run (bufs, start_sample - latency, end_sample - latency, pspeed, nframes, *i != _processors.back()); bufs.set_count ((*i)->output_streams()); + /* Note: plugin latency may change. While the plugin does inform the session via + * processor_latency_changed(). But the session may not yet have gotten around to + * update the actual worste-case and update this track's _signal_latency. + * + * So there can be cases where adding up all latencies may not equal _signal_latency. + */ if ((*i)->active ()) { latency += (*i)->signal_latency (); } +#if 0 + if ((*i) == _delayline) { + latency += _delayline->get_delay (); + } +#endif } } @@ -595,11 +684,11 @@ Route::monitor_run (samplepos_t start_sample, samplepos_t end_sample, pframes_t assert (is_monitor()); BufferSet& bufs (_session.get_route_buffers (n_process_buffers())); fill_buffers_with_input (bufs, _input, nframes); - passthru (bufs, start_sample, end_sample, nframes, declick, true); + passthru (bufs, start_sample, end_sample, nframes, declick, true, false); } void -Route::passthru (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes, int declick, bool gain_automation_ok) +Route::passthru (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes, int declick, bool gain_automation_ok, bool run_disk_reader) { _silent = false; @@ -619,7 +708,7 @@ Route::passthru (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp /* run processor chain */ - process_output_buffers (bufs, start_sample, end_sample, nframes, declick, gain_automation_ok); + process_output_buffers (bufs, start_sample, end_sample, nframes, declick, gain_automation_ok, run_disk_reader); } void @@ -629,7 +718,7 @@ Route::passthru_silence (samplepos_t start_sample, samplepos_t end_sample, pfram bufs.set_count (_input->n_ports()); write_out_of_band_data (bufs, start_sample, end_sample, nframes); - process_output_buffers (bufs, start_sample, end_sample, nframes, declick, false); + process_output_buffers (bufs, start_sample, end_sample, nframes, declick, false, false); } void @@ -2834,10 +2923,7 @@ Route::set_processor_state (const XMLNode& node) _meter->set_state (**niter, Stateful::current_state_version); new_order.push_back (_meter); } else if (prop->value() == "delay") { - if (_delayline) { - _delayline->set_state (**niter, Stateful::current_state_version); - new_order.push_back (_delayline); - } + // skip -- internal } else if (prop->value() == "main-outs") { _main_outs->set_state (**niter, Stateful::current_state_version); } else if (prop->value() == "intreturn") { @@ -3578,19 +3664,95 @@ Route::no_roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sam */ } + no_roll_unlocked (nframes, start_sample, end_sample); + + return 0; +} + +void +Route::no_roll_unlocked (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample) +{ BufferSet& bufs = _session.get_route_buffers (n_process_buffers()); fill_buffers_with_input (bufs, _input, nframes); + /* filter captured data before meter sees it */ + filter_input (bufs); + if (_meter_point == MeterInput) { _meter->run (bufs, start_sample, end_sample, 0.0, nframes, true); } - passthru (bufs, start_sample, end_sample, nframes, 0, true); + passthru (bufs, start_sample, end_sample, nframes, 0, true, false); flush_processor_buffers_locked (nframes); +} - return 0; +samplecnt_t +Route::playback_latency (bool incl_downstream) const +{ + samplecnt_t rv; + if (_disk_reader) { + rv = _disk_reader->output_latency (); + } else { + rv = _signal_latency; + } + if (incl_downstream) { + rv += _output->connected_latency (true); + } else { + rv += _output->latency (); + } + return rv; +} + +pframes_t +Route::latency_preroll (pframes_t nframes, samplepos_t& start_sample, samplepos_t& end_sample) +{ + samplecnt_t latency_preroll = _session.remaining_latency_preroll (); + if (latency_preroll == 0) { + return nframes; + } + if (!_disk_reader) { + start_sample -= latency_preroll; + end_sample -= latency_preroll; + return nframes; + } + + samplecnt_t route_offset = playback_latency (); + + if (latency_preroll > route_offset + nframes) { + no_roll_unlocked (nframes, start_sample - latency_preroll, end_sample - latency_preroll); + return 0; + } + + if (latency_preroll > route_offset) { + + samplecnt_t skip = latency_preroll - route_offset; + no_roll_unlocked (skip, start_sample - latency_preroll, start_sample - latency_preroll + skip); + + if (nframes == skip) { + return 0; + } + + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i); + if (iop) { + iop->increment_port_buffer_offset (skip); + } + } + _input->increment_port_buffer_offset (skip); + _output->increment_port_buffer_offset (skip); + + start_sample -= route_offset; + end_sample -= route_offset; + + return nframes - skip; + } + + start_sample -= latency_preroll; + end_sample -= latency_preroll; + return nframes; } int @@ -3609,6 +3771,9 @@ Route::roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample } return 0; } + if ((nframes = latency_preroll (nframes, start_sample, end_sample)) == 0) { + return 0; + } _silent = false; @@ -3624,7 +3789,7 @@ Route::roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sample _meter->run (bufs, start_sample, end_sample, 1.0 /*speed()*/, nframes, true); } - passthru (bufs, start_sample, end_sample, nframes, declick, (!_disk_writer || !_disk_writer->record_enabled()) && _session.transport_rolling()); + passthru (bufs, start_sample, end_sample, nframes, declick, (!_disk_writer || !_disk_writer->record_enabled()) && _session.transport_rolling(), true); if ((_disk_reader && _disk_reader->need_butler()) || (_disk_writer && _disk_writer->need_butler())) { need_butler = true; @@ -3863,34 +4028,25 @@ Route::add_export_point() Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lw (_processor_lock); - // this aligns all tracks; but not tracks + busses - samplecnt_t latency = _session.worst_track_out_latency (); // FIXME - assert (latency >= _initial_delay); - _capturing_processor.reset (new CapturingProcessor (_session, latency - _initial_delay)); - _capturing_processor->activate (); - + /* Align all tracks for stem-export w/o processing. + * Compensate for all plugins between the this route's disk-reader + * and the common final downstream output (ie alignment point for playback). + */ + _capturing_processor.reset (new CapturingProcessor (_session, playback_latency (true))); configure_processors_unlocked (0, &lw); - + _capturing_processor->activate (); } return _capturing_processor; } samplecnt_t -Route::update_signal_latency (bool set_initial_delay) +Route::update_signal_latency (bool apply_to_delayline) { Glib::Threads::RWLock::ReaderLock lm (_processor_lock); - samplecnt_t l_in = _input->latency (); + samplecnt_t l_in = 0; // _input->latency (); samplecnt_t l_out = _output->user_latency(); - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i)->active ()) { - l_in += (*i)->signal_latency (); - } - (*i)->set_input_latency (l_in); - } - for (ProcessorList::reverse_iterator i = _processors.rbegin(); i != _processors.rend(); ++i) { (*i)->set_output_latency (l_out); if ((*i)->active ()) { @@ -3902,11 +4058,21 @@ Route::update_signal_latency (bool set_initial_delay) _signal_latency = l_out; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->active ()) { + l_in += (*i)->signal_latency (); + } + (*i)->set_input_latency (l_in); + (*i)->set_playback_offset (_signal_latency + _output->latency ()); + (*i)->set_capture_offset (_input->latency ()); + } + + lm.release (); - if (set_initial_delay) { + if (apply_to_delayline) { /* see also Session::post_playback_latency() */ - set_latency_compensation (_session.worst_track_latency () + _session.worst_track_out_latency () - output ()->latency ()); + apply_latency_compensation (); } if (_signal_latency != l_out) { @@ -3924,26 +4090,29 @@ Route::set_user_latency (samplecnt_t nframes) } void -Route::set_latency_compensation (samplecnt_t longest_session_latency) +Route::apply_latency_compensation () { - samplecnt_t old = _initial_delay; - assert (!_disk_reader || _disk_reader->output_latency () <= _signal_latency); + if (_delayline) { + samplecnt_t old = _delayline->get_delay (); - if (_disk_reader && _disk_reader->output_latency () < longest_session_latency) { - _initial_delay = longest_session_latency - _disk_reader->output_latency (); - } else { - _initial_delay = 0; - } + samplecnt_t play_lat_in = _input->connected_latency (true); + samplecnt_t play_lat_out = _output->connected_latency (true); + samplecnt_t latcomp = play_lat_in - play_lat_out - _signal_latency; - DEBUG_TRACE (DEBUG::Latency, string_compose ( - "%1: compensate for maximum latency of %2," - "given own latency of %3, using initial delay of %4\n", - name(), longest_session_latency, _signal_latency, _initial_delay)); +#if 0 // DEBUG + samplecnt_t capt_lat_in = _input->connected_latency (false); + samplecnt_t capt_lat_out = _output->connected_latency (false); + samplecnt_t latcomp_capt = capt_lat_out - capt_lat_in - _signal_latency; - if (_initial_delay != old) { - //initial_delay_changed (); /* EMIT SIGNAL */ - } + cout << "ROUTE " << name() << " delay for " << latcomp << " (c: " << latcomp_capt << ")" << endl; +#endif + + _delayline->set_delay (latcomp > 0 ? latcomp : 0); + if (old != _delayline->get_delay ()) { + signal_latency_updated (); /* EMIT SIGNAL */ + } + } } void @@ -4645,12 +4814,6 @@ Route::setup_invisible_processors () } } -#if 0 // not used - just yet - if (!is_master() && !is_monitor() && !is_auditioner()) { - new_processors.push_front (_delayline); - } -#endif - /* MONITOR CONTROL */ if (_monitor_control && is_monitor ()) { @@ -4744,6 +4907,15 @@ Route::setup_invisible_processors () } } + if (!is_master() && !is_monitor() && !is_auditioner()) { + ProcessorList::iterator reader_pos = find (new_processors.begin(), new_processors.end(), _disk_reader); + if (reader_pos != new_processors.end()) { + /* insert before disk-reader */ + new_processors.insert (reader_pos, _delayline); + } else { + new_processors.push_front (_delayline); + } + } _processors = new_processors; @@ -4887,9 +5059,11 @@ Route::non_realtime_locate (samplepos_t pos) _pannable->non_realtime_locate (pos); } - if (_delayline.get()) { - _delayline.get()->flush(); +#if 0 // XXX mayhaps clear delayline here (and at stop?) + if (_delayline) { + _delayline->flush (); } +#endif { //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index b903ed66fc..8bf18fc730 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -186,6 +186,7 @@ Session::Session (AudioEngine &eng, , _session_range_end_is_free (true) , _slave (0) , _silent (false) + , _remaining_latency_preroll (0) , _transport_speed (0) , _default_transport_speed (1.0) , _last_transport_speed (0) @@ -2171,14 +2172,12 @@ Session::maybe_enable_record (bool rt_context) samplepos_t Session::audible_sample (bool* latent_locate) const { - samplepos_t ret; - - sampleoffset_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_sample(); @@ -2186,57 +2185,37 @@ Session::audible_sample (bool* latent_locate) const ret = _transport_sample; } - if (transport_rolling()) { - ret -= offset; - - /* Check to see if we have passed the first guaranteed - * audible sample past our last start position. if not, - * return that last start point because in terms - * of audible samples, 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(); - sampleoffset_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 ((samplepos_t)0, ret); } - samplecnt_t Session::preroll_samples (samplepos_t pos) const { @@ -5744,10 +5723,7 @@ Session::graph_reordered () boost::shared_ptr<RouteList> rl = routes.reader (); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); - if (tr) { - tr->update_latency_information (); - } + (*i)->update_signal_latency (true); // XXX } } @@ -6857,7 +6833,6 @@ Session::unknown_processors () const return p; } -/* this is always called twice, first for playback (true), then for capture */ void Session::update_latency (bool playback) { @@ -6873,7 +6848,6 @@ Session::update_latency (bool playback) /* Note; RouteList is sorted as process-graph */ boost::shared_ptr<RouteList> r = routes.reader (); - samplecnt_t max_latency = 0; if (playback) { /* reverse the list so that we work backwards from the last route to run to the first */ @@ -6882,34 +6856,14 @@ Session::update_latency (bool playback) 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. - */ - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - max_latency = max (max_latency, (*i)->set_private_port_latencies (playback)); - } - - /* 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); + samplecnt_t latency = (*i)->set_private_port_latencies (playback); + (*i)->set_public_port_latencies (latency, playback); } if (playback) { - post_playback_latency (); - } else { - post_capture_latency (); } @@ -6923,20 +6877,16 @@ Session::post_playback_latency () boost::shared_ptr<RouteList> r = routes.reader (); - _worst_track_out_latency = 0; + _worst_track_out_latency = 0; // XXX remove me for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { assert (!(*i)->is_auditioner()); // XXX remove me - if (!(*i)->active()) { continue ; } _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ()); - if (boost::dynamic_pointer_cast<Track> (*i)) { - _worst_track_out_latency = max (_worst_track_out_latency, (*i)->output ()->latency ()); - } } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (!(*i)->active()) { continue ; } - (*i)->set_latency_compensation (_worst_track_out_latency - (*i)->output ()->latency ()); + (*i)->apply_latency_compensation (); } } @@ -6945,15 +6895,11 @@ Session::post_capture_latency () { set_worst_capture_latency (); - /* reflect any changes in capture latencies into capture offsets - */ + /* reflect any changes in capture latencies into capture offsets */ boost::shared_ptr<RouteList> rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); - if (tr) { - tr->update_latency_information (); - } + (*i)->update_signal_latency (); } } @@ -6995,6 +6941,8 @@ 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)); } @@ -7014,6 +6962,10 @@ Session::set_worst_capture_latency () boost::shared_ptr<RouteList> r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); + if (!tr) { + continue; + } _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency()); } @@ -7023,6 +6975,7 @@ Session::set_worst_capture_latency () void Session::update_latency_compensation (bool force_whole_graph) { + // TODO: consolidate bool some_track_latency_changed = false; if (_state_of_the_state & (InitialConnecting|Deletion)) { @@ -7039,7 +6992,7 @@ Session::update_latency_compensation (bool force_whole_graph) assert (!(*i)->is_auditioner()); // XXX remove me if ((*i)->active()) { samplecnt_t tl; - if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency ())) { + if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency () /* - (*i)->output()->user_latency()*/)) { some_track_latency_changed = true; } _worst_track_latency = max (tl, _worst_track_latency); @@ -7055,13 +7008,8 @@ Session::update_latency_compensation (bool force_whole_graph) _engine.update_latencies (); } - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); - if (!tr) { - continue; - } - tr->update_latency_information (); + (*i)->update_signal_latency (true); } } @@ -7394,10 +7342,9 @@ 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 Track::update_latency_information() - * which calls DiskWriter::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)) { update_latency_compensation (); diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index 1674cd9a7a..ed5422cf07 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -292,18 +292,47 @@ Session::process_with_events (pframes_t nframes) process_event (ev); } - /* count in */ + /* only count-in when going to roll at speed 1.0 */ if (_transport_speed != 1.0 && _count_in_samples > 0) { _count_in_samples = 0; } + if (_transport_speed == 0.0) { + _remaining_latency_preroll = 0; + } + + assert (_count_in_samples == 0 || _remaining_latency_preroll == 0 || _count_in_samples == _remaining_latency_preroll); - if (_count_in_samples > 0) { - samplecnt_t ns = std::min ((samplecnt_t)nframes, _count_in_samples); + if (_count_in_samples > 0 || _remaining_latency_preroll > 0) { + samplecnt_t ns; + + if (_remaining_latency_preroll > 0) { + ns = std::min ((samplecnt_t)nframes, _remaining_latency_preroll); + } else { + ns = std::min ((samplecnt_t)nframes, _count_in_samples); + } + + if (_count_in_samples > 0) { + run_click (_transport_sample - _count_in_samples, ns); + assert (_count_in_samples >= ns); + _count_in_samples -= ns; + } - no_roll (ns); - run_click (_transport_sample - _count_in_samples, ns); + if (_remaining_latency_preroll > 0) { + if (_count_in_samples == 0) { + click (_transport_sample - _remaining_latency_preroll, ns); + } + if (process_routes (ns, session_needs_butler)) { + fail_roll (ns); + } + } else { + no_roll (ns); + } + + if (_remaining_latency_preroll > 0) { + assert (_remaining_latency_preroll >= ns); + _remaining_latency_preroll -= ns; + } - _count_in_samples -= ns; nframes -= ns; /* process events.. */ diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index bb89ff57d4..07782750e3 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1171,6 +1171,12 @@ Session::start_locate (samplepos_t target_sample, bool with_roll, bool with_flus } } +samplecnt_t +Session::worst_latency_preroll () const +{ + return _worst_output_latency + _worst_input_latency; +} + int Session::micro_locate (samplecnt_t distance) { @@ -1250,15 +1256,16 @@ Session::locate (samplepos_t target_sample, bool with_roll, bool with_flush, boo // thread(s?) can restart. g_atomic_int_inc (&_seek_counter); _last_roll_or_reversal_location = target_sample; - timecode_time(_transport_sample, transmitting_timecode_time); + _remaining_latency_preroll = worst_latency_preroll (); + timecode_time(_transport_sample, transmitting_timecode_time); // XXX here? /* do "stopped" stuff if: * * we are rolling AND - * no autoplay in effect AND - * we're not going to keep rolling after the locate AND - * !(playing a loop with JACK sync) - * + * no autoplay in effect AND + * we're not going to keep rolling after the locate AND + * !(playing a loop with JACK sync) + * */ bool transport_was_stopped = !transport_rolling(); @@ -1492,6 +1499,9 @@ Session::set_transport_speed (double speed, samplepos_t destination_sample, bool /* not zero, not 1.0 ... varispeed */ + // TODO handled transport start.. _remaining_latency_preroll + // and reversal of playback direction. + if ((synced_to_engine()) && speed != 0.0 && speed != 1.0) { warning << string_compose ( _("Global varispeed cannot be supported while %1 is connected to JACK transport control"), @@ -1659,6 +1669,7 @@ Session::start_transport () _last_roll_location = _transport_sample; _last_roll_or_reversal_location = _transport_sample; + _remaining_latency_preroll = worst_latency_preroll (); have_looped = false; @@ -1728,13 +1739,22 @@ Session::start_transport () _count_in_samples *= 1. + bar_fract; } + if (_count_in_samples > _remaining_latency_preroll) { + _remaining_latency_preroll = _count_in_samples; + } + int clickbeat = 0; samplepos_t cf = _transport_sample - _count_in_samples; - while (cf < _transport_sample) { - add_click (cf - _worst_track_latency, clickbeat == 0); + samplecnt_t offset = _click_io->connected_latency (true); + while (cf < _transport_sample + offset) { + add_click (cf, clickbeat == 0); cf += dt; clickbeat = fmod (clickbeat + 1, num); } + + if (_count_in_samples < _remaining_latency_preroll) { + _count_in_samples = _remaining_latency_preroll; + } } } diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 37c3a99961..2d57ef782a 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -536,7 +536,7 @@ Track::no_roll (pframes_t nframes, samplepos_t start_sample, samplepos_t end_sam _meter->run (bufs, start_sample, end_sample, _session.transport_speed(), nframes, true); } - passthru (bufs, start_sample, end_sample, nframes, false, true); + passthru (bufs, start_sample, end_sample, nframes, false, true, false); } flush_processor_buffers_locked (nframes); |