diff options
| author | Carl Hetherington <cth@carlh.net> | 2017-02-26 01:40:30 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2017-04-19 23:04:32 +0100 |
| commit | 58dce923b9d438a27ce1cd7e3125370f74d46e3a (patch) | |
| tree | 5a332a967aa3fb9bcb923f62b4832d8bb4d2bb17 /src/lib/audio_merger.cc | |
| parent | e7e06b4c9a6bf4459ff27a30cf347121c0e40e07 (diff) | |
Fix merging of audio in various circumstances.
Diffstat (limited to 'src/lib/audio_merger.cc')
| -rw-r--r-- | src/lib/audio_merger.cc | 118 |
1 files changed, 88 insertions, 30 deletions
diff --git a/src/lib/audio_merger.cc b/src/lib/audio_merger.cc index 49cdea6a3..5e0589bb1 100644 --- a/src/lib/audio_merger.cc +++ b/src/lib/audio_merger.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,12 +24,14 @@ using std::pair; using std::min; using std::max; +using std::list; +using std::cout; using std::make_pair; using boost::shared_ptr; +using boost::optional; -AudioMerger::AudioMerger (int channels, int frame_rate) - : _buffers (new AudioBuffers (channels, 0)) - , _last_pull (0) +AudioMerger::AudioMerger (int frame_rate) + : _last_pull (0) , _frame_rate (frame_rate) { @@ -38,31 +40,36 @@ AudioMerger::AudioMerger (int channels, int frame_rate) /** Pull audio up to a given time; after this call, no more data can be pushed * before the specified time. */ -pair<shared_ptr<AudioBuffers>, DCPTime> +list<pair<shared_ptr<AudioBuffers>, DCPTime> > AudioMerger::pull (DCPTime time) { - /* Number of frames to return */ - Frame const to_return = time.frames_floor (_frame_rate) - _last_pull.frames_floor (_frame_rate); - shared_ptr<AudioBuffers> out (new AudioBuffers (_buffers->channels(), to_return)); - - /* And this is how many we will get from our buffer */ - Frame const to_return_from_buffers = min (to_return, Frame (_buffers->frames())); - - /* Copy the data that we have to the back end of the return buffer */ - out->copy_from (_buffers.get(), to_return_from_buffers, 0, to_return - to_return_from_buffers); - /* Silence any gap at the start */ - out->make_silent (0, to_return - to_return_from_buffers); - - DCPTime out_time = _last_pull; - _last_pull = time; - - /* And remove the data we're returning from our buffers */ - if (_buffers->frames() > to_return_from_buffers) { - _buffers->move (to_return_from_buffers, 0, _buffers->frames() - to_return_from_buffers); + list<pair<shared_ptr<AudioBuffers>, DCPTime> > out; + + DCPTimePeriod period (_last_pull, time); + _buffers.sort (AudioMerger::BufferComparator()); + + list<Buffer> new_buffers; + + BOOST_FOREACH (Buffer i, _buffers) { + if (i.period().to < time) { + /* Completely within the pull period */ + out.push_back (make_pair (i.audio, i.time)); + } else if (i.time < time) { + /* Overlaps the end of the pull period */ + shared_ptr<AudioBuffers> audio (new AudioBuffers (i.audio->channels(), DCPTime(time - i.time).frames_floor(_frame_rate))); + audio->copy_from (i.audio.get(), audio->frames(), 0, 0); + out.push_back (make_pair (audio, i.time)); + i.audio->trim_start (audio->frames ()); + i.time += DCPTime::from_frames(audio->frames(), _frame_rate); + new_buffers.push_back (i); + } else { + /* Not involved */ + new_buffers.push_back (i); + } } - _buffers->set_frames (_buffers->frames() - to_return_from_buffers); - return make_pair (out, out_time); + _buffers = new_buffers; + return out; } void @@ -70,9 +77,60 @@ AudioMerger::push (boost::shared_ptr<const AudioBuffers> audio, DCPTime time) { DCPOMATIC_ASSERT (time >= _last_pull); - Frame const frame = time.frames_floor (_frame_rate); - Frame after = max (Frame (_buffers->frames()), frame + audio->frames() - _last_pull.frames_floor (_frame_rate)); - _buffers->ensure_size (after); - _buffers->accumulate_frames (audio.get(), 0, frame - _last_pull.frames_floor (_frame_rate), audio->frames ()); - _buffers->set_frames (after); + DCPTimePeriod period (time, time + DCPTime::from_frames (audio->frames(), _frame_rate)); + + /* Mix any parts of this new block with existing ones */ + BOOST_FOREACH (Buffer i, _buffers) { + optional<DCPTimePeriod> overlap = i.period().overlap (period); + if (overlap) { + int32_t const offset = DCPTime(overlap->from - i.time).frames_floor(_frame_rate); + int32_t const frames = overlap->duration().frames_floor(_frame_rate); + if (i.time < time) { + i.audio->accumulate_frames(audio.get(), frames, 0, offset); + } else { + i.audio->accumulate_frames(audio.get(), frames, offset, 0); + } + } + } + + list<DCPTimePeriod> periods; + BOOST_FOREACH (Buffer i, _buffers) { + periods.push_back (i.period ()); + } + + /* Add the non-overlapping parts */ + BOOST_FOREACH (DCPTimePeriod i, subtract (period, periods)) { + list<Buffer>::iterator before = _buffers.end(); + list<Buffer>::iterator after = _buffers.end(); + for (list<Buffer>::iterator j = _buffers.begin(); j != _buffers.end(); ++j) { + if (j->period().to == i.from) { + before = j; + } + if (j->period().from == i.to) { + after = j; + } + } + + /* Get the part of audio that we want to use */ + shared_ptr<AudioBuffers> part (new AudioBuffers (audio->channels(), i.to.frames_floor(_frame_rate) - i.from.frames_floor(_frame_rate))); + part->copy_from (audio.get(), part->frames(), DCPTime(i.from - time).frames_floor(_frame_rate), 0); + + if (before == _buffers.end() && after == _buffers.end()) { + /* New buffer */ + _buffers.push_back (Buffer (part, time, _frame_rate)); + } else if (before != _buffers.end() && after == _buffers.end()) { + /* We have an existing buffer before this one; append new data to it */ + before->audio->append (part); + } else if (before ==_buffers.end() && after != _buffers.end()) { + /* We have an existing buffer after this one; append it to the new data and replace */ + part->append (after->audio); + after->audio = part; + after->time = time; + } else { + /* We have existing buffers both before and after; coalesce them all */ + before->audio->append (part); + before->audio->append (after->audio); + _buffers.erase (after); + } + } } |
