NOOP, remove trailing tabs/whitespace.
[ardour.git] / libs / audiographer / src / general / sr_converter.cc
1 /*
2     Copyright (C) 2012 Paul Davis
3     Author: Sakari Bergen
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include "audiographer/general/sr_converter.h"
22
23 #include "audiographer/exception.h"
24 #include "audiographer/type_utils.h"
25
26 #include <cmath>
27 #include <boost/format.hpp>
28
29 namespace AudioGrapher
30 {
31 using boost::format;
32 using boost::str;
33
34 SampleRateConverter::SampleRateConverter (uint32_t channels)
35   : active (false)
36   , channels (channels)
37   , max_frames_in(0)
38   , leftover_data (0)
39   , leftover_frames (0)
40   , max_leftover_frames (0)
41   , data_out (0)
42   , data_out_size (0)
43   , src_state (0)
44 {
45         add_supported_flag (ProcessContext<>::EndOfInput);
46 }
47
48 void
49 SampleRateConverter::init (framecnt_t in_rate, framecnt_t out_rate, int quality)
50 {
51         reset();
52
53         if (in_rate == out_rate) {
54                 src_data.src_ratio = 1;
55                 return;
56         }
57
58         active = true;
59         int err;
60         src_state = src_new (quality, channels, &err);
61         if (throw_level (ThrowObject) && !src_state) {
62                 throw Exception (*this, str (format
63                         ("Cannot initialize sample rate converter: %1%")
64                         % src_strerror (err)));
65         }
66
67         src_data.src_ratio = (double) out_rate / (double) in_rate;
68 }
69
70 SampleRateConverter::~SampleRateConverter ()
71 {
72         reset();
73 }
74
75 framecnt_t
76 SampleRateConverter::allocate_buffers (framecnt_t max_frames)
77 {
78         if (!active) { return max_frames; }
79
80         framecnt_t max_frames_out = (framecnt_t) ceil (max_frames * src_data.src_ratio);
81         if (data_out_size < max_frames_out) {
82
83                 delete[] data_out;
84                 data_out = new float[max_frames_out];
85                 src_data.data_out = data_out;
86
87                 max_leftover_frames = 4 * max_frames;
88                 leftover_data = (float *) realloc (leftover_data, max_leftover_frames * sizeof (float));
89                 if (throw_level (ThrowObject) && !leftover_data) {
90                         throw Exception (*this, "A memory allocation error occured");
91                 }
92
93                 max_frames_in = max_frames;
94                 data_out_size = max_frames_out;
95         }
96
97         return max_frames_out;
98 }
99
100 void
101 SampleRateConverter::process (ProcessContext<float> const & c)
102 {
103         check_flags (*this, c);
104
105         if (!active) {
106                 output (c);
107                 return;
108         }
109
110         framecnt_t frames = c.frames();
111         float * in = const_cast<float *> (c.data()); // TODO check if this is safe!
112
113         if (throw_level (ThrowProcess) && frames > max_frames_in) {
114                 throw Exception (*this, str (format (
115                         "process() called with too many frames, %1% instead of %2%")
116                         % frames % max_frames_in));
117         }
118
119         int err;
120         bool first_time = true;
121
122         do {
123                 src_data.output_frames = data_out_size / channels;
124                 src_data.data_out = data_out;
125
126                 if (leftover_frames > 0) {
127
128                         /* input data will be in leftover_data rather than data_in */
129
130                         src_data.data_in = leftover_data;
131
132                         if (first_time) {
133
134                                 /* first time, append new data from data_in into the leftover_data buffer */
135
136                                 TypeUtils<float>::copy (in, &leftover_data [leftover_frames * channels], frames);
137                                 src_data.input_frames = frames / channels + leftover_frames;
138                         } else {
139
140                                 /* otherwise, just use whatever is still left in leftover_data; the contents
141                                         were adjusted using memmove() right after the last SRC call (see
142                                         below)
143                                 */
144
145                                 src_data.input_frames = leftover_frames;
146                         }
147
148                 } else {
149                         src_data.data_in = in;
150                         src_data.input_frames = frames / channels;
151                 }
152
153                 first_time = false;
154
155                 if (debug_level (DebugVerbose)) {
156                         debug_stream() << "data_in: " << src_data.data_in <<
157                                 ", input_frames: " << src_data.input_frames <<
158                                 ", data_out: " << src_data.data_out <<
159                                 ", output_frames: " << src_data.output_frames << std::endl;
160                 }
161
162                 err = src_process (src_state, &src_data);
163                 if (throw_level (ThrowProcess) && err) {
164                         throw Exception (*this, str (format
165                         ("An error occured during sample rate conversion: %1%")
166                         % src_strerror (err)));
167                 }
168
169                 leftover_frames = src_data.input_frames - src_data.input_frames_used;
170
171                 if (leftover_frames > 0) {
172                         if (throw_level (ThrowProcess) && leftover_frames > max_leftover_frames) {
173                                 throw Exception(*this, "leftover frames overflowed");
174                         }
175                         TypeUtils<float>::move (&src_data.data_in[src_data.input_frames_used * channels],
176                                                 leftover_data, leftover_frames * channels);
177                 }
178
179                 ProcessContext<float> c_out (c, data_out, src_data.output_frames_gen * channels);
180                 if (!src_data.end_of_input || leftover_frames) {
181                         c_out.remove_flag (ProcessContext<float>::EndOfInput);
182                 }
183                 output (c_out);
184
185                 if (debug_level (DebugProcess)) {
186                         debug_stream() <<
187                                 "src_data.output_frames_gen: " << src_data.output_frames_gen <<
188                                 ", leftover_frames: " << leftover_frames << std::endl;
189                 }
190
191                 if (throw_level (ThrowProcess) && src_data.output_frames_gen == 0 && leftover_frames) {
192                         throw Exception (*this, boost::str (boost::format
193                                 ("No output frames genereated with %1% leftover frames")
194                                 % leftover_frames));
195                 }
196
197         } while (leftover_frames > frames);
198
199         // src_data.end_of_input has to be checked to prevent infinite recursion
200         if (!src_data.end_of_input && c.has_flag(ProcessContext<float>::EndOfInput)) {
201                 set_end_of_input (c);
202         }
203 }
204
205 void SampleRateConverter::set_end_of_input (ProcessContext<float> const & c)
206 {
207         src_data.end_of_input = true;
208
209         float f;
210         ProcessContext<float> const dummy (c, &f, 0, channels);
211
212         /* No idea why this has to be done twice for all data to be written,
213          * but that just seems to be the way it is...
214          */
215         dummy.remove_flag (ProcessContext<float>::EndOfInput);
216         process (dummy);
217         dummy.set_flag (ProcessContext<float>::EndOfInput);
218         process (dummy);
219 }
220
221
222 void SampleRateConverter::reset ()
223 {
224         active = false;
225         max_frames_in = 0;
226         src_data.end_of_input = false;
227
228         if (src_state) {
229                 src_delete (src_state);
230         }
231
232         leftover_frames = 0;
233         max_leftover_frames = 0;
234         if (leftover_data) {
235                 free (leftover_data);
236         }
237
238         data_out_size = 0;
239         delete [] data_out;
240         data_out = 0;
241 }
242
243 } // namespace