Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / resample.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2009 Adam Williams <broadcast at earthling dot net>
5  * 
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.
10  * 
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.
15  * 
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
19  * 
20  */
21
22 #include "bcsignals.h"
23 #include "clip.h"
24 #include "resample.h"
25 #include "samples.h"
26 #include "transportque.inc"
27
28 #include <math.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 // Resampling from Lame
34
35 Resample::Resample()
36 {
37         old = new double[BLACKSIZE];
38         resample_init = 0;
39         last_ratio = 0;
40         output_temp = 0;
41         output_size = 0;
42         output_allocation = 0;
43         input_size = RESAMPLE_CHUNKSIZE;
44         input_position = 0;
45         input = new Samples(input_size + 1);
46         output_position = 0;
47         itime = 0;
48         direction = PLAY_FORWARD;
49 }
50
51
52 Resample::~Resample()
53 {
54         delete [] old;
55         delete [] output_temp;
56         delete input;
57 }
58
59 int Resample::read_samples(Samples *buffer, int64_t start, int64_t len)
60 {
61         return 0;
62 }
63
64 int Resample::get_direction()
65 {
66         return direction;
67 }
68
69
70 void Resample::reset()
71 {
72         resample_init = 0;
73         output_size = 0;
74         output_position = 0;
75         input_position = 0;
76 }
77
78 double Resample::blackman(int i, double offset, double fcn, int l)
79 {
80   /* This algorithm from:
81 SIGNAL PROCESSING ALGORITHMS IN FORTRAN AND C
82 S.D. Stearns and R.A. David, Prentice-Hall, 1992
83   */
84
85         double bkwn;
86         double wcn = (M_PI * fcn);
87         double dly = l / 2.0;
88         double x = i-offset;
89         if(x < 0) x = 0;
90         else
91         if(x > l) x = l;
92
93         bkwn = 0.42 - 0.5 * cos((x * 2) * M_PI /l) + 0.08 * cos((x * 4) * M_PI /l);
94         if(fabs(x - dly) < 1e-9) 
95                 return wcn / M_PI;
96     else 
97         return (sin((wcn * (x - dly))) / (M_PI * (x - dly)) * bkwn);
98 }
99
100
101 int Resample::get_output_size()
102 {
103         return output_size;
104 }
105
106 // void Resample::read_output(double *output, int size)
107 // {
108 //      memcpy(output, output_temp, size * sizeof(double));
109 // // Shift leftover forward
110 //      for(int i = size; i < output_size; i++)
111 //              output_temp[i - size] = output_temp[i];
112 //      output_size -= size;
113 // }
114
115
116
117 void Resample::resample_chunk(Samples *input_buffer,
118         int64_t in_len,
119         int in_rate,
120         int out_rate)
121 {
122         double resample_ratio = (double)in_rate / out_rate;
123         int filter_l;
124         double fcn, intratio;
125         double offset, xvalue;
126         int num_used;
127         int i, j, k;
128         double *input = input_buffer->get_data();
129 //printf("Resample::resample_chunk %d in_len=" _LD " input_size=%d\n", 
130 //__LINE__,
131 //in_len,
132 //input_size);
133
134         intratio = (fabs(resample_ratio - floor(.5 + resample_ratio)) < .0001);
135         fcn = .90 / resample_ratio;
136         if(fcn > .90) fcn = .90;
137         filter_l = BLACKSIZE - 6;  
138 /* must be odd */
139         if(0 == filter_l % 2 ) --filter_l;  
140
141 /* if resample_ratio = int, filter_l should be even */
142         filter_l += (int)intratio;
143
144 // Blackman filter initialization must be called whenever there is a 
145 // sampling ratio change
146         if(!resample_init || last_ratio != resample_ratio)
147         {
148                 resample_init = 1;
149                 itime = 0;
150                 bzero(old, sizeof(double) * BLACKSIZE);
151
152 // precompute blackman filter coefficients
153         for (j = 0; j <= 2 * BPC; ++j) 
154                 {
155                         for(j = 0; j <= 2 * BPC; j++)
156                         {
157                                 offset = (double)(j - BPC) / (2 * BPC);
158                                 for(i = 0; i <= filter_l; i++)
159                                 {
160                                         blackfilt[j][i] = blackman(i, offset, fcn, filter_l);
161                                 }
162                         }
163                 }
164         }
165
166 // Main loop
167         double *inbuf_old = old;
168         for(k = 0; 1; k++)
169         {
170                 double time0;
171                 int joff;
172                 
173                 time0 = k * resample_ratio;
174                 j = (int)floor(time0 - itime);
175
176 //              if(j + filter_l / 2 >= input_size) break;
177                 if(j + filter_l / 2 >= in_len) break;
178
179 /* blackman filter.  by default, window centered at j+.5(filter_l%2) */
180 /* but we want a window centered at time0.   */
181                 offset = (time0 - itime - (j + .5 * (filter_l % 2)));
182                 joff = (int)floor((offset * 2 * BPC) + BPC + .5);
183                 xvalue = 0;
184
185
186                 for(i = 0; i <= filter_l; i++)
187                 {
188                         int j2 = i + j - filter_l / 2;
189 //printf("j2=%d\n", j2);
190                         double y = ((j2 < 0) ? inbuf_old[BLACKSIZE + j2] : input[j2]);
191
192                         xvalue += y * blackfilt[joff][i];
193                 }
194
195
196                 if(output_allocation <= output_size)
197                 {
198                         double *new_output = 0;
199                         int64_t new_allocation = output_allocation ? (output_allocation * 2) : 16384;
200                         new_output = new double[new_allocation];
201                         if(output_temp)
202                         {
203                                 bcopy(output_temp, new_output, output_allocation * sizeof(double));
204                                 delete [] output_temp;
205                         }
206
207                         output_temp = new_output;
208                         output_allocation = new_allocation;
209                 }
210
211                 output_temp[output_size++] = xvalue;
212         }
213
214         num_used = MIN(in_len, j + filter_l / 2);
215         itime += num_used - k * resample_ratio;
216         for(i = 0; i < BLACKSIZE; i++)
217                 inbuf_old[i] = input[num_used + i - BLACKSIZE];
218
219         last_ratio = resample_ratio;
220
221 }
222
223 int Resample::read_chunk(Samples *input, 
224         int64_t len)
225 {
226         int fragment = len;
227         if(direction == PLAY_REVERSE &&
228                 input_position - len < 0)
229         {
230                 fragment = input_position;
231         }
232
233         int result = read_samples(input, input_position, fragment);
234
235         if(direction == PLAY_FORWARD)
236         {
237                 input_position += fragment;
238         }
239         else
240         {
241                 input_position -= fragment;
242 // Mute unused part of buffer
243                 if(fragment < len)
244                 {
245                         bzero(input->get_data() + fragment, 
246                                 (len - fragment) * sizeof(double));
247                 }
248         }
249         
250         return result;
251 }
252
253
254 void Resample::reverse_buffer(double *buffer, int64_t len)
255 {
256         double *ap = buffer;
257         double *bp = ap + len;
258         while( ap < --bp ) {
259                 double t = *ap;
260                 *ap++ = *bp;
261                 *bp = t;
262         }
263 }
264
265
266 int Resample::resample(Samples *output, 
267         int64_t out_len,
268         int in_rate,
269         int out_rate,
270         int64_t out_position,
271         int direction)
272 {
273         int result = 0;
274
275
276 // printf("Resample::resample 1 output_position=" _LD " out_position=" _LD " out_len=" _LD "\n", 
277 // output_position, 
278 // out_position,
279 // out_len);
280 // Changed position
281         if(labs(this->output_position - out_position) > 0 ||
282                 direction != this->direction)
283         {
284                 reset();
285
286 // Compute starting point in input rate.
287                 this->input_position = out_position * in_rate / out_rate;
288                 this->direction = direction;
289         }
290
291
292         int remaining_len = out_len;
293         double *output_ptr = output->get_data();
294         while(remaining_len > 0 && !result)
295         {
296 // Drain output buffer
297                 if(output_size)
298                 {
299                         int fragment_len = output_size;
300                         if(fragment_len > remaining_len) fragment_len = remaining_len;
301
302 //printf("Resample::resample 1 %d %d\n", remaining_len, output_size);
303                         bcopy(output_temp, output_ptr, fragment_len * sizeof(double));
304
305 // Shift leftover forward
306                         for(int i = fragment_len; i < output_size; i++)
307                                 output_temp[i - fragment_len] = output_temp[i];
308
309                         output_size -= fragment_len;
310                         remaining_len -= fragment_len;
311                         output_ptr += fragment_len;
312                 }
313
314 // Import new samples
315 //printf("Resample::resample 2 %d\n", remaining_len);
316                 if(remaining_len > 0)
317                 {
318 //printf("Resample::resample 3 input_size=%d out_position=%d\n", input_size, out_position);
319                         result = read_chunk(input, input_size);
320                         resample_chunk(input,
321                                 input_size,
322                                 in_rate,
323                                 out_rate);
324                 }
325         }
326
327
328         if(direction == PLAY_FORWARD)
329                 this->output_position = out_position + out_len;
330         else
331                 this->output_position = out_position - out_len;
332
333 //printf("Resample::resample 2 %d %d\n", this->output_position, out_position);
334 //printf("Resample::resample 2 %d %d\n", out_len, output_size);
335
336 //printf("Resample::resample 2 %d\n", output_size);
337         return result;
338 }
339
340
341