Fix the build for older macOS.
[dcpomatic.git] / src / lib / audio_ring_buffers.cc
1 /*
2     Copyright (C) 2016-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "audio_ring_buffers.h"
23 #include "dcpomatic_assert.h"
24 #include "exceptions.h"
25 #include <iostream>
26
27
28 using std::min;
29 using std::cout;
30 using std::make_pair;
31 using std::pair;
32 using std::list;
33 using std::shared_ptr;
34 using boost::optional;
35 using namespace dcpomatic;
36
37
38 AudioRingBuffers::AudioRingBuffers ()
39 {
40
41 }
42
43
44 /** @param frame_rate Frame rate in use; this is only used to check timing consistency of the incoming data */
45 void
46 AudioRingBuffers::put (shared_ptr<const AudioBuffers> data, DCPTime time, int frame_rate)
47 {
48         boost::mutex::scoped_lock lm (_mutex);
49
50         if (!_buffers.empty()) {
51                 DCPOMATIC_ASSERT (_buffers.front().first->channels() == data->channels());
52                 DCPTime const end = (_buffers.back().second + DCPTime::from_frames(_buffers.back().first->frames(), frame_rate));
53                 if (labs(end.get() - time.get()) > 1) {
54                         cout << "bad put " << to_string(_buffers.back().second) << " " << _buffers.back().first->frames() << " " << to_string(time) << "\n";
55                 }
56                 DCPOMATIC_ASSERT (labs(end.get() - time.get()) < 2);
57         }
58
59         _buffers.push_back(make_pair(data, time));
60 }
61
62
63 /** @return time of the returned data; if it's not set this indicates an underrun */
64 optional<DCPTime>
65 AudioRingBuffers::get (float* out, int channels, int frames)
66 {
67         boost::mutex::scoped_lock lm (_mutex);
68
69         optional<DCPTime> time;
70
71         while (frames > 0) {
72                 if (_buffers.empty ()) {
73                         for (int i = 0; i < frames; ++i) {
74                                 for (int j = 0; j < channels; ++j) {
75                                         *out++ = 0;
76                                 }
77                         }
78                         cout << "audio underrun; missing " << frames << "!\n";
79                         return time;
80                 }
81
82                 auto front = _buffers.front ();
83                 if (!time) {
84                         time = front.second + DCPTime::from_frames(_used_in_head, 48000);
85                 }
86
87                 int const to_do = min (frames, front.first->frames() - _used_in_head);
88                 float** p = front.first->data();
89                 int const c = min (front.first->channels(), channels);
90                 for (int i = 0; i < to_do; ++i) {
91                         for (int j = 0; j < c; ++j) {
92                                 *out++ = p[j][i + _used_in_head];
93                         }
94                         for (int j = c; j < channels; ++j) {
95                                 *out++ = 0;
96                         }
97                 }
98                 _used_in_head += to_do;
99                 frames -= to_do;
100
101                 if (_used_in_head == front.first->frames()) {
102                         _buffers.pop_front ();
103                         _used_in_head = 0;
104                 }
105         }
106
107         return time;
108 }
109
110
111 optional<DCPTime>
112 AudioRingBuffers::peek () const
113 {
114         boost::mutex::scoped_lock lm (_mutex);
115         if (_buffers.empty()) {
116                 return {};
117         }
118         return _buffers.front().second;
119 }
120
121
122 void
123 AudioRingBuffers::clear ()
124 {
125         boost::mutex::scoped_lock lm (_mutex);
126         _buffers.clear ();
127         _used_in_head = 0;
128 }
129
130
131 Frame
132 AudioRingBuffers::size () const
133 {
134         boost::mutex::scoped_lock lm (_mutex);
135         Frame s = 0;
136         for (auto const& i: _buffers) {
137                 s += i.first->frames();
138         }
139         return s - _used_in_head;
140 }