2 Copyright (C) 2012 Paul Davis
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.
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.
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.
21 #include "audiographer/general/sr_converter.h"
23 #include "audiographer/exception.h"
24 #include "audiographer/type_utils.h"
27 #include <boost/format.hpp>
29 namespace AudioGrapher
34 SampleRateConverter::SampleRateConverter (uint32_t channels)
40 , max_leftover_frames (0)
45 add_supported_flag (ProcessContext<>::EndOfInput);
49 SampleRateConverter::init (framecnt_t in_rate, framecnt_t out_rate, int quality)
53 if (in_rate == out_rate) {
54 src_data.src_ratio = 1;
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)));
67 src_data.src_ratio = (double) out_rate / (double) in_rate;
70 SampleRateConverter::~SampleRateConverter ()
76 SampleRateConverter::allocate_buffers (framecnt_t max_frames)
78 if (!active) { return max_frames; }
80 framecnt_t max_frames_out = (framecnt_t) ceil (max_frames * src_data.src_ratio);
81 if (data_out_size < max_frames_out) {
84 data_out = new float[max_frames_out];
85 src_data.data_out = data_out;
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");
93 max_frames_in = max_frames;
94 data_out_size = max_frames_out;
97 return max_frames_out;
101 SampleRateConverter::process (ProcessContext<float> const & c)
103 check_flags (*this, c);
110 framecnt_t frames = c.frames();
111 float * in = const_cast<float *> (c.data()); // TODO check if this is safe!
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));
120 bool first_time = true;
123 src_data.output_frames = data_out_size / channels;
124 src_data.data_out = data_out;
126 if (leftover_frames > 0) {
128 /* input data will be in leftover_data rather than data_in */
130 src_data.data_in = leftover_data;
134 /* first time, append new data from data_in into the leftover_data buffer */
136 TypeUtils<float>::copy (in, &leftover_data [leftover_frames * channels], frames);
137 src_data.input_frames = frames / channels + leftover_frames;
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
145 src_data.input_frames = leftover_frames;
149 src_data.data_in = in;
150 src_data.input_frames = frames / channels;
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;
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)));
169 leftover_frames = src_data.input_frames - src_data.input_frames_used;
171 if (leftover_frames > 0) {
172 if (throw_level (ThrowProcess) && leftover_frames > max_leftover_frames) {
173 throw Exception(*this, "leftover frames overflowed");
175 TypeUtils<float>::move (&src_data.data_in[src_data.input_frames_used * channels],
176 leftover_data, leftover_frames * channels);
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);
185 if (debug_level (DebugProcess)) {
187 "src_data.output_frames_gen: " << src_data.output_frames_gen <<
188 ", leftover_frames: " << leftover_frames << std::endl;
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")
197 } while (leftover_frames > frames);
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);
205 void SampleRateConverter::set_end_of_input (ProcessContext<float> const & c)
207 src_data.end_of_input = true;
210 ProcessContext<float> const dummy (c, &f, 0, channels);
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...
215 dummy.remove_flag (ProcessContext<float>::EndOfInput);
217 dummy.set_flag (ProcessContext<float>::EndOfInput);
222 void SampleRateConverter::reset ()
226 src_data.end_of_input = false;
229 src_delete (src_state);
233 max_leftover_frames = 0;
235 free (leftover_data);