diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index fb2b18f177..1132967ecf 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -470,6 +470,13 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou pframes_t nframes, int declick, bool gain_automation_ok); + virtual void bounce_process (BufferSet& bufs, + framepos_t start_frame, framecnt_t nframes, + boost::shared_ptr endpoint, bool include_endpoint, + bool for_export); + + framecnt_t bounce_get_latency (boost::shared_ptr endpoint, bool include_endpoint, bool for_export) const; + boost::shared_ptr _input; boost::shared_ptr _output; diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 94201882e7..61b6e56bb6 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -417,38 +417,7 @@ AudioTrack::export_stuff (BufferSet& buffers, framepos_t start, framecnt_t nfram } } - // If no processing is required, there's no need to go any further. - - if (!endpoint && !include_endpoint) { - return 0; - } - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - - if (!include_endpoint && (*i) == endpoint) { - break; - } - - /* if we're not exporting, stop processing if we come across a routing processor. - */ - - if (!for_export && (*i)->does_routing()) { - break; - } - - /* even for export, don't run any processor that does routing. - - oh, and don't bother with the peak meter either. - */ - - if (!(*i)->does_routing() && !boost::dynamic_pointer_cast(*i)) { - (*i)->run (buffers, start, start+nframes, nframes, true); - } - - if ((*i) == endpoint) { - break; - } - } + bounce_process (buffers, start, nframes, endpoint, include_endpoint, for_export); return 0; } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 4d7bb5802d..2727f511cb 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -536,6 +536,68 @@ Route::process_output_buffers (BufferSet& bufs, } } +void +Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, + boost::shared_ptr endpoint, bool include_endpoint, bool for_export) +{ + /* If no processing is required, there's no need to go any further. */ + if (!endpoint && !include_endpoint) { + return; + } + + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + + if (!include_endpoint && (*i) == endpoint) { + break; + } + + /* if we're not exporting, stop processing if we come across a routing processor. */ + + if (!for_export && (*i)->does_routing()) { + break; + } + + /* don't run any processors that does routing. + * oh, and don't bother with the peak meter either. + */ + if (!(*i)->does_routing() && !boost::dynamic_pointer_cast(*i)) { + (*i)->run (buffers, start, start+nframes, nframes, true); + } + + //buffers.set_count ((*i)->output_streams()); + + if ((*i) == endpoint) { + break; + } + } +} + +framecnt_t +Route::bounce_get_latency (boost::shared_ptr endpoint, bool include_endpoint, bool for_export) const +{ + framecnt_t latency = 0; + if (!endpoint && !include_endpoint) { + return latency; + } + + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + + if (!include_endpoint && (*i) == endpoint) { + break; + } + if (!for_export && (*i)->does_routing()) { + break; + } + if (!(*i)->does_routing() && !boost::dynamic_pointer_cast(*i)) { + latency += (*i)->signal_latency (); + } + if ((*i) == endpoint) { + break; + } + } + return latency; +} + ChanCount Route::n_process_buffers () { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 85059ccaea..bd33201199 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -4133,6 +4133,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, framepos_t position; framecnt_t this_chunk; framepos_t to_do; + framepos_t latency_skip; BufferSet buffers; SessionDirectory sdir(get_best_session_directory_for_new_source ()); const string sound_dir = sdir.sound_path(); @@ -4208,10 +4209,12 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, position = start; to_do = len; + latency_skip = track.bounce_get_latency (endpoint, include_endpoint, for_export); /* create a set of reasonably-sized buffers */ - buffers.ensure_buffers (DataType::AUDIO, max_proc.n_audio(), chunk_size); - buffers.ensure_buffers (DataType::MIDI, max_proc.n_midi(), chunk_size); + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + buffers.ensure_buffers(*t, max_proc.get(*t), chunk_size); + } buffers.set_count (max_proc); for (vector >::iterator src = srcs.begin(); src != srcs.end(); ++src) { @@ -4228,6 +4231,40 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, goto out; } + start += this_chunk; + to_do -= this_chunk; + itt.progress = (float) (1.0 - ((double) to_do / len)); + + if (latency_skip >= chunk_size) { + latency_skip -= chunk_size; + continue; + } + + const framecnt_t current_chunk = this_chunk - latency_skip; + + uint32_t n = 0; + for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { + boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + + if (afs) { + if (afs->write (buffers.get_audio(n).data(latency_skip), current_chunk) != current_chunk) { + goto out; + } + } + } + latency_skip = 0; + } + + /* post-roll, pick up delayed processor output */ + latency_skip = track.bounce_get_latency (endpoint, include_endpoint, for_export); + + while (latency_skip && !itt.cancel) { + this_chunk = min (latency_skip, chunk_size); + latency_skip -= this_chunk; + + buffers.silence (this_chunk, 0); + track.bounce_process (buffers, start, this_chunk, endpoint, include_endpoint, for_export); + uint32_t n = 0; for (vector >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); @@ -4238,12 +4275,6 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, } } } - - start += this_chunk; - to_do -= this_chunk; - - itt.progress = (float) (1.0 - ((double) to_do / len)); - } if (!itt.cancel) {