Chris@41
|
1 /*
|
Chris@41
|
2 ** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
|
Chris@41
|
3 ** All rights reserved.
|
Chris@41
|
4 **
|
Chris@41
|
5 ** This code is released under 2-clause BSD license. Please see the
|
Chris@41
|
6 ** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
|
Chris@41
|
7 */
|
Chris@41
|
8
|
Chris@41
|
9 #include "config.h"
|
Chris@41
|
10
|
Chris@41
|
11 #include <stdio.h>
|
Chris@41
|
12 #include <stdlib.h>
|
Chris@41
|
13 #include <string.h>
|
Chris@41
|
14 #include <math.h>
|
Chris@41
|
15 #include <assert.h>
|
Chris@41
|
16
|
Chris@41
|
17 #if (HAVE_FFTW3)
|
Chris@41
|
18 #include <fftw3.h>
|
Chris@41
|
19 #else
|
Chris@41
|
20 static inline void
|
Chris@41
|
21 fftw_cleanup (void)
|
Chris@41
|
22 { return ;
|
Chris@41
|
23 }
|
Chris@41
|
24 #endif
|
Chris@41
|
25
|
Chris@41
|
26 #include <samplerate.h>
|
Chris@41
|
27
|
Chris@41
|
28 #include "util.h"
|
Chris@41
|
29 #define BUFFER_LEN 50000
|
Chris@41
|
30 #define BLOCK_LEN (12)
|
Chris@41
|
31
|
Chris@41
|
32 #define MAX_CHANNELS 10
|
Chris@41
|
33
|
Chris@41
|
34 static void simple_test (int converter, int channel_count, double target_snr) ;
|
Chris@41
|
35 static void process_test (int converter, int channel_count, double target_snr) ;
|
Chris@41
|
36 static void callback_test (int converter, int channel_count, double target_snr) ;
|
Chris@41
|
37
|
Chris@41
|
38 int
|
Chris@41
|
39 main (void)
|
Chris@41
|
40 { double target ;
|
Chris@41
|
41 int k ;
|
Chris@41
|
42
|
Chris@41
|
43 puts ("\n Zero Order Hold interpolator :") ;
|
Chris@41
|
44 target = 38.0 ;
|
Chris@41
|
45 for (k = 1 ; k <= 3 ; k++)
|
Chris@41
|
46 { simple_test (SRC_ZERO_ORDER_HOLD, k, target) ;
|
Chris@41
|
47 process_test (SRC_ZERO_ORDER_HOLD, k, target) ;
|
Chris@41
|
48 callback_test (SRC_ZERO_ORDER_HOLD, k, target) ;
|
Chris@41
|
49 } ;
|
Chris@41
|
50
|
Chris@41
|
51 puts ("\n Linear interpolator :") ;
|
Chris@41
|
52 target = 79.0 ;
|
Chris@41
|
53 for (k = 1 ; k <= 3 ; k++)
|
Chris@41
|
54 { simple_test (SRC_LINEAR, k, target) ;
|
Chris@41
|
55 process_test (SRC_LINEAR, k, target) ;
|
Chris@41
|
56 callback_test (SRC_LINEAR, k, target) ;
|
Chris@41
|
57 } ;
|
Chris@41
|
58
|
Chris@41
|
59 puts ("\n Sinc interpolator :") ;
|
Chris@41
|
60 target = 100.0 ;
|
Chris@41
|
61 for (k = 1 ; k <= MAX_CHANNELS ; k++)
|
Chris@41
|
62 { simple_test (SRC_SINC_FASTEST, k, target) ;
|
Chris@41
|
63 process_test (SRC_SINC_FASTEST, k, target) ;
|
Chris@41
|
64 callback_test (SRC_SINC_FASTEST, k, target) ;
|
Chris@41
|
65 } ;
|
Chris@41
|
66
|
Chris@41
|
67 fftw_cleanup () ;
|
Chris@41
|
68 puts ("") ;
|
Chris@41
|
69
|
Chris@41
|
70 return 0 ;
|
Chris@41
|
71 } /* main */
|
Chris@41
|
72
|
Chris@41
|
73 /*==============================================================================
|
Chris@41
|
74 */
|
Chris@41
|
75
|
Chris@41
|
76 static float input_serial [BUFFER_LEN * MAX_CHANNELS] ;
|
Chris@41
|
77 static float input_interleaved [BUFFER_LEN * MAX_CHANNELS] ;
|
Chris@41
|
78 static float output_interleaved [BUFFER_LEN * MAX_CHANNELS] ;
|
Chris@41
|
79 static float output_serial [BUFFER_LEN * MAX_CHANNELS] ;
|
Chris@41
|
80
|
Chris@41
|
81 static void
|
Chris@41
|
82 simple_test (int converter, int channel_count, double target_snr)
|
Chris@41
|
83 { SRC_DATA src_data ;
|
Chris@41
|
84
|
Chris@41
|
85 double freq, snr ;
|
Chris@41
|
86 int ch, error, frames ;
|
Chris@41
|
87
|
Chris@41
|
88 printf ("\t%-22s (%2d channel%c) ............ ", "simple_test", channel_count, channel_count > 1 ? 's' : ' ') ;
|
Chris@41
|
89 fflush (stdout) ;
|
Chris@41
|
90
|
Chris@41
|
91 assert (channel_count <= MAX_CHANNELS) ;
|
Chris@41
|
92
|
Chris@41
|
93 memset (input_serial, 0, sizeof (input_serial)) ;
|
Chris@41
|
94 memset (input_interleaved, 0, sizeof (input_interleaved)) ;
|
Chris@41
|
95 memset (output_interleaved, 0, sizeof (output_interleaved)) ;
|
Chris@41
|
96 memset (output_serial, 0, sizeof (output_serial)) ;
|
Chris@41
|
97
|
Chris@41
|
98 frames = BUFFER_LEN ;
|
Chris@41
|
99
|
Chris@41
|
100 /* Calculate channel_count separate windowed sine waves. */
|
Chris@41
|
101 for (ch = 0 ; ch < channel_count ; ch++)
|
Chris@41
|
102 { freq = (200.0 + 33.333333333 * ch) / 44100.0 ;
|
Chris@41
|
103 gen_windowed_sines (1, &freq, 1.0, input_serial + ch * frames, frames) ;
|
Chris@41
|
104 } ;
|
Chris@41
|
105
|
Chris@41
|
106 /* Interleave the data in preparation for SRC. */
|
Chris@41
|
107 interleave_data (input_serial, input_interleaved, frames, channel_count) ;
|
Chris@41
|
108
|
Chris@41
|
109 /* Choose a converstion ratio <= 1.0. */
|
Chris@41
|
110 src_data.src_ratio = 0.95 ;
|
Chris@41
|
111
|
Chris@41
|
112 src_data.data_in = input_interleaved ;
|
Chris@41
|
113 src_data.input_frames = frames ;
|
Chris@41
|
114
|
Chris@41
|
115 src_data.data_out = output_interleaved ;
|
Chris@41
|
116 src_data.output_frames = frames ;
|
Chris@41
|
117
|
Chris@41
|
118 if ((error = src_simple (&src_data, converter, channel_count)))
|
Chris@41
|
119 { printf ("\n\nLine %d : %s\n\n", __LINE__, src_strerror (error)) ;
|
Chris@41
|
120 exit (1) ;
|
Chris@41
|
121 } ;
|
Chris@41
|
122
|
Chris@41
|
123 if (fabs (src_data.output_frames_gen - src_data.src_ratio * src_data.input_frames) > 2)
|
Chris@41
|
124 { printf ("\n\nLine %d : bad output data length %ld should be %d.\n", __LINE__,
|
Chris@41
|
125 src_data.output_frames_gen, (int) floor (src_data.src_ratio * src_data.input_frames)) ;
|
Chris@41
|
126 printf ("\tsrc_ratio : %.4f\n", src_data.src_ratio) ;
|
Chris@41
|
127 printf ("\tinput_len : %ld\n", src_data.input_frames) ;
|
Chris@41
|
128 printf ("\toutput_len : %ld\n\n", src_data.output_frames_gen) ;
|
Chris@41
|
129 exit (1) ;
|
Chris@41
|
130 } ;
|
Chris@41
|
131
|
Chris@41
|
132 /* De-interleave data so SNR can be calculated for each channel. */
|
Chris@41
|
133 deinterleave_data (output_interleaved, output_serial, frames, channel_count) ;
|
Chris@41
|
134
|
Chris@41
|
135 for (ch = 0 ; ch < channel_count ; ch++)
|
Chris@41
|
136 { snr = calculate_snr (output_serial + ch * frames, frames, 1) ;
|
Chris@41
|
137 if (snr < target_snr)
|
Chris@41
|
138 { printf ("\n\nLine %d: channel %d snr %f should be %f\n", __LINE__, ch, snr, target_snr) ;
|
Chris@41
|
139 save_oct_float ("output.dat", input_serial, channel_count * frames, output_serial, channel_count * frames) ;
|
Chris@41
|
140 exit (1) ;
|
Chris@41
|
141 } ;
|
Chris@41
|
142 } ;
|
Chris@41
|
143
|
Chris@41
|
144 puts ("ok") ;
|
Chris@41
|
145
|
Chris@41
|
146 return ;
|
Chris@41
|
147 } /* simple_test */
|
Chris@41
|
148
|
Chris@41
|
149 /*==============================================================================
|
Chris@41
|
150 */
|
Chris@41
|
151
|
Chris@41
|
152 static void
|
Chris@41
|
153 process_test (int converter, int channel_count, double target_snr)
|
Chris@41
|
154 { SRC_STATE *src_state ;
|
Chris@41
|
155 SRC_DATA src_data ;
|
Chris@41
|
156
|
Chris@41
|
157 double freq, snr ;
|
Chris@41
|
158 int ch, error, frames, current_in, current_out ;
|
Chris@41
|
159
|
Chris@41
|
160 printf ("\t%-22s (%2d channel%c) ............ ", "process_test", channel_count, channel_count > 1 ? 's' : ' ') ;
|
Chris@41
|
161 fflush (stdout) ;
|
Chris@41
|
162
|
Chris@41
|
163 assert (channel_count <= MAX_CHANNELS) ;
|
Chris@41
|
164
|
Chris@41
|
165 memset (input_serial, 0, sizeof (input_serial)) ;
|
Chris@41
|
166 memset (input_interleaved, 0, sizeof (input_interleaved)) ;
|
Chris@41
|
167 memset (output_interleaved, 0, sizeof (output_interleaved)) ;
|
Chris@41
|
168 memset (output_serial, 0, sizeof (output_serial)) ;
|
Chris@41
|
169
|
Chris@41
|
170 frames = BUFFER_LEN ;
|
Chris@41
|
171
|
Chris@41
|
172 /* Calculate channel_count separate windowed sine waves. */
|
Chris@41
|
173 for (ch = 0 ; ch < channel_count ; ch++)
|
Chris@41
|
174 { freq = (400.0 + 11.333333333 * ch) / 44100.0 ;
|
Chris@41
|
175 gen_windowed_sines (1, &freq, 1.0, input_serial + ch * frames, frames) ;
|
Chris@41
|
176 } ;
|
Chris@41
|
177
|
Chris@41
|
178 /* Interleave the data in preparation for SRC. */
|
Chris@41
|
179 interleave_data (input_serial, input_interleaved, frames, channel_count) ;
|
Chris@41
|
180
|
Chris@41
|
181 /* Perform sample rate conversion. */
|
Chris@41
|
182 if ((src_state = src_new (converter, channel_count, &error)) == NULL)
|
Chris@41
|
183 { printf ("\n\nLine %d : src_new() failed : %s\n\n", __LINE__, src_strerror (error)) ;
|
Chris@41
|
184 exit (1) ;
|
Chris@41
|
185 } ;
|
Chris@41
|
186
|
Chris@41
|
187 src_data.end_of_input = 0 ; /* Set this later. */
|
Chris@41
|
188
|
Chris@41
|
189 /* Choose a converstion ratio < 1.0. */
|
Chris@41
|
190 src_data.src_ratio = 0.95 ;
|
Chris@41
|
191
|
Chris@41
|
192 src_data.data_in = input_interleaved ;
|
Chris@41
|
193 src_data.data_out = output_interleaved ;
|
Chris@41
|
194
|
Chris@41
|
195 current_in = current_out = 0 ;
|
Chris@41
|
196
|
Chris@41
|
197 while (1)
|
Chris@41
|
198 { src_data.input_frames = MAX (MIN (BLOCK_LEN, frames - current_in), 0) ;
|
Chris@41
|
199 src_data.output_frames = MAX (MIN (BLOCK_LEN, frames - current_out), 0) ;
|
Chris@41
|
200
|
Chris@41
|
201 if ((error = src_process (src_state, &src_data)))
|
Chris@41
|
202 { printf ("\n\nLine %d : %s\n\n", __LINE__, src_strerror (error)) ;
|
Chris@41
|
203 exit (1) ;
|
Chris@41
|
204 } ;
|
Chris@41
|
205
|
Chris@41
|
206 if (src_data.end_of_input && src_data.output_frames_gen == 0)
|
Chris@41
|
207 break ;
|
Chris@41
|
208
|
Chris@41
|
209 current_in += src_data.input_frames_used ;
|
Chris@41
|
210 current_out += src_data.output_frames_gen ;
|
Chris@41
|
211
|
Chris@41
|
212 src_data.data_in += src_data.input_frames_used * channel_count ;
|
Chris@41
|
213 src_data.data_out += src_data.output_frames_gen * channel_count ;
|
Chris@41
|
214
|
Chris@41
|
215 src_data.end_of_input = (current_in >= frames) ? 1 : 0 ;
|
Chris@41
|
216 } ;
|
Chris@41
|
217
|
Chris@41
|
218 src_state = src_delete (src_state) ;
|
Chris@41
|
219
|
Chris@41
|
220 if (fabs (current_out - src_data.src_ratio * current_in) > 2)
|
Chris@41
|
221 { printf ("\n\nLine %d : bad output data length %d should be %d.\n", __LINE__,
|
Chris@41
|
222 current_out, (int) floor (src_data.src_ratio * current_in)) ;
|
Chris@41
|
223 printf ("\tsrc_ratio : %.4f\n", src_data.src_ratio) ;
|
Chris@41
|
224 printf ("\tinput_len : %d\n", frames) ;
|
Chris@41
|
225 printf ("\toutput_len : %d\n\n", current_out) ;
|
Chris@41
|
226 exit (1) ;
|
Chris@41
|
227 } ;
|
Chris@41
|
228
|
Chris@41
|
229 /* De-interleave data so SNR can be calculated for each channel. */
|
Chris@41
|
230 deinterleave_data (output_interleaved, output_serial, frames, channel_count) ;
|
Chris@41
|
231
|
Chris@41
|
232 for (ch = 0 ; ch < channel_count ; ch++)
|
Chris@41
|
233 { snr = calculate_snr (output_serial + ch * frames, frames, 1) ;
|
Chris@41
|
234 if (snr < target_snr)
|
Chris@41
|
235 { printf ("\n\nLine %d: channel %d snr %f should be %f\n", __LINE__, ch, snr, target_snr) ;
|
Chris@41
|
236 save_oct_float ("output.dat", input_serial, channel_count * frames, output_serial, channel_count * frames) ;
|
Chris@41
|
237 exit (1) ;
|
Chris@41
|
238 } ;
|
Chris@41
|
239 } ;
|
Chris@41
|
240
|
Chris@41
|
241 puts ("ok") ;
|
Chris@41
|
242
|
Chris@41
|
243 return ;
|
Chris@41
|
244 } /* process_test */
|
Chris@41
|
245
|
Chris@41
|
246 /*==============================================================================
|
Chris@41
|
247 */
|
Chris@41
|
248
|
Chris@41
|
249 typedef struct
|
Chris@41
|
250 { int channels ;
|
Chris@41
|
251 long total_frames ;
|
Chris@41
|
252 long current_frame ;
|
Chris@41
|
253 float *data ;
|
Chris@41
|
254 } TEST_CB_DATA ;
|
Chris@41
|
255
|
Chris@41
|
256 static long
|
Chris@41
|
257 test_callback_func (void *cb_data, float **data)
|
Chris@41
|
258 { TEST_CB_DATA *pcb_data ;
|
Chris@41
|
259
|
Chris@41
|
260 long frames ;
|
Chris@41
|
261
|
Chris@41
|
262 if ((pcb_data = cb_data) == NULL)
|
Chris@41
|
263 return 0 ;
|
Chris@41
|
264
|
Chris@41
|
265 if (data == NULL)
|
Chris@41
|
266 return 0 ;
|
Chris@41
|
267
|
Chris@41
|
268 *data = pcb_data->data + (pcb_data->current_frame * pcb_data->channels) ;
|
Chris@41
|
269
|
Chris@41
|
270 if (pcb_data->total_frames - pcb_data->current_frame < BLOCK_LEN)
|
Chris@41
|
271 frames = pcb_data->total_frames - pcb_data->current_frame ;
|
Chris@41
|
272 else
|
Chris@41
|
273 frames = BLOCK_LEN ;
|
Chris@41
|
274
|
Chris@41
|
275 pcb_data->current_frame += frames ;
|
Chris@41
|
276
|
Chris@41
|
277 return frames ;
|
Chris@41
|
278 } /* test_callback_func */
|
Chris@41
|
279
|
Chris@41
|
280 static void
|
Chris@41
|
281 callback_test (int converter, int channel_count, double target_snr)
|
Chris@41
|
282 { TEST_CB_DATA test_callback_data ;
|
Chris@41
|
283 SRC_STATE *src_state = NULL ;
|
Chris@41
|
284
|
Chris@41
|
285 double freq, snr, src_ratio ;
|
Chris@41
|
286 int ch, error, frames, read_total, read_count ;
|
Chris@41
|
287
|
Chris@41
|
288 printf ("\t%-22s (%2d channel%c) ............ ", "callback_test", channel_count, channel_count > 1 ? 's' : ' ') ;
|
Chris@41
|
289 fflush (stdout) ;
|
Chris@41
|
290
|
Chris@41
|
291 assert (channel_count <= MAX_CHANNELS) ;
|
Chris@41
|
292
|
Chris@41
|
293 memset (input_serial, 0, sizeof (input_serial)) ;
|
Chris@41
|
294 memset (input_interleaved, 0, sizeof (input_interleaved)) ;
|
Chris@41
|
295 memset (output_interleaved, 0, sizeof (output_interleaved)) ;
|
Chris@41
|
296 memset (output_serial, 0, sizeof (output_serial)) ;
|
Chris@41
|
297 memset (&test_callback_data, 0, sizeof (test_callback_data)) ;
|
Chris@41
|
298
|
Chris@41
|
299 frames = BUFFER_LEN ;
|
Chris@41
|
300
|
Chris@41
|
301 /* Calculate channel_count separate windowed sine waves. */
|
Chris@41
|
302 for (ch = 0 ; ch < channel_count ; ch++)
|
Chris@41
|
303 { freq = (200.0 + 33.333333333 * ch) / 44100.0 ;
|
Chris@41
|
304 gen_windowed_sines (1, &freq, 1.0, input_serial + ch * frames, frames) ;
|
Chris@41
|
305 } ;
|
Chris@41
|
306
|
Chris@41
|
307 /* Interleave the data in preparation for SRC. */
|
Chris@41
|
308 interleave_data (input_serial, input_interleaved, frames, channel_count) ;
|
Chris@41
|
309
|
Chris@41
|
310 /* Perform sample rate conversion. */
|
Chris@41
|
311 src_ratio = 0.95 ;
|
Chris@41
|
312 test_callback_data.channels = channel_count ;
|
Chris@41
|
313 test_callback_data.total_frames = frames ;
|
Chris@41
|
314 test_callback_data.current_frame = 0 ;
|
Chris@41
|
315 test_callback_data.data = input_interleaved ;
|
Chris@41
|
316
|
Chris@41
|
317 if ((src_state = src_callback_new (test_callback_func, converter, channel_count, &error, &test_callback_data)) == NULL)
|
Chris@41
|
318 { printf ("\n\nLine %d : %s\n\n", __LINE__, src_strerror (error)) ;
|
Chris@41
|
319 exit (1) ;
|
Chris@41
|
320 } ;
|
Chris@41
|
321
|
Chris@41
|
322 read_total = 0 ;
|
Chris@41
|
323 while (read_total < frames)
|
Chris@41
|
324 { read_count = src_callback_read (src_state, src_ratio, frames - read_total, output_interleaved + read_total * channel_count) ;
|
Chris@41
|
325
|
Chris@41
|
326 if (read_count <= 0)
|
Chris@41
|
327 break ;
|
Chris@41
|
328
|
Chris@41
|
329 read_total += read_count ;
|
Chris@41
|
330 } ;
|
Chris@41
|
331
|
Chris@41
|
332 if ((error = src_error (src_state)) != 0)
|
Chris@41
|
333 { printf ("\n\nLine %d : %s\n\n", __LINE__, src_strerror (error)) ;
|
Chris@41
|
334 exit (1) ;
|
Chris@41
|
335 } ;
|
Chris@41
|
336
|
Chris@41
|
337 src_state = src_delete (src_state) ;
|
Chris@41
|
338
|
Chris@41
|
339 if (fabs (read_total - src_ratio * frames) > 2)
|
Chris@41
|
340 { printf ("\n\nLine %d : bad output data length %d should be %d.\n", __LINE__,
|
Chris@41
|
341 read_total, (int) floor (src_ratio * frames)) ;
|
Chris@41
|
342 printf ("\tsrc_ratio : %.4f\n", src_ratio) ;
|
Chris@41
|
343 printf ("\tinput_len : %d\n", frames) ;
|
Chris@41
|
344 printf ("\toutput_len : %d\n\n", read_total) ;
|
Chris@41
|
345 exit (1) ;
|
Chris@41
|
346 } ;
|
Chris@41
|
347
|
Chris@41
|
348 /* De-interleave data so SNR can be calculated for each channel. */
|
Chris@41
|
349 deinterleave_data (output_interleaved, output_serial, frames, channel_count) ;
|
Chris@41
|
350
|
Chris@41
|
351 for (ch = 0 ; ch < channel_count ; ch++)
|
Chris@41
|
352 { snr = calculate_snr (output_serial + ch * frames, frames, 1) ;
|
Chris@41
|
353 if (snr < target_snr)
|
Chris@41
|
354 { printf ("\n\nLine %d: channel %d snr %f should be %f\n", __LINE__, ch, snr, target_snr) ;
|
Chris@41
|
355 save_oct_float ("output.dat", input_serial, channel_count * frames, output_serial, channel_count * frames) ;
|
Chris@41
|
356 exit (1) ;
|
Chris@41
|
357 } ;
|
Chris@41
|
358 } ;
|
Chris@41
|
359
|
Chris@41
|
360 puts ("ok") ;
|
Chris@41
|
361
|
Chris@41
|
362 return ;
|
Chris@41
|
363 } /* callback_test */
|
Chris@41
|
364
|