2 Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 /** @file src/audio_merger.cc
22 * @brief AudioMerger class.
26 #include "audio_merger.h"
27 #include "dcpomatic_time.h"
34 using std::make_shared;
38 using std::shared_ptr;
39 using boost::optional;
40 using namespace dcpomatic;
43 AudioMerger::AudioMerger (int frame_rate)
44 : _frame_rate (frame_rate)
51 AudioMerger::frames (DCPTime t) const
53 return t.frames_floor (_frame_rate);
57 /** Pull audio up to a given time; after this call, no more data can be pushed
58 * before the specified time.
59 * @param time Time to pull up to.
60 * @return Blocks of merged audio up to `time'.
62 list<pair<shared_ptr<AudioBuffers>, DCPTime>>
63 AudioMerger::pull (DCPTime time)
65 list<pair<shared_ptr<AudioBuffers>, DCPTime>> out;
67 list<Buffer> new_buffers;
69 _buffers.sort ([](Buffer const& a, Buffer const& b) {
70 return a.time < b.time;
73 for (auto i: _buffers) {
74 if (i.period().to <= time) {
75 /* Completely within the pull period */
76 DCPOMATIC_ASSERT (i.audio->frames() > 0);
77 out.push_back (make_pair (i.audio, i.time));
78 } else if (i.time < time) {
79 /* Overlaps the end of the pull period */
80 int32_t const overlap = frames(DCPTime(time - i.time));
81 /* Though time > i.time, overlap could be 0 if the difference in time is less than one frame */
83 auto audio = make_shared<AudioBuffers>(i.audio, overlap, 0);
84 out.push_back (make_pair(audio, i.time));
85 i.audio->trim_start (overlap);
86 i.time += DCPTime::from_frames(overlap, _frame_rate);
87 DCPOMATIC_ASSERT (i.audio->frames() > 0);
88 new_buffers.push_back (i);
92 DCPOMATIC_ASSERT (i.audio->frames() > 0);
93 new_buffers.push_back (i);
97 _buffers = new_buffers;
99 for (auto const& i: out) {
100 DCPOMATIC_ASSERT (i.first->frames() > 0);
107 /** Push some data into the merger at a given time */
109 AudioMerger::push (std::shared_ptr<const AudioBuffers> audio, DCPTime time)
111 DCPOMATIC_ASSERT (audio->frames() > 0);
113 DCPTimePeriod period (time, time + DCPTime::from_frames (audio->frames(), _frame_rate));
115 /* Mix any overlapping parts of this new block with existing ones */
116 for (auto i: _buffers) {
117 auto overlap = i.period().overlap(period);
119 int32_t const offset = frames(DCPTime(overlap->from - i.time));
120 int32_t const frames_to_mix = frames(overlap->duration());
122 i.audio->accumulate_frames(audio.get(), frames_to_mix, 0, offset);
124 i.audio->accumulate_frames(audio.get(), frames_to_mix, offset, 0);
129 list<DCPTimePeriod> periods;
130 for (auto i: _buffers) {
131 periods.push_back (i.period());
134 /* Add the non-overlapping parts */
135 for (auto i: subtract(period, periods)) {
136 auto before = _buffers.end();
137 auto after = _buffers.end();
138 for (auto j = _buffers.begin(); j != _buffers.end(); ++j) {
139 if (j->period().to == i.from) {
142 if (j->period().from == i.to) {
147 /* Get the part of audio that we want to use */
148 auto part = make_shared<AudioBuffers>(audio, frames(i.to) - frames(i.from), frames(DCPTime(i.from - time)));
150 if (before == _buffers.end() && after == _buffers.end()) {
151 if (part->frames() > 0) {
153 _buffers.push_back (Buffer (part, time, _frame_rate));
155 } else if (before != _buffers.end() && after == _buffers.end()) {
156 /* We have an existing buffer before this one; append new data to it */
157 before->audio->append (part);
158 } else if (before ==_buffers.end() && after != _buffers.end()) {
159 /* We have an existing buffer after this one; append it to the new data and replace */
160 part->append (after->audio);
164 /* We have existing buffers both before and after; coalesce them all */
165 before->audio->append (part);
166 before->audio->append (after->audio);
167 _buffers.erase (after);
174 AudioMerger::clear ()