Properly aligned export (Stem + Session)
Delay ports being exported by their playback latency.
This commit is contained in:
@@ -22,11 +22,13 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user