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