Fix typo in log message.
[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                         return time;
79                 }
80
81                 auto front = _buffers.front ();
82                 if (!time) {
83                         time = front.second + DCPTime::from_frames(_used_in_head, 48000);
84                 }
85
86                 int const to_do = min (frames, front.first->frames() - _used_in_head);
87                 float* const* p = front.first->data();
88                 int const c = min (front.first->channels(), channels);
89                 for (int i = 0; i < to_do; ++i) {
90                         for (int j = 0; j < c; ++j) {
91                                 *out++ = p[j][i + _used_in_head];
92                         }
93                         for (int j = c; j < channels; ++j) {
94                                 *out++ = 0;
95                         }
96                 }
97                 _used_in_head += to_do;
98                 frames -= to_do;
99
100                 if (_used_in_head == front.first->frames()) {
101                         _buffers.pop_front ();
102                         _used_in_head = 0;
103                 }
104         }
105
106         return time;
107 }
108
109
110 optional<DCPTime>
111 AudioRingBuffers::peek () const
112 {
113         boost::mutex::scoped_lock lm (_mutex);
114         if (_buffers.empty()) {
115                 return {};
116         }
117         return _buffers.front().second;
118 }
119
120
121 void
122 AudioRingBuffers::clear ()
123 {
124         boost::mutex::scoped_lock lm (_mutex);
125         _buffers.clear ();
126         _used_in_head = 0;
127 }
128
129
130 Frame
131 AudioRingBuffers::size () const
132 {
133         boost::mutex::scoped_lock lm (_mutex);
134         Frame s = 0;
135         for (auto const& i: _buffers) {
136                 s += i.first->frames();
137         }
138         return s - _used_in_head;
139 }