From 24ec0b974d84df061cbbe645668dc62fa7120678 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 26 Sep 2017 17:49:24 +0200 Subject: [PATCH] Properly aligned export (Stem + Session) Delay ports being exported by their playback latency. --- libs/ardour/ardour/export_channel.h | 9 ++++- libs/ardour/ardour/session.h | 3 +- libs/ardour/export_channel.cc | 57 +++++++++++++++++++++-------- libs/ardour/export_handler.cc | 30 +-------------- libs/ardour/session.cc | 1 - libs/ardour/session_export.cc | 42 +++++++-------------- 6 files changed, 63 insertions(+), 79 deletions(-) diff --git a/libs/ardour/ardour/export_channel.h b/libs/ardour/ardour/export_channel.h index a94d65fbdc..8f4d8fc27c 100644 --- a/libs/ardour/ardour/export_channel.h +++ b/libs/ardour/ardour/export_channel.h @@ -22,11 +22,13 @@ #define __ardour_export_channel_h__ #include +#include #include #include #include "pbd/signals.h" +#include "pbd/ringbuffer.h" #include "ardour/buffer_set.h" #include "ardour/export_pointers.h" @@ -68,6 +70,8 @@ class LIBARDOUR_API PortExportChannel : public ExportChannel typedef std::set > PortSet; PortExportChannel (); + ~PortExportChannel (); + void set_max_buffer_size(samplecnt_t samples); void read (Sample const *& data, samplecnt_t samples) const; @@ -83,8 +87,9 @@ class LIBARDOUR_API PortExportChannel : public ExportChannel private: PortSet ports; - boost::scoped_array buffer; - samplecnt_t buffer_size; + samplecnt_t _buffer_size; + boost::scoped_array _buffer; + std::list > > _delaylines; }; diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index e04e99de88..66ab5ce082 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -751,7 +751,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop boost::shared_ptr get_export_handler (); boost::shared_ptr get_export_status (); - int start_audio_export (samplepos_t position, bool realtime = false, bool region_export = false, bool comensate_master_latency = false); + int start_audio_export (samplepos_t position, bool realtime = false, bool region_export = false); PBD::Signal1 ProcessExport; static PBD::Signal2 Exported; @@ -1345,7 +1345,6 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop bool _realtime_export; bool _region_export; samplepos_t _export_preroll; - samplepos_t _export_latency; boost::shared_ptr export_handler; boost::shared_ptr export_status; diff --git a/libs/ardour/export_channel.cc b/libs/ardour/export_channel.cc index 9a7e6a9ace..66fec3a53b 100644 --- a/libs/ardour/export_channel.cc +++ b/libs/ardour/export_channel.cc @@ -35,14 +35,33 @@ using namespace ARDOUR; PortExportChannel::PortExportChannel () - : buffer_size(0) + : _buffer_size (0) { } +PortExportChannel::~PortExportChannel () +{ + _delaylines.clear (); +} + void PortExportChannel::set_max_buffer_size(samplecnt_t samples) { - buffer_size = samples; - buffer.reset (new Sample[samples]); + _buffer_size = samples; + _buffer.reset (new Sample[samples]); + + _delaylines.clear (); + + for (PortSet::const_iterator it = ports.begin(); it != ports.end(); ++it) { + boost::shared_ptr p = it->lock (); + if (!p) { continue; } + samplecnt_t latency = p->private_latency_range (true).max; + PBD::RingBuffer* rb = new PBD::RingBuffer (latency + 1 + _buffer_size); + for (samplepos_t i = 0; i < latency; ++i) { + Sample zero = 0; + rb->write (&zero, 1); + } + _delaylines.push_back (boost::shared_ptr >(rb)); + } } bool @@ -58,10 +77,10 @@ PortExportChannel::operator< (ExportChannel const & other) const void PortExportChannel::read (Sample const *& data, samplecnt_t samples) const { - assert(buffer); - assert(samples <= buffer_size); + assert(_buffer); + assert(samples <= _buffer_size); - if (ports.size() == 1) { + if (ports.size() == 1 && _delaylines.size() ==1 && _delaylines.front()->bufsize () == _buffer_size + 1) { boost::shared_ptr p = ports.begin()->lock (); AudioBuffer& ab (p->get_audio_buffer(samples)); // unsets AudioBuffer::_written data = ab.data(); @@ -69,22 +88,28 @@ PortExportChannel::read (Sample const *& data, samplecnt_t samples) const return; } - memset (buffer.get(), 0, samples * sizeof (Sample)); + memset (_buffer.get(), 0, samples * sizeof (Sample)); + std::list > >::const_iterator di = _delaylines.begin (); for (PortSet::const_iterator it = ports.begin(); it != ports.end(); ++it) { boost::shared_ptr p = it->lock (); - if (p) { - AudioBuffer& ab (p->get_audio_buffer(samples)); // unsets AudioBuffer::_written - Sample* port_buffer = ab.data(); - ab.set_written (true); - - for (uint32_t i = 0; i < samples; ++i) { - buffer[i] += (float) port_buffer[i]; - } + if (!p) { + continue; + } + AudioBuffer& ab (p->get_audio_buffer(samples)); // unsets AudioBuffer::_written + Sample* port_buffer = ab.data(); + ab.set_written (true); + (*di)->write (port_buffer, samples); + // TODO optimze, get_read_vector() + for (uint32_t i = 0; i < samples; ++i) { + Sample spl; + (*di)->read (&spl, 1); + _buffer[i] += spl; } + ++di; } - data = buffer.get(); + data = _buffer.get(); } void diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc index 7301e12b68..df74bda065 100644 --- a/libs/ardour/export_handler.cc +++ b/libs/ardour/export_handler.cc @@ -193,7 +193,6 @@ ExportHandler::start_timespan () handle_duplicate_format_extensions(); bool realtime = current_timespan->realtime (); bool region_export = true; - bool incl_master_bus = false; for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second; ++it) { // Filenames can be shared across timespans FileSpec & spec = it->second; @@ -206,33 +205,6 @@ ExportHandler::start_timespan () default: break; } -#if 1 // hack alert -- align master bus, compensate master latency - - /* there's no easier way to get this information here. - * Ports are configured in the PortExportChannelSelector GUI, - * This ExportHandler has no context of routes. - */ - boost::shared_ptr master_bus = session.master_out (); - if (master_bus) { - const PortSet& ps = master_bus->output ()->ports(); - - const ExportChannelConfiguration::ChannelList& channels = spec.channel_config->get_channels (); - for (ExportChannelConfiguration::ChannelList::const_iterator it = channels.begin(); it != channels.end(); ++it) { - - boost::shared_ptr pep = boost::dynamic_pointer_cast (*it); - if (!pep) { - continue; - } - PortExportChannel::PortSet const& ports = pep->get_ports (); - for (PortExportChannel::PortSet::const_iterator it = ports.begin(); it != ports.end(); ++it) { - boost::shared_ptr ap = (*it).lock(); - if (ps.contains (ap)) { - incl_master_bus = true; - } - } - } - } -#endif graph_builder->add_config (spec, realtime); } @@ -245,7 +217,7 @@ ExportHandler::start_timespan () session.ProcessExport.connect_same_thread (process_connection, boost::bind (&ExportHandler::process, this, _1)); process_position = current_timespan->get_start(); // TODO check if it's a RegionExport.. set flag to skip process_without_events() - session.start_audio_export (process_position, realtime, region_export, incl_master_bus); + session.start_audio_export (process_position, realtime, region_export); } void diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 8bf18fc730..df8ae9fc51 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -224,7 +224,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) diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc index 33ede3cadc..879f34020a 100644 --- a/libs/ardour/session_export.cc +++ b/libs/ardour/session_export.cc @@ -104,7 +104,7 @@ Session::pre_export () /** Called for each range that is being exported */ int -Session::start_audio_export (samplepos_t position, bool realtime, bool region_export, bool comensate_master_latency) +Session::start_audio_export (samplepos_t position, bool realtime, bool region_export) { if (!_exporting) { pre_export (); @@ -127,31 +127,6 @@ Session::start_audio_export (samplepos_t position, bool realtime, bool region_ex _export_preroll = 1; } - /* "worst_track_latency" is the correct value for stem-exports - * see to Route::add_export_point(), - * - * For master-bus export, we also need to add the master's latency. - * (or actually longest-total-session-latency - worst-track-latency) - * to align the export to 00:00:00:00. - * - * We must not use worst_playback_latency because that - * includes external (hardware) latencies and would overcompensate - * during file-export. - * - * (this is all still very [w]hacky. Individual Bus and Track outputs - * are not aligned but one can select them in the PortExportChannelSelector) - */ - _export_latency = worst_track_out_latency (); - - boost::shared_ptr master = master_out (); - if (master && comensate_master_latency) { - _export_latency += master->signal_latency (); - } - - if (region_export) { - _export_latency = 0; - } - /* We're about to call Track::seek, so the butler must have finished everything up otherwise it could be doing do_refill in its thread while we are doing it here. @@ -181,6 +156,12 @@ Session::start_audio_export (samplepos_t position, bool realtime, bool region_ex */ _transport_sample = position; + + if (!region_export) { + _remaining_latency_preroll = worst_latency_preroll (); + } else { + _remaining_latency_preroll = 0; + } export_status->stop = false; /* get transport ready. note how this is calling butler functions @@ -277,18 +258,21 @@ Session::process_export_fw (pframes_t nframes) return; } - if (_export_latency > 0) { - samplepos_t remain = std::min ((samplepos_t)nframes, _export_latency); + if (_remaining_latency_preroll > 0) { + samplepos_t remain = std::min ((samplepos_t)nframes, _remaining_latency_preroll); if (need_buffers) { _engine.main_thread()->get_buffers (); } + process_without_events (remain); + if (need_buffers) { _engine.main_thread()->drop_buffers (); } - _export_latency -= remain; + _remaining_latency_preroll -= remain; + _transport_sample -= remain; nframes -= remain; if (nframes == 0) { -- 2.30.2