Fix merging of audio in various circumstances.
[dcpomatic.git] / src / lib / audio_merger.cc
1 /*
2     Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
3
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.
8
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.
13
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.
17
18 */
19
20 #include "audio_merger.h"
21 #include "dcpomatic_time.h"
22 #include <iostream>
23
24 using std::pair;
25 using std::min;
26 using std::max;
27 using std::list;
28 using std::cout;
29 using std::make_pair;
30 using boost::shared_ptr;
31 using boost::optional;
32
33 AudioMerger::AudioMerger (int frame_rate)
34         : _last_pull (0)
35         , _frame_rate (frame_rate)
36 {
37
38 }
39
40 /** Pull audio up to a given time; after this call, no more data can be pushed
41  *  before the specified time.
42  */
43 list<pair<shared_ptr<AudioBuffers>, DCPTime> >
44 AudioMerger::pull (DCPTime time)
45 {
46         list<pair<shared_ptr<AudioBuffers>, DCPTime> > out;
47
48         DCPTimePeriod period (_last_pull, time);
49         _buffers.sort (AudioMerger::BufferComparator());
50
51         list<Buffer> new_buffers;
52
53         BOOST_FOREACH (Buffer i, _buffers) {
54                 if (i.period().to < time) {
55                         /* Completely within the pull period */
56                         out.push_back (make_pair (i.audio, i.time));
57                 } else if (i.time < time) {
58                         /* Overlaps the end of the pull period */
59                         shared_ptr<AudioBuffers> audio (new AudioBuffers (i.audio->channels(), DCPTime(time - i.time).frames_floor(_frame_rate)));
60                         audio->copy_from (i.audio.get(), audio->frames(), 0, 0);
61                         out.push_back (make_pair (audio, i.time));
62                         i.audio->trim_start (audio->frames ());
63                         i.time += DCPTime::from_frames(audio->frames(), _frame_rate);
64                         new_buffers.push_back (i);
65                 } else {
66                         /* Not involved */
67                         new_buffers.push_back (i);
68                 }
69         }
70
71         _buffers = new_buffers;
72         return out;
73 }
74
75 void
76 AudioMerger::push (boost::shared_ptr<const AudioBuffers> audio, DCPTime time)
77 {
78         DCPOMATIC_ASSERT (time >= _last_pull);
79
80         DCPTimePeriod period (time, time + DCPTime::from_frames (audio->frames(), _frame_rate));
81
82         /* Mix any parts of this new block with existing ones */
83         BOOST_FOREACH (Buffer i, _buffers) {
84                 optional<DCPTimePeriod> overlap = i.period().overlap (period);
85                 if (overlap) {
86                         int32_t const offset = DCPTime(overlap->from - i.time).frames_floor(_frame_rate);
87                         int32_t const frames = overlap->duration().frames_floor(_frame_rate);
88                         if (i.time < time) {
89                                 i.audio->accumulate_frames(audio.get(), frames, 0, offset);
90                         } else {
91                                 i.audio->accumulate_frames(audio.get(), frames, offset, 0);
92                         }
93                 }
94         }
95
96         list<DCPTimePeriod> periods;
97         BOOST_FOREACH (Buffer i, _buffers) {
98                 periods.push_back (i.period ());
99         }
100
101         /* Add the non-overlapping parts */
102         BOOST_FOREACH (DCPTimePeriod i, subtract (period, periods)) {
103                 list<Buffer>::iterator before = _buffers.end();
104                 list<Buffer>::iterator after = _buffers.end();
105                 for (list<Buffer>::iterator j = _buffers.begin(); j != _buffers.end(); ++j) {
106                         if (j->period().to == i.from) {
107                                 before = j;
108                         }
109                         if (j->period().from == i.to) {
110                                 after = j;
111                         }
112                 }
113
114                 /* Get the part of audio that we want to use */
115                 shared_ptr<AudioBuffers> part (new AudioBuffers (audio->channels(), i.to.frames_floor(_frame_rate) - i.from.frames_floor(_frame_rate)));
116                 part->copy_from (audio.get(), part->frames(), DCPTime(i.from - time).frames_floor(_frame_rate), 0);
117
118                 if (before == _buffers.end() && after == _buffers.end()) {
119                         /* New buffer */
120                         _buffers.push_back (Buffer (part, time, _frame_rate));
121                 } else if (before != _buffers.end() && after == _buffers.end()) {
122                         /* We have an existing buffer before this one; append new data to it */
123                         before->audio->append (part);
124                 } else if (before ==_buffers.end() && after != _buffers.end()) {
125                         /* We have an existing buffer after this one; append it to the new data and replace */
126                         part->append (after->audio);
127                         after->audio = part;
128                         after->time = time;
129                 } else {
130                         /* We have existing buffers both before and after; coalesce them all */
131                         before->audio->append (part);
132                         before->audio->append (after->audio);
133                         _buffers.erase (after);
134                 }
135         }
136 }