Properly aligned export (Stem + Session)
authorRobin Gareus <robin@gareus.org>
Tue, 26 Sep 2017 15:49:24 +0000 (17:49 +0200)
committerRobin Gareus <robin@gareus.org>
Fri, 29 Sep 2017 03:03:48 +0000 (05:03 +0200)
Delay ports being exported by their playback latency.

libs/ardour/ardour/export_channel.h
libs/ardour/ardour/session.h
libs/ardour/export_channel.cc
libs/ardour/export_handler.cc
libs/ardour/session.cc
libs/ardour/session_export.cc

index a94d65fbdcffdb9972319d9363ffe7ba03160543..8f4d8fc27cf3e47dd8bfe459c9af8d247ed125aa 100644 (file)
 #define __ardour_export_channel_h__
 
 #include <set>
+#include <list>
 
 #include <boost/scoped_array.hpp>
 #include <boost/shared_ptr.hpp>
 
 #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<boost::weak_ptr<AudioPort> > 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<Sample> buffer;
-       samplecnt_t buffer_size;
+       samplecnt_t                 _buffer_size;
+       boost::scoped_array<Sample> _buffer;
+       std::list <boost::shared_ptr<PBD::RingBuffer<Sample> > >  _delaylines;
 };
 
 
index e04e99de8873c1f0b71c9e31b50a27e0fa1f0e04..66ab5ce0824d07b1d5f90859b7c52d5f8b003ad2 100644 (file)
@@ -751,7 +751,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        boost::shared_ptr<ExportHandler> get_export_handler ();
        boost::shared_ptr<ExportStatus> 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<int, samplecnt_t> ProcessExport;
        static PBD::Signal2<void,std::string, std::string> 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<ExportHandler> export_handler;
        boost::shared_ptr<ExportStatus>  export_status;
index 9a7e6a9ace757871655f7f1d3d902d51fd10f81d..66fec3a53b4127057070b83d41f4e2e954ec8deb 100644 (file)
 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<AudioPort> p = it->lock ();
+               if (!p) { continue; }
+               samplecnt_t latency = p->private_latency_range (true).max;
+               PBD::RingBuffer<Sample>* rb = new PBD::RingBuffer<Sample> (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<PBD::RingBuffer<Sample> >(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<AudioPort> 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 <boost::shared_ptr<PBD::RingBuffer<Sample> > >::const_iterator di = _delaylines.begin ();
        for (PortSet::const_iterator it = ports.begin(); it != ports.end(); ++it) {
                boost::shared_ptr<AudioPort> 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
index 7301e12b68594bd490e31191ce7a61659a047601..df74bda06532720cd16a7436264f4ff3d887ce02 100644 (file)
@@ -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<Route> 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 <PortExportChannel> pep = boost::dynamic_pointer_cast<PortExportChannel> (*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<AudioPort> 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
index 8bf18fc7307d7098b02f048330590f32ea186758..df8ae9fc51f355969c1b459e394141115fc930d8 100644 (file)
@@ -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)
index 33ede3cadcdad58fe6e7f695a577d0717a48e238..879f34020af63004387fe0314a118004c9e54fc7 100644 (file)
@@ -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<Route> 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) {