4 * Copyright (C) 2009 Adam Williams <broadcast at earthling dot net>
6 * This program 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.
11 * This program 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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bcsignals.h"
26 #include "transportque.inc"
33 // Resampling from Lame
37 old.allocate(BLACKSIZE, 0);
38 double *old_data = old.get_data();
39 memset(old_data, 0, BLACKSIZE*sizeof(*old_data));
44 output_allocation = 0;
45 input_size = RESAMPLE_CHUNKSIZE;
47 input = new Samples(input_size + 1);
50 direction = PLAY_FORWARD;
56 delete [] output_temp;
60 int Resample::read_samples(Samples *buffer, int64_t start, int64_t len, int direction)
65 void Resample::reset()
73 void Resample::blackman(double fcn, int filter_l)
75 double wcn = M_PI * fcn;
76 double ctr = filter_l / 2.0;
77 double cir = 2*M_PI/filter_l;
78 for( int j=0; j<=2*BPC; ++j ) {
79 double offset = (j-BPC) / (2.*BPC); // -0.5 ... 0.5
80 for( int i=0; i<=filter_l; ++i ) {
81 double x = i - offset;
82 bclamp(x, 0,filter_l);
83 double v, dx = x - ctr;
84 if( fabs(dx) >= 1e-9 ) {
85 double curve = sin(wcn * dx) / (M_PI * dx);
87 double blkmn = 0.42 - 0.5 * cos(th) + 0.08 * cos(2*th);
98 int Resample::get_output_size()
103 // void Resample::read_output(double *output, int size)
105 // memcpy(output, output_temp, size * sizeof(double));
106 // // Shift leftover forward
107 // for( int i = size; i < output_size; i++ )
108 // output_temp[i - size] = output_temp[i];
109 // output_size -= size;
113 // starts odd = (even-1)
114 #define FILTER_N (BLACKSIZE-6)
115 #define FILTER_L (FILTER_N - (~FILTER_N & 1));
117 void Resample::resample_chunk(Samples *input_buffer, int64_t in_len,
118 int in_rate, int out_rate)
120 //printf("Resample::resample_chunk %d in_len=%jd input_size=%d\n",
121 // __LINE__, in_len, input_size);
122 double *input = input_buffer->get_data();
123 double resample_ratio = (double)in_rate / out_rate;
124 double fcn = .90 / resample_ratio;
125 if( fcn > .90 ) fcn = .90;
126 int filter_l = FILTER_L;
127 // if resample_ratio = int, filter_l should include right edge
128 if( fabs(resample_ratio - floor(.5 + resample_ratio)) < .0001 )
130 // Blackman filter initialization must be called whenever there is a
131 // sampling ratio change
132 if( !resample_init || last_ratio != resample_ratio ) {
134 last_ratio = resample_ratio;
135 blackman(fcn, filter_l);
139 double filter_l2 = filter_l/2.;
141 int64_t end_time = itime + in_len + l2;
142 int64_t out_time = end_time / resample_ratio + 1;
143 int64_t demand = out_time - output_position;
144 if( demand >= output_allocation ) {
145 // demand 2**n buffer
146 int64_t new_allocation = output_allocation ? output_allocation : 16384;
147 while( new_allocation < demand ) new_allocation <<= 1;
148 double *new_output = new double[new_allocation];
150 memmove(new_output, output_temp, output_allocation*sizeof(double));
151 delete [] output_temp;
153 output_temp = new_output;
154 output_allocation = new_allocation;
158 double *old_data = old.get_data();
160 int otime = 0, last_used = 0;
161 while( output_size < output_allocation ) {
162 double in_pos = otime * resample_ratio;
163 // window centered at ctr_pos
164 ctr_pos = in_pos + itime;
165 double pos = ctr_pos - filter_l2;
166 int ipos = floor(pos);
167 last_used = ipos + filter_l;
168 if( last_used >= in_len ) break;
169 double fraction = pos - ipos;
170 int phase = floor(fraction * 2*BPC + .5);
171 int i = ipos, j = filter_l; // fir filter
172 double xvalue = 0, *filt = blackfilt[phase];
173 for( ; j>=0 && i<0; ++i,--j ) xvalue += *filt++ * old_data[BLACKSIZE + i];
174 for( ; j>=0; ++i,--j ) xvalue += *filt++ * input[i];
175 output_temp[output_size++] = xvalue;
178 // move ctr_pos backward by in_len as new itime offset
179 // the next read will be in the history, itime is negative
180 itime = ctr_pos - in_len;
181 memmove(old_data, input+in_len-BLACKSIZE, BLACKSIZE*sizeof(double));
184 void Resample::reverse_buffer(double *buffer, int64_t len)
187 double *bp = ap + len;
195 int Resample::set_input_position(int64_t in_pos, int in_dir)
198 input_position = in_pos;
200 // update old, just before/after input going fwd/rev;
201 int dir = direction == PLAY_FORWARD ? -1 : 1;
202 in_pos += dir * BLACKSIZE;
203 return read_samples(&old, in_pos, BLACKSIZE, in_dir);
206 int Resample::resample(Samples *output, int64_t out_len,
207 int in_rate, int out_rate, int64_t out_position, int direction)
210 if( this->output_position != out_position ||
211 this->direction != direction ) {
212 //printf("missed %jd!=%jd\n", output_position, out_position);
213 // starting point in input rate.
214 int64_t in_pos = out_position * in_rate / out_rate;
215 set_input_position(in_pos, direction);
218 //printf("matched %jd==%jd\n", output_position, out_position);
220 int dir = direction == PLAY_REVERSE ? -1 : 1;
221 int remaining_len = out_len;
222 double *output_ptr = output->get_data();
223 while( remaining_len > 0 && !result ) {
225 int len = bmin(output_size, remaining_len);
226 memmove(output_ptr, output_temp, len*sizeof(double));
227 memmove(output_temp, output_temp+len, (output_size-=len)*sizeof(double));
228 output_ptr += len; remaining_len -= len;
230 if( remaining_len > 0 ) {
231 result = read_samples(input, input_position, input_size, direction);
233 resample_chunk(input, input_size, in_rate, out_rate);
234 input_position += dir * input_size;
238 this->output_position = out_position + dir * out_len;