Extract audio merging code from Player.
[dcpomatic.git] / src / lib / audio_merger.h
1 /*
2     Copyright (C) 2013 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_buffers.h"
21
22 template <class T, class F>
23 class AudioMerger
24 {
25 public:
26         AudioMerger (int channels, boost::function<F (T)> t_to_f, boost::function<T (F)> f_to_t)
27                 : _buffers (new AudioBuffers (channels, 0))
28                 , _next_emission (0)
29                 , _t_to_f (t_to_f)
30                 , _f_to_t (f_to_t)
31         {}
32
33         void push (boost::shared_ptr<const AudioBuffers> audio, T time)
34         {
35                 if (time > _next_emission) {
36                         /* We can emit some audio from our buffer; this is how many frames
37                            we are going to emit.
38                         */
39                         F const to_emit = _t_to_f (time - _next_emission);
40                         boost::shared_ptr<AudioBuffers> emit (new AudioBuffers (_buffers->channels(), to_emit));
41
42                         /* And this is how many we will get from our buffer */
43                         F const to_emit_from_buffers = min (to_emit, _buffers->frames ());
44
45                         /* Copy the data that we have to the back end of `emit' */
46                         emit->copy_from (_buffers.get(), to_emit_from_buffers, 0, to_emit - to_emit_from_buffers);
47
48                         /* Silence any gap at the start */
49                         emit->make_silent (0, to_emit - to_emit_from_buffers);
50
51                         /* Emit that */
52                         Audio (emit, _next_emission);
53
54                         _next_emission += _f_to_t (to_emit);
55
56                         /* And remove the data we've emitted from our buffers */
57                         if (_buffers->frames() > to_emit_from_buffers) {
58                                 _buffers->move (to_emit_from_buffers, 0, _buffers->frames() - to_emit_from_buffers);
59                         }
60                         _buffers->set_frames (_buffers->frames() - to_emit_from_buffers);
61                 }
62
63                 /* Now accumulate the new audio into our buffers */
64                 F frame = _t_to_f (time);
65                 F after = max (_buffers->frames(), frame + audio->frames() - _t_to_f (_next_emission));
66                 _buffers->ensure_size (after);
67                 _buffers->accumulate_frames (audio.get(), 0, frame - _t_to_f (_next_emission), audio->frames ());
68                 _buffers->set_frames (after);
69         }
70
71         F min (F a, int b)
72         {
73                 if (a < b) {
74                         return a;
75                 }
76
77                 return b;
78         }
79
80         F max (int a, F b)
81         {
82                 if (a > b) {
83                         return a;
84                 }
85
86                 return b;
87         }
88                 
89         void flush ()
90         {
91                 if (_buffers->frames() > 0) {
92                         Audio (_buffers, _next_emission);
93                 }
94         }
95         
96         boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, T)> Audio;
97
98 private:
99         boost::shared_ptr<AudioBuffers> _buffers;
100         T _next_emission;
101         boost::function<F (T)> _t_to_f;
102         boost::function<T (F)> _f_to_t;
103 };