1 ////////////////////////////////////////////////////////////////////////////
2 // **** WAVPACK **** //
3 // Hybrid Lossless Wavefile Compressor //
4 // Copyright (c) 1998 - 2020 David Bryant. //
5 // All Rights Reserved. //
6 // Distributed under the BSD Software License (see license.txt) //
7 ////////////////////////////////////////////////////////////////////////////
8
9 // wvtest.c
10
11 // This is the main module for the WavPack command-line library tester.
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <math.h>
18 #include <pthread.h>
19
20 #include "wavpack.h"
21 #include "utils.h" // for PACKAGE_VERSION, etc.
22 #include "md5.h"
23
24 #define CLEAR(destin) memset (&destin, 0, sizeof (destin));
25
26 #ifndef M_PI
27 #define M_PI 3.14159265358979323
28 #endif
29
30 static const char *sign_on = "\n"
31 " WVTEST libwavpack Tester/Exerciser for WavPack %s Version %s\n"
32 " Copyright (c) 2020 David Bryant. All Rights Reserved.\n\n";
33
34 static const char *version_warning = "\n"
35 " WARNING: WVTEST using libwavpack version %s, expected %s (see README)\n\n";
36
37 static const char *usage =
38 " Usage: WVTEST --default|--exhaustive [-options]\n"
39 " WVTEST --seektest[=n] file.wv [...] (n=runs per file, def=1)\n\n"
40 " Options: --default = perform the default test suite\n"
41 " --exhaustive = perform the exhaustive test suite\n"
42 " --short = perform shorter runs of each test\n"
43 " --long = perform longer runs of each test\n"
44 " --no-decode = skip the decoding process\n"
45 " --no-extras = skip the \"extra\" modes\n"
46 " --no-hybrid = skip the hybrid modes\n"
47 " --no-floats = skip the float modes\n"
48 " --no-lossy = skip the lossy modes\n"
49 " --no-speeds = skip the speed modes (fast, high, etc.)\n"
50 " --help = display this message\n"
51 " --version = write the version to stdout\n"
52 " --write=n[-n][,...] = write specific test(s) (or range(s)) to disk\n\n"
53 " Web: Visit www.wavpack.com for latest version and info\n";
54
55 #define TEST_FLAG_EXTRA_MODE(x) ((x) & TEST_FLAG_EXTRA_MASK)
56 #define TEST_FLAG_EXTRA_MASK 0x7
57 #define TEST_FLAG_FLOAT_DATA 0x8
58 #define TEST_FLAG_WRITE_FILE 0x10
59 #define TEST_FLAG_DEFAULT 0x20
60 #define TEST_FLAG_EXHAUSTIVE 0x40
61 #define TEST_FLAG_NO_FLOATS 0x80
62 #define TEST_FLAG_NO_HYBRID 0x100
63 #define TEST_FLAG_NO_EXTRAS 0x200
64 #define TEST_FLAG_NO_LOSSY 0x400
65 #define TEST_FLAG_NO_SPEEDS 0x800
66 #define TEST_FLAG_STORE_FLOAT_AS_INT32 0x1000
67 #define TEST_FLAG_STORE_INT32_AS_FLOAT 0x2000
68 #define TEST_FLAG_IGNORE_WVC 0x4000
69 #define TEST_FLAG_NO_DECODE 0x8000
70
71 static int run_test_size_modes (int wpconfig_flags, int test_flags, int base_minutes);
72 static int run_test_speed_modes (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds);
73 static int run_test_extra_modes (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds);
74 static int run_test (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds);
75
76 #define NUM_WRITE_RANGES 10
77 static struct { int start, stop; } write_ranges [NUM_WRITE_RANGES];
78 int number_of_ranges;
79
80 enum generator_type { noise, tone };
81
82 struct audio_generator {
83 enum generator_type type;
84 union {
85 struct noise_generator {
86 float sum1, sum2, sum2p; // these are changing
87 float factor, scalar; // these are constant
88 } noise_cxt;
89
90 struct tone_generator {
91 int sample_rate, samples_per_update; // these are constant
92 int high_frequency, low_frequency; // these are constant
93 float angle, velocity, acceleration; // these are changing
94 int samples_left;
95 } tone_cxt;
96 } u;
97 };
98
99 static int seeking_test (char *filename, uint32_t test_count);
100 static void tone_generator_init (struct audio_generator *cxt, int sample_rate, int low_freq, int high_freq);
101 static void noise_generator_init (struct audio_generator *cxt, float factor);
102 static void audio_generator_run (struct audio_generator *cxt, float *samples, int num_samples);
103 static void mix_samples_with_gain (float *destin, float *source, int num_samples, int num_chans, float initial_gain, float final_gain);
104 static void truncate_float_samples (float *samples, int num_samples, int bits);
105 static void float_to_integer_samples (float *samples, int num_samples, int bits);
106 static void float_to_32bit_integer_samples (float *samples, int num_samples);
107 static void *store_samples (void *dst, int32_t *src, int qmode, int bps, int count);
108 static double frandom (void);
109
110 typedef struct {
111 uint32_t buffer_size, bytes_written, bytes_read, first_block_size;
112 volatile unsigned char *buffer_base, *buffer_head, *buffer_tail;
113 int push_back, done, error, empty_waits, full_waits;
114 pthread_cond_t cond_read, cond_write;
115 pthread_mutex_t mutex;
116 FILE *file;
117 } StreamingFile;
118
119 typedef struct {
120 StreamingFile *wv_stream, *wvc_stream;
121 unsigned char md5_decoded [16];
122 uint32_t sample_count;
123 int num_errors;
124 } WavpackDecoder;
125
126 static void initialize_stream (StreamingFile *ws, int buffer_size);
127 static int write_block (void *id, void *data, int32_t length);
128 static void flush_stream (StreamingFile *ws);
129 static void free_stream (StreamingFile *ws);
130 static void *decode_thread (void *threadid);
131 static WavpackStreamReader freader;
132
133 //////////////////////////////////////// main () function for CLI //////////////////////////////////////
134
main(argc,argv)135 int main (argc, argv) int argc; char **argv;
136 {
137 int wpconfig_flags = CONFIG_MD5_CHECKSUM | CONFIG_OPTIMIZE_MONO, test_flags = 0, base_minutes = 2, res;
138 int seektest = 0;
139
140 // loop through command-line arguments
141
142 while (--argc) {
143 if (**++argv == '-' && (*argv)[1] == '-' && (*argv)[2]) {
144 char *long_option = *argv + 2, *long_param = long_option;
145
146 while (*long_param)
147 if (*long_param++ == '=')
148 break;
149
150 if (!strcmp (long_option, "help")) { // --help
151 printf ("%s", usage);
152 return 0;
153 }
154 else if (!strcmp (long_option, "version")) { // --version
155 printf ("wvtest %s\n", PACKAGE_VERSION);
156 printf ("libwavpack %s\n", WavpackGetLibraryVersionString ());
157 return 0;
158 }
159 else if (!strcmp (long_option, "short")) { // --short
160 base_minutes = 1;
161 }
162 else if (!strcmp (long_option, "long")) { // --long
163 base_minutes = 5;
164 }
165 else if (!strcmp (long_option, "default")) { // --default
166 test_flags |= TEST_FLAG_DEFAULT;
167 }
168 else if (!strcmp (long_option, "exhaustive")) { // --exhaustive
169 test_flags |= TEST_FLAG_EXHAUSTIVE;
170 }
171 else if (!strcmp (long_option, "no-extras")) { // --no-extras
172 test_flags |= TEST_FLAG_NO_EXTRAS;
173 }
174 else if (!strcmp (long_option, "no-hybrid")) { // --no-hybrid
175 test_flags |= TEST_FLAG_NO_HYBRID;
176 }
177 else if (!strcmp (long_option, "no-lossy")) { // --no-lossy
178 test_flags |= TEST_FLAG_NO_LOSSY;
179 }
180 else if (!strcmp (long_option, "no-speeds")) { // --no-speeds
181 test_flags |= TEST_FLAG_NO_SPEEDS;
182 }
183 else if (!strcmp (long_option, "no-floats")) { // --no-floats
184 test_flags |= TEST_FLAG_NO_FLOATS;
185 }
186 else if (!strcmp (long_option, "no-decode")) { // --no-decode
187 test_flags |= TEST_FLAG_NO_DECODE;
188 }
189 else if (!strncmp (long_option, "write", 5)) { // --write
190 for (number_of_ranges = 0; *long_param && isdigit (*long_param) && number_of_ranges < NUM_WRITE_RANGES;) {
191 write_ranges [number_of_ranges].start = strtol (long_param, &long_param, 10);
192
193 if (*long_param == '-') {
194 long_param++;
195 if (isdigit (*long_param))
196 write_ranges [number_of_ranges].stop = strtol (long_param, &long_param, 10);
197 else
198 break;
199 }
200 else
201 write_ranges [number_of_ranges].stop = write_ranges [number_of_ranges].start;
202
203 number_of_ranges++;
204
205 if (*long_param == ',')
206 long_param++;
207 else
208 break;
209 }
210
211 if (*long_param || !number_of_ranges) {
212 printf ("syntax error in write specification!\n");
213 return 1;
214 }
215 else
216 test_flags |= TEST_FLAG_WRITE_FILE;
217 }
218 else if (!strncmp (long_option, "seektest", 8)) { // --seektest[=n]
219 if (*long_param)
220 seektest = strtol (long_param, NULL, 10);
221 else
222 seektest = 1;
223
224 if (seektest)
225 break;
226 }
227 else {
228 printf ("unknown option: %s !\n", long_option);
229 return 1;
230 }
231 }
232 else {
233 printf ("unknown option: %s !\n", *argv);
234 return 1;
235 }
236 }
237
238 if (strcmp (WavpackGetLibraryVersionString (), PACKAGE_VERSION))
239 printf (version_warning, WavpackGetLibraryVersionString (), PACKAGE_VERSION);
240 else
241 printf (sign_on, VERSION_OS, WavpackGetLibraryVersionString ());
242
243 if (!seektest && !(test_flags & (TEST_FLAG_DEFAULT | TEST_FLAG_EXHAUSTIVE))) {
244 puts (usage);
245 return 1;
246 }
247
248 if (seektest) {
249 while (--argc)
250 if ((res = seeking_test (*++argv, seektest)))
251 break;
252 }
253 else {
254 printf ("\n\n ****** pure lossless ******\n");
255 res = run_test_size_modes (wpconfig_flags, test_flags, base_minutes);
256 if (res) goto done;
257
258 if (!(test_flags & TEST_FLAG_NO_HYBRID)) {
259 printf ("\n\n ****** hybrid lossless ******\n");
260 res = run_test_size_modes (wpconfig_flags | CONFIG_HYBRID_FLAG | CONFIG_CREATE_WVC, test_flags, base_minutes);
261 if (res) goto done;
262
263 if (!(test_flags & TEST_FLAG_NO_LOSSY)) {
264 printf ("\n\n ****** hybrid lossy ******\n");
265 res = run_test_size_modes (wpconfig_flags | CONFIG_HYBRID_FLAG, test_flags, base_minutes);
266 if (res) goto done;
267
268 printf ("\n\n ****** hybrid lossless (but ignore wvc on decode) ******\n");
269 res = run_test_size_modes (wpconfig_flags | CONFIG_HYBRID_FLAG | CONFIG_CREATE_WVC,
270 test_flags | TEST_FLAG_IGNORE_WVC, base_minutes);
271 if (res) goto done;
272 }
273 }
274 }
275
276 done:
277 if (res)
278 printf ("\ntest failed!\n\n");
279 else
280 printf ("\nall tests pass\n\n");
281
282 return res;
283 }
284
285 // Function to stress-test the WavpackSeekSample() API. Given the specified WavPack file, perform
286 // the specified number of seektest runs on that file. For each test run, a different, random
287 // seek interval is chosen. Note that MD5 sums are calculated for each chunk interval so we
288 // actually verify that every sample decoded is correct. For each test run, we decode the entire
289 // file 4 times over, on average.
290
seeking_test(char * filename,uint32_t test_count)291 static int seeking_test (char *filename, uint32_t test_count)
292 {
293 char error [80];
294 WavpackContext *wpc = WavpackOpenFileInput (filename, error, OPEN_WVC | OPEN_DSD_NATIVE | OPEN_ALT_TYPES, 0);
295 int64_t min_chunk_size = 256, total_samples, sample_count = 0;
296 char md5_string1 [] = "????????????????????????????????";
297 char md5_string2 [] = "????????????????????????????????";
298 int32_t *decoded_samples, num_chans, bps, test_index, qmode;
299 unsigned char md5_initial [16], md5_stored [16];
300 MD5_CTX md5_global, md5_local;
301 unsigned char *chunked_md5;
302
303 printf ("\n-------------------- file: %s %s--------------------\n",
304 filename, (WavpackGetMode (wpc) & MODE_WVC) ? "(+wvc) " : "");
305
306 if (!wpc) {
307 printf ("seeking_test(): error \"%s\" opening input file \"%s\"\n", error, filename);
308 return -1;
309 }
310
311 num_chans = WavpackGetNumChannels (wpc);
312 total_samples = WavpackGetNumSamples64 (wpc);
313 bps = WavpackGetBytesPerSample (wpc);
314 qmode = WavpackGetQualifyMode (wpc);
315
316 if (total_samples < 2 || total_samples == -1) {
317 printf ("seeking_test(): can't determine file size!\n");
318 return -1;
319 }
320
321 if (qmode & QMODE_DSD_IN_BLOCKS) {
322 printf ("seeking_test(): can't handle blocked DSD audio (i.e., from .dsf files)!\n");
323 return -1;
324 }
325
326 // For very short files, reduce the minimum chunk size
327
328 while (min_chunk_size > 1 && total_samples / min_chunk_size < 256)
329 min_chunk_size /= 2;
330
331 for (test_index = 0; test_index < test_count; test_index++) {
332 uint32_t chunk_samples, total_chunks, chunk_count = 0, seek_count = 0;
333
334 chunk_samples = min_chunk_size + frandom () * min_chunk_size; // 256 - 511 (unless reduced)
335 total_chunks = (total_samples + chunk_samples - 1) / chunk_samples;
336 decoded_samples = malloc (sizeof (int32_t) * chunk_samples * num_chans);
337 chunked_md5 = malloc (total_chunks * 16);
338
339 if (!chunked_md5 || !decoded_samples) {
340 printf ("seeking_test(): can't allocate memory!\n");
341 return -1;
342 }
343
344 sample_count = chunk_count = 0;
345 MD5_Init (&md5_global);
346
347 // read the entire file, calculating the MD5 sums for the whole file and for each "chunk"
348
349 while (1) {
350 int samples = WavpackUnpackSamples (wpc, decoded_samples, chunk_samples);
351
352 if (!samples)
353 break;
354
355 store_samples (decoded_samples, decoded_samples, qmode, bps, samples * num_chans);
356 MD5_Update (&md5_global, (unsigned char *) decoded_samples, bps * samples * num_chans);
357
358 MD5_Init (&md5_local);
359 MD5_Update (&md5_local, (unsigned char *) decoded_samples, bps * samples * num_chans);
360 MD5_Final (chunked_md5 + chunk_count * 16, &md5_local);
361
362 sample_count += samples;
363 chunk_count++;
364 }
365
366 if (WavpackGetNumErrors (wpc)) {
367 printf ("seeking_test(): decoder reported %d errors!\n", WavpackGetNumErrors (wpc));
368 return -1;
369 }
370
371 if (total_samples != sample_count) {
372 printf ("seeking_test(): sample count is not correct!\n");
373 return -1;
374 }
375
376 if (total_chunks != chunk_count) {
377 printf ("seeking_test(): chunk count is not correct (not sure if this can happen)!\n");
378 return -1;
379 }
380
381 // The first time through the file we verify that the MD5 sum matches what's stored in the file
382 // (if one is stored there). On subsequent tests, we verify that the whole-file MD5 sum matches
383 // what we got the first time.
384
385 if (!test_index) {
386 int file_has_md5 = WavpackGetMD5Sum (wpc, md5_stored), i;
387
388 MD5_Final (md5_initial, &md5_global);
389
390 for (i = 0; i < 16; ++i) {
391 sprintf (md5_string1 + (i * 2), "%02x", md5_stored [i]);
392 sprintf (md5_string2 + (i * 2), "%02x", md5_initial [i]);
393 }
394
395 printf ("stored/actual sample count: %lld / %lld\n", (long long int) total_samples, (long long int) sample_count);
396 if (file_has_md5) printf ("stored md5: %s\n", md5_string1);
397 printf ("actual md5: %s\n", md5_string2);
398
399 if (WavpackGetMode (wpc) & MODE_LOSSLESS)
400 if (file_has_md5 && memcmp (md5_stored, md5_initial, sizeof (md5_stored))) {
401 printf ("seeking_test(): MD5 does not match MD5 stored in file!\n");
402 return -1;
403 }
404 }
405 else {
406 unsigned char md5_subsequent [16];
407
408 MD5_Final (md5_subsequent, &md5_global);
409
410 if (memcmp (md5_subsequent, md5_initial, sizeof (md5_stored))) {
411 printf ("seeking_test(): MD5 does not match MD5 read initially!\n");
412 return -1;
413 }
414 }
415
416 // Half the time, reopen the file. This lets us catch errors caused by seeking to locations
417 // that have never been decoded (at least not for this open call).
418
419 if (frandom() < 0.5) {
420 WavpackCloseFile (wpc);
421 wpc = WavpackOpenFileInput (filename, error, OPEN_WVC | OPEN_DSD_NATIVE | OPEN_ALT_TYPES, 0);
422
423 if (!wpc) {
424 printf ("seeking_test(): error \"%s\" reopening input file \"%s\"\n", error, filename);
425 return -1;
426 }
427 }
428
429 chunk_count *= 4; // decode each chunk 4 times, on average
430
431 while (chunk_count) {
432 int start_chunk = 0, stop_chunk, current_chunk, num_chunks = 1;
433
434 start_chunk = floor (frandom () * total_chunks);
435 if (start_chunk == total_chunks) start_chunk--;
436
437 // At a minimum, we very one chunk after the seek. However, we also random chose additional
438 // chunks to verify so that sometimes we verify lots of data after the seek.
439
440 while (start_chunk + num_chunks < total_chunks && frandom () < 0.667)
441 num_chunks *= 2;
442
443 if (start_chunk + num_chunks > total_chunks)
444 num_chunks = total_chunks - start_chunk;
445
446 stop_chunk = start_chunk + num_chunks - 1;
447
448 if (!WavpackSeekSample64 (wpc, (int64_t) start_chunk * chunk_samples)) {
449 printf ("seeking_test(): seek error!\n");
450 return -1;
451 }
452
453 for (current_chunk = start_chunk; current_chunk <= stop_chunk; ++current_chunk) {
454 int samples = WavpackUnpackSamples (wpc, decoded_samples, chunk_samples);
455 unsigned char md5_chunk [16];
456
457 if (!samples) {
458 printf ("seeking_test(): seek error!\n");
459 return -1;
460 }
461
462 store_samples (decoded_samples, decoded_samples, qmode, bps, samples * num_chans);
463
464 // if (frandom() < 0.0001)
465 // decoded_samples [(int) floor (samples * frandom())] ^= 1;
466
467 MD5_Init (&md5_local);
468 MD5_Update (&md5_local, (unsigned char *) decoded_samples, bps * samples * num_chans);
469 MD5_Final (md5_chunk, &md5_local);
470
471 if (memcmp (chunked_md5 + current_chunk * 16, md5_chunk, sizeof (md5_chunk))) {
472 printf ("seeking_test(): seek+decode error at %lld!\n", (long long int) current_chunk * chunk_samples);
473 return -1;
474 }
475
476 if (chunk_count)
477 chunk_count--;
478 else
479 break;
480 }
481
482 // display the "dot" every 10 seeks
483
484 if (++seek_count % 10 == 0) {
485 if (seek_count % 640) {
486 putchar ('.'); fflush (stdout);
487 }
488 else
489 puts (".");
490 }
491 }
492
493 printf ("\nresult: %u successful seeks on %u-sample boundaries\n", seek_count, chunk_samples);
494
495 if (!WavpackSeekSample (wpc, 0)) {
496 printf ("seeking_test(): rewind error!\n");
497 return -1;
498 }
499
500 free (chunked_md5);
501 free (decoded_samples);
502 }
503
504 WavpackCloseFile (wpc);
505 return 0;
506 }
507
508 // Given a WavPack configuration and test flags, run the various combinations of
509 // bit-depth and channel configurations. A return value of FALSE indicates an error.
510
run_test_size_modes(int wpconfig_flags,int test_flags,int base_minutes)511 static int run_test_size_modes (int wpconfig_flags, int test_flags, int base_minutes)
512 {
513 int res;
514
515 printf ("\n *** 8-bit, mono ***\n");
516 res = run_test_speed_modes (wpconfig_flags, test_flags, 8, 1, base_minutes*5*60);
517 if (res) return res;
518
519 if (test_flags & TEST_FLAG_EXHAUSTIVE) {
520 printf ("\n *** 16-bit, mono ***\n");
521 res = run_test_speed_modes (wpconfig_flags, test_flags, 16, 1, base_minutes*5*60);
522 if (res) return res;
523 }
524
525 printf ("\n *** 16-bit, stereo ***\n");
526 res = run_test_speed_modes (wpconfig_flags, test_flags, 16, 2, base_minutes*3*60);
527 if (res) return res;
528
529 if ((test_flags & TEST_FLAG_EXHAUSTIVE) && !(test_flags & TEST_FLAG_NO_FLOATS)) {
530 printf ("\n *** 16-bit (converted to float), stereo ***\n");
531 res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_FLOAT_DATA, 16, 2, base_minutes*3*60);
532 if (res) return res;
533 }
534
535 printf ("\n *** 24-bit, 5.1 channels ***\n");
536 res = run_test_speed_modes (wpconfig_flags, test_flags, 24, 6, base_minutes*60);
537 if (res) return res;
538
539 if (test_flags & TEST_FLAG_EXHAUSTIVE) {
540 if (!(test_flags & TEST_FLAG_NO_FLOATS)) {
541 printf ("\n *** 24-bit (converted to float), 5.1 channels ***\n");
542 res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_FLOAT_DATA, 24, 6, base_minutes*60);
543 if (res) return res;
544 }
545
546 printf ("\n *** 32-bit integer, 5.1 channels ***\n");
547 res = run_test_speed_modes (wpconfig_flags, test_flags, 32, 6, base_minutes*60);
548 if (res) return res;
549
550 if (!(test_flags & TEST_FLAG_NO_FLOATS)) {
551 printf ("\n *** 32-bit float stored as integer (pathological), 5.1 channels ***\n");
552 res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_STORE_FLOAT_AS_INT32, 32, 6, base_minutes*60);
553 if (res) return res;
554
555 if (!(wpconfig_flags & CONFIG_HYBRID_FLAG)) {
556 printf ("\n *** 32-bit integer stored as float (pathological), 5.1 channels ***\n");
557 res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_STORE_INT32_AS_FLOAT, 32, 6, base_minutes*60);
558 if (res) return res;
559 }
560 }
561 }
562
563 if (!(test_flags & TEST_FLAG_NO_FLOATS)) {
564 printf ("\n *** 32-bit float, 5.1 channels ***\n");
565 res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_FLOAT_DATA, 32, 6, base_minutes*60);
566 if (res) return res;
567 }
568
569 return 0;
570 }
571
572 // Given a WavPack configuration and test flags, run the various combinations of
573 // speed modes (i.e, fast, high, etc). A return value of FALSE indicates an error.
574
run_test_speed_modes(int wpconfig_flags,int test_flags,int bits,int num_chans,int num_seconds)575 static int run_test_speed_modes (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds)
576 {
577 int res;
578
579 if (!(test_flags & TEST_FLAG_NO_SPEEDS)) {
580 res = run_test_extra_modes (wpconfig_flags | CONFIG_FAST_FLAG, test_flags, bits, num_chans, num_seconds);
581 if (res) return res;
582 }
583
584 res = run_test_extra_modes (wpconfig_flags, test_flags, bits, num_chans, num_seconds);
585 if (res) return res;
586
587 if (!(test_flags & TEST_FLAG_NO_SPEEDS)) {
588 res = run_test_extra_modes (wpconfig_flags | CONFIG_HIGH_FLAG, test_flags, bits, num_chans, num_seconds);
589 if (res) return res;
590 }
591
592 if (!(test_flags & TEST_FLAG_NO_SPEEDS)) {
593 res = run_test_extra_modes (wpconfig_flags | CONFIG_VERY_HIGH_FLAG, test_flags, bits, num_chans, num_seconds);
594 if (res) return res;
595 }
596
597 return 0;
598 }
599
600 // Given a WavPack configuration and test flags, run the various combinations of "extra" modes (0-6).
601 // Note that except for the base mode (no extra), the "default" and "exhaustive" configurations do
602 // different extra modes. Combining the "default" and "exhaustive" configurations does all the extra
603 // modes. A return value of FALSE indicates an error.
604
run_test_extra_modes(int wpconfig_flags,int test_flags,int bits,int num_chans,int num_seconds)605 static int run_test_extra_modes (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds)
606 {
607 int res;
608
609 res = run_test (wpconfig_flags, test_flags, bits, num_chans, num_seconds);
610 if (res) return res;
611
612 if (test_flags & TEST_FLAG_NO_EXTRAS)
613 return 0;
614
615 if (test_flags & TEST_FLAG_EXHAUSTIVE) {
616 res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (1), bits, num_chans, num_seconds);
617 if (res) return res;
618 }
619
620 if (test_flags & TEST_FLAG_DEFAULT) {
621 res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (2), bits, num_chans, num_seconds);
622 if (res) return res;
623 }
624
625 if (test_flags & TEST_FLAG_EXHAUSTIVE) {
626 res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (3), bits, num_chans, num_seconds);
627 if (res) return res;
628 }
629
630 if (test_flags & TEST_FLAG_EXHAUSTIVE) {
631 res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (4), bits, num_chans, num_seconds);
632 if (res) return res;
633 }
634
635 if (test_flags & TEST_FLAG_DEFAULT) {
636 res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (5), bits, num_chans, num_seconds);
637 if (res) return res;
638 }
639
640 if (test_flags & TEST_FLAG_EXHAUSTIVE) {
641 res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (6), bits, num_chans, num_seconds);
642 if (res) return res;
643 }
644
645 return 0;
646 }
647
648 // Given a WavPack configuration and test flags, actually run the specified test. This entails
649 // generating the actual audio test data, creating the "virtual" WavPack file and writing to it,
650 // and spawning the thread that will read the "virtual" file and do the decoding (which is obviously
651 // required to verify the entire encode/decode chain). For lossless modes, and MD5 hash is used to
652 // verify the result, otherwise the decoder is trusted to detect and report errors (although the
653 // total number of samples is verified).
654
655 #define BUFFER_SIZE 1000000
656 #define NUM_GENERATORS 6
657
658 struct audio_channel {
659 float audio_gain_hist [NUM_GENERATORS], audio_gain [NUM_GENERATORS], angle_offset;
660 int lfe_flag;
661 };
662
663 #define SAMPLE_RATE 44100
664 #define ENCODE_SAMPLES 128
665 #define NOISE_GAIN 0.6667
666 #define TONE_GAIN 0.3333
667
run_test(int wpconfig_flags,int test_flags,int bits,int num_chans,int num_seconds)668 static int run_test (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds)
669 {
670 static int test_number;
671
672 float sequencing_angle = 0.0, speed = 60.0, width = 200.0, *source, *destin, ratio, bps;
673 int lossless = !(wpconfig_flags & CONFIG_HYBRID_FLAG) || ((wpconfig_flags & CONFIG_CREATE_WVC) && !(test_flags & TEST_FLAG_IGNORE_WVC));
674 char md5_string1 [] = "????????????????????????????????";
675 char md5_string2 [] = "????????????????????????????????";
676 uint32_t total_encoded_bytes, total_encoded_samples;
677 struct audio_generator generators [NUM_GENERATORS];
678 int seconds = 0, samples = 0, wc = 0, chan_mask;
679 char *filename = NULL, mode_string [32] = "-";
680 struct audio_channel *channels;
681 pthread_t pthread;
682 WavpackContext *out_wpc;
683 WavpackConfig wpconfig;
684 StreamingFile wv_stream, wvc_stream;
685 WavpackDecoder wv_decoder;
686 unsigned char md5_encoded [16];
687 MD5_CTX md5_context;
688 void *term_value;
689 int i, j, k;
690
691 if (wpconfig_flags & CONFIG_FAST_FLAG)
692 strcat (mode_string, "f");
693 else if (wpconfig_flags & CONFIG_HIGH_FLAG)
694 strcat (mode_string, "h");
695 else if (wpconfig_flags & CONFIG_VERY_HIGH_FLAG)
696 strcat (mode_string, "hh");
697
698 printf ("test %04d...", ++test_number); fflush (stdout);
699 MD5_Init (&md5_context);
700
701 noise_generator_init (&generators [0], 128.0);
702 tone_generator_init (&generators [1], SAMPLE_RATE, 20, 200);
703 noise_generator_init (&generators [2], 12.0);
704 tone_generator_init (&generators [3], SAMPLE_RATE, 200, 2000);
705 noise_generator_init (&generators [4], 1.75);
706 tone_generator_init (&generators [5], SAMPLE_RATE, 2000, 20000);
707
708 CLEAR (wpconfig);
709 CLEAR (wv_decoder);
710 CLEAR (wv_stream);
711 CLEAR (wvc_stream);
712
713 channels = malloc (num_chans * sizeof (*channels));
714 source = malloc (ENCODE_SAMPLES * sizeof (*source));
715 destin = malloc (ENCODE_SAMPLES * num_chans * sizeof (*destin));
716
717 if (!channels || !source || !destin) {
718 printf ("run_test(): can't allocate memory!\n");
719 exit (-1);
720 }
721
722 memset (channels, 0, num_chans * sizeof (*channels));
723
724 switch (num_chans) {
725 case 1:
726 channels [0].angle_offset = 0.0;
727 chan_mask = 0x4;
728 break;
729
730 case 2:
731 channels [0].angle_offset -= M_PI / 24.0;
732 channels [1].angle_offset += M_PI / 24.0;
733 chan_mask = 0x3;
734 break;
735
736 case 4:
737 channels [0].angle_offset -= M_PI / 24.0;
738 channels [1].angle_offset += M_PI / 24.0;
739 channels [2].angle_offset -= 23.0 * M_PI / 24.0;
740 channels [3].angle_offset += 23.0 * M_PI / 24.0;
741 chan_mask = 0x33;
742 break;
743
744 case 6:
745 channels [0].angle_offset -= M_PI / 24.0;
746 channels [1].angle_offset += M_PI / 24.0;
747 channels [3].lfe_flag = 1;
748 channels [4].angle_offset -= 23.0 * M_PI / 24.0;
749 channels [5].angle_offset += 23.0 * M_PI / 24.0;
750 chan_mask = 0x3F;
751 break;
752
753 default:
754 printf ("invalid channel count = %d\n", num_chans);
755 exit (-1);
756 }
757
758 if (!(test_flags & TEST_FLAG_NO_DECODE)) {
759 initialize_stream (&wv_stream, BUFFER_SIZE);
760 wv_decoder.wv_stream = &wv_stream;
761 }
762 else
763 initialize_stream (&wv_stream, 0);
764
765 if (test_flags & TEST_FLAG_WRITE_FILE) {
766 int i;
767
768 for (i = 0; i < number_of_ranges; ++i)
769 if (test_number >= write_ranges [i].start && test_number <= write_ranges [i].stop) {
770 filename = malloc (32);
771
772 if (!filename) {
773 printf ("run_test(): can't allocate memory!\n");
774 exit (-1);
775 }
776
777 sprintf (filename, "testfile-%04d.wv", test_number);
778
779 if (((wv_stream.file = fopen (filename, "w+b")) == NULL)) {
780 printf ("can't create file %s!\n", filename);
781 free_stream (&wv_stream);
782 return 1;
783 }
784
785 break;
786 }
787 }
788
789 if (wpconfig_flags & CONFIG_CREATE_WVC) {
790 if (!(test_flags & (TEST_FLAG_IGNORE_WVC | TEST_FLAG_NO_DECODE))) {
791 initialize_stream (&wvc_stream, BUFFER_SIZE);
792 wv_decoder.wvc_stream = &wvc_stream;
793 }
794 else
795 initialize_stream (&wvc_stream, 0);
796
797 if (filename) {
798 char *filename_c = malloc (strlen (filename) + 10);
799
800 strcpy (filename_c, filename);
801 strcat (filename_c, "c");
802
803 if ((wvc_stream.file = fopen (filename_c, "w+b")) == NULL) {
804 printf ("can't create file %s!\n", filename_c);
805 free_stream (&wv_stream);
806 free_stream (&wvc_stream);
807 return 1;
808 }
809
810 free (filename_c);
811 }
812 }
813
814 out_wpc = WavpackOpenFileOutput (write_block, &wv_stream, (wpconfig_flags & CONFIG_CREATE_WVC) ? &wvc_stream : NULL);
815
816 if (!(test_flags & TEST_FLAG_NO_DECODE))
817 pthread_create (&pthread, NULL, decode_thread, (void *) &wv_decoder);
818
819 if (test_flags & (TEST_FLAG_FLOAT_DATA | TEST_FLAG_STORE_INT32_AS_FLOAT)) {
820 wpconfig.float_norm_exp = 127;
821 wpconfig.bytes_per_sample = 4;
822 wpconfig.bits_per_sample = 32;
823 }
824 else {
825 wpconfig.bytes_per_sample = (bits + 7) >> 3;
826 wpconfig.bits_per_sample = bits;
827 }
828
829 if (test_flags & TEST_FLAG_EXTRA_MASK) {
830 sprintf (mode_string + strlen (mode_string), "x%c", '0' + (test_flags & TEST_FLAG_EXTRA_MASK));
831 wpconfig.xmode = test_flags & TEST_FLAG_EXTRA_MASK;
832 wpconfig_flags |= CONFIG_EXTRA_MODE;
833 }
834
835 wpconfig.sample_rate = SAMPLE_RATE;
836 wpconfig.num_channels = num_chans;
837 wpconfig.channel_mask = chan_mask;
838 wpconfig.flags = wpconfig_flags;
839
840 if (wpconfig_flags & CONFIG_HYBRID_FLAG) {
841 if (wpconfig_flags & CONFIG_CREATE_WVC) {
842 if (test_flags & TEST_FLAG_IGNORE_WVC) {
843 strcat (mode_string, "b4c");
844 wpconfig.bitrate = 4.0;
845 }
846 else {
847 strcat (mode_string, "b3c");
848 wpconfig.bitrate = 3.0;
849 }
850 }
851 else {
852 strcat (mode_string, "b5");
853 wpconfig.bitrate = 5.0;
854 }
855 }
856
857 WavpackSetConfiguration64 (out_wpc, &wpconfig, -1, NULL);
858 WavpackPackInit (out_wpc);
859
860 while (seconds < num_seconds) {
861
862 double translated_angle = cos (sequencing_angle) * 100.0;
863 double width_scalar = pow (2.0, -width);
864
865 for (k = 0; k < num_chans; ++k) {
866 channels [k].audio_gain [0] = pow (sin (translated_angle + channels [k].angle_offset - M_PI * 1.6667) + 1.0, width) * width_scalar * NOISE_GAIN;
867 channels [k].audio_gain [1] = pow (sin (translated_angle + channels [k].angle_offset - M_PI * 0.6667) + 1.0, width) * width_scalar * TONE_GAIN;
868 channels [k].audio_gain [2] = pow (sin (translated_angle + channels [k].angle_offset - M_PI * 0.3333) + 1.0, width) * width_scalar * NOISE_GAIN;
869 channels [k].audio_gain [3] = pow (sin (translated_angle + channels [k].angle_offset - M_PI * 1.3333) + 1.0, width) * width_scalar * TONE_GAIN;
870 channels [k].audio_gain [4] = pow (sin (translated_angle + channels [k].angle_offset - M_PI) + 1.0, width) * width_scalar * NOISE_GAIN;
871 channels [k].audio_gain [5] = pow (sin (translated_angle + channels [k].angle_offset) + 1.0, width) * width_scalar * TONE_GAIN;
872 }
873
874 memset (destin, 0, ENCODE_SAMPLES * num_chans * sizeof (*destin));
875
876 for (j = 0; j < NUM_GENERATORS; ++j) {
877 audio_generator_run (&generators [j], source, ENCODE_SAMPLES);
878
879 for (k = 0; k < num_chans; ++k) {
880 if (!channels [k].lfe_flag || j < 2)
881 mix_samples_with_gain (destin + k, source, ENCODE_SAMPLES, num_chans, channels [k].audio_gain_hist [j], channels [k].audio_gain [j]);
882
883 channels [k].audio_gain_hist [j] = channels [k].audio_gain [j];
884 }
885 }
886
887 if (test_flags & TEST_FLAG_FLOAT_DATA) {
888 if (bits <= 25)
889 truncate_float_samples (destin, ENCODE_SAMPLES * num_chans, bits);
890 else if (bits != 32) {
891 printf ("invalid bits configuration\n");
892 exit (-1);
893 }
894 }
895 else if (!(test_flags & TEST_FLAG_STORE_FLOAT_AS_INT32)) {
896 if (bits < 32)
897 float_to_integer_samples (destin, ENCODE_SAMPLES * num_chans, bits);
898 else if (bits == 32)
899 float_to_32bit_integer_samples (destin, ENCODE_SAMPLES * num_chans);
900 else {
901 printf ("invalid bits configuration\n");
902 exit (-1);
903 }
904 }
905
906 WavpackPackSamples (out_wpc, (int32_t *) destin, ENCODE_SAMPLES);
907 store_samples (destin, (int32_t *) destin, 0, wpconfig.bytes_per_sample, ENCODE_SAMPLES * num_chans);
908 MD5_Update (&md5_context, (unsigned char *) destin, wpconfig.bytes_per_sample * ENCODE_SAMPLES * num_chans);
909
910 sequencing_angle += 2.0 * M_PI / SAMPLE_RATE / speed * ENCODE_SAMPLES;
911 if (sequencing_angle > M_PI) sequencing_angle -= M_PI * 2.0;
912
913 if ((samples += ENCODE_SAMPLES) >= SAMPLE_RATE) {
914 samples -= SAMPLE_RATE;
915 ++seconds;
916
917 if (!(wc & 1)) {
918 if (width > 1.0) width *= 0.875;
919 else if (width > 0.125) width -= 0.125;
920 else {
921 width = 0.0;
922 wc++;
923 }
924 }
925 else {
926 if (width < 1.0) width += 0.125;
927 else if (width < 200.0) width *= 1.125;
928 else wc++;
929 }
930 }
931 }
932
933 WavpackFlushSamples (out_wpc);
934 MD5_Final (md5_encoded, &md5_context);
935
936 if (wpconfig.flags & CONFIG_MD5_CHECKSUM) {
937 WavpackStoreMD5Sum (out_wpc, md5_encoded);
938 WavpackFlushSamples (out_wpc);
939 }
940
941 WavpackCloseFile (out_wpc);
942
943 free (channels);
944 free (source);
945 free (destin);
946
947 if ((wpconfig_flags & CONFIG_CREATE_WVC) && !(test_flags & TEST_FLAG_IGNORE_WVC))
948 total_encoded_bytes = wv_stream.bytes_written + wvc_stream.bytes_written;
949 else
950 total_encoded_bytes = wv_stream.bytes_written;
951
952 total_encoded_samples = seconds * SAMPLE_RATE + samples;
953 ratio = total_encoded_bytes / ((float) total_encoded_samples * wpconfig.bytes_per_sample * num_chans);
954 bps = total_encoded_bytes * 8 / ((float) total_encoded_samples * num_chans);
955
956 flush_stream (&wv_stream);
957 flush_stream (&wvc_stream);
958
959 if (!(test_flags & TEST_FLAG_NO_DECODE)) {
960 pthread_join (pthread, &term_value);
961
962 if (term_value) {
963 printf ("decode_thread() returned error %d\n", (int) (long) term_value);
964 return 1;
965 }
966 }
967
968 if (!(test_flags & TEST_FLAG_NO_DECODE)) {
969 for (i = 0; i < 16; ++i) {
970 sprintf (md5_string1 + (i * 2), "%02x", md5_encoded [i]);
971 sprintf (md5_string2 + (i * 2), "%02x", wv_decoder.md5_decoded [i]);
972 }
973
974 if (wv_decoder.num_errors || wv_decoder.sample_count != total_encoded_samples ||
975 (lossless && memcmp (md5_encoded, wv_decoder.md5_decoded, sizeof (md5_encoded)))) {
976 printf ("\n---------------------------------------------\n");
977 printf ("enc/dec sample count: %u / %u\n", total_encoded_samples, wv_decoder.sample_count);
978 printf ("encoded md5: %s\n", md5_string1);
979 printf ("decoded md5: %s\n", md5_string2);
980 printf ("reported decode errors: %d\n", wv_decoder.num_errors);
981 printf ("---------------------------------------------\n");
982 return wv_decoder.num_errors + 1;
983 }
984 }
985
986 free_stream (&wv_stream);
987 free_stream (&wvc_stream);
988
989 printf ("pass (%8s, %.2f%%, %.2f bps, %s)\n", mode_string, 100.0 - ratio * 100.0, bps, md5_string2);
990
991 return 0;
992 }
993
994 // Thread / function that opens a virtual WavPack file, decodes it and calculates the MD5 hash of the
995 // decoded audio data.
996
997 #define DECODE_SAMPLES 1000
998
decode_thread(void * threadid)999 static void *decode_thread (void *threadid)
1000 {
1001 WavpackDecoder *wd = (WavpackDecoder *) threadid;
1002 char error [80];
1003 WavpackContext *wpc = WavpackOpenFileInputEx (&freader, wd->wv_stream, wd->wvc_stream, error, 0, 0);
1004 int32_t *decoded_samples, num_chans, bps;
1005 MD5_CTX md5_context;
1006
1007 if (!wpc) {
1008 printf ("decode_thread(): error \"%s\" opening input file\n", error);
1009 wd->num_errors = 1;
1010 pthread_exit (NULL);
1011 }
1012
1013 MD5_Init (&md5_context);
1014 num_chans = WavpackGetNumChannels (wpc);
1015 bps = WavpackGetBytesPerSample (wpc);
1016
1017 decoded_samples = malloc (sizeof (int32_t) * DECODE_SAMPLES * num_chans);
1018
1019 if (!decoded_samples) {
1020 printf ("decode_thread(): can't allocate memory!\n");
1021 exit (-1);
1022 }
1023
1024 while (1) {
1025 int samples = WavpackUnpackSamples (wpc, decoded_samples, DECODE_SAMPLES);
1026
1027 if (!samples)
1028 break;
1029
1030 store_samples (decoded_samples, decoded_samples, 0, bps, samples * num_chans);
1031 MD5_Update (&md5_context, (unsigned char *) decoded_samples, bps * samples * num_chans);
1032 wd->sample_count += samples;
1033 }
1034
1035 MD5_Final (wd->md5_decoded, &md5_context);
1036 wd->num_errors = WavpackGetNumErrors (wpc);
1037 free (decoded_samples);
1038 WavpackCloseFile (wpc);
1039 pthread_exit (NULL);
1040 return NULL;
1041 }
1042
1043 // This code implements a simple virtual "file" so that we can have a WavPack encoding process and
1044 // a WavPack decoding process running at the same time (using Pthreads).
1045
write_block(void * id,void * data,int32_t length)1046 static int write_block (void *id, void *data, int32_t length)
1047 {
1048 StreamingFile *ws = (StreamingFile *) id;
1049 unsigned char *data_ptr = data;
1050
1051 if (!ws || !data || !length)
1052 return 0;
1053
1054 // if (frandom() < .0001)
1055 // ((char *) data) [(int) floor (length * frandom())] ^= 1;
1056
1057 if (!ws->first_block_size)
1058 ws->first_block_size = length;
1059
1060 ws->bytes_written += length;
1061
1062 if (ws->file && !ws->error) {
1063 if (!fwrite (data, 1, length, ws->file)) {
1064 ws->error = 1;
1065 fclose (ws->file);
1066 ws->file = NULL;
1067 }
1068 }
1069
1070 if (!ws->buffer_size) // if no buffer, just swallow data silently
1071 return 1;
1072
1073 pthread_mutex_lock (&ws->mutex);
1074
1075 while (length) {
1076 int32_t bytes_available = ws->buffer_tail - ws->buffer_head - 1;
1077 int32_t bytes_to_copy = length;
1078
1079 if (bytes_available < 0)
1080 bytes_available += ws->buffer_size;
1081
1082 if (bytes_available < bytes_to_copy)
1083 bytes_to_copy = bytes_available;
1084
1085 if (ws->buffer_head + bytes_to_copy > ws->buffer_base + ws->buffer_size)
1086 bytes_to_copy = ws->buffer_base + ws->buffer_size - ws->buffer_head;
1087
1088 if (!bytes_to_copy) {
1089 ws->full_waits++;
1090 pthread_cond_wait (&ws->cond_read, &ws->mutex);
1091 continue;
1092 }
1093
1094 memcpy ((void *) ws->buffer_head, data_ptr, bytes_to_copy);
1095
1096 if ((ws->buffer_head += bytes_to_copy) == ws->buffer_base + ws->buffer_size)
1097 ws->buffer_head = ws->buffer_base;
1098
1099 data_ptr += bytes_to_copy;
1100 length -= bytes_to_copy;
1101 }
1102
1103 pthread_cond_signal (&ws->cond_write);
1104 pthread_mutex_unlock (&ws->mutex);
1105
1106 return 1;
1107 }
1108
read_bytes(void * id,void * data,int32_t bcount)1109 static int32_t read_bytes (void *id, void *data, int32_t bcount)
1110 {
1111 StreamingFile *ws = (StreamingFile *) id;
1112 unsigned char *data_ptr = data;
1113
1114 pthread_mutex_lock (&ws->mutex);
1115
1116 while (bcount) {
1117 if (ws->push_back) {
1118 *data_ptr++ = ws->push_back;
1119 ws->push_back = 0;
1120 bcount--;
1121 }
1122 else if (ws->buffer_head != ws->buffer_tail) {
1123 int bytes_available = ws->buffer_head - ws->buffer_tail;
1124 int32_t bytes_to_copy = bcount;
1125
1126 if (bytes_available < 0)
1127 bytes_available += ws->buffer_size;
1128
1129 if (bytes_available < bytes_to_copy)
1130 bytes_to_copy = bytes_available;
1131
1132 if (ws->buffer_tail + bytes_to_copy > ws->buffer_base + ws->buffer_size)
1133 bytes_to_copy = ws->buffer_base + ws->buffer_size - ws->buffer_tail;
1134
1135 memcpy (data_ptr, (void *) ws->buffer_tail, bytes_to_copy);
1136
1137 if ((ws->buffer_tail += bytes_to_copy) == ws->buffer_base + ws->buffer_size)
1138 ws->buffer_tail = ws->buffer_base;
1139
1140 ws->bytes_read += bytes_to_copy;
1141 data_ptr += bytes_to_copy;
1142 bcount -= bytes_to_copy;
1143 }
1144 else if (ws->done)
1145 break;
1146 else {
1147 ws->empty_waits++;
1148 pthread_cond_wait (&ws->cond_write, &ws->mutex);
1149 }
1150 }
1151
1152 pthread_cond_signal (&ws->cond_read);
1153 pthread_mutex_unlock (&ws->mutex);
1154
1155 return data_ptr - (unsigned char *) data;
1156 }
1157
get_pos(void * id)1158 static uint32_t get_pos (void *id)
1159 {
1160 return -1;
1161 }
1162
set_pos_abs(void * id,uint32_t pos)1163 static int set_pos_abs (void *id, uint32_t pos)
1164 {
1165 return 0;
1166 }
1167
set_pos_rel(void * id,int32_t delta,int mode)1168 static int set_pos_rel (void *id, int32_t delta, int mode)
1169 {
1170 return -1;
1171 }
1172
push_back_byte(void * id,int c)1173 static int push_back_byte (void *id, int c)
1174 {
1175 StreamingFile *ws = (StreamingFile *) id;
1176
1177 if (!ws->push_back)
1178 return ws->push_back = c;
1179 else
1180 return EOF;
1181 }
1182
get_length(void * id)1183 static uint32_t get_length (void *id)
1184 {
1185 return 0;
1186 }
1187
can_seek(void * id)1188 static int can_seek (void *id)
1189 {
1190 return 0;
1191 }
1192
1193 static WavpackStreamReader freader = {
1194 read_bytes, get_pos, set_pos_abs, set_pos_rel, push_back_byte, get_length, can_seek,
1195 };
1196
initialize_stream(StreamingFile * ws,int buffer_size)1197 static void initialize_stream (StreamingFile *ws, int buffer_size)
1198 {
1199 if (buffer_size) {
1200 ws->buffer_base = malloc (ws->buffer_size = buffer_size);
1201 ws->buffer_head = ws->buffer_tail = ws->buffer_base;
1202 pthread_cond_init (&ws->cond_write, NULL);
1203 pthread_cond_init (&ws->cond_read, NULL);
1204 pthread_mutex_init (&ws->mutex, NULL);
1205 }
1206 }
1207
flush_stream(StreamingFile * ws)1208 static void flush_stream (StreamingFile *ws)
1209 {
1210 if (ws->buffer_base) {
1211 pthread_mutex_lock (&ws->mutex);
1212 ws->done = 1;
1213 pthread_cond_signal (&ws->cond_write);
1214 pthread_mutex_unlock (&ws->mutex);
1215 }
1216 }
1217
free_stream(StreamingFile * ws)1218 static void free_stream (StreamingFile *ws)
1219 {
1220 if (ws->file) {
1221 fclose (ws->file);
1222 ws->file = NULL;
1223 }
1224
1225 if (ws->buffer_base) {
1226 free ((void *) ws->buffer_base);
1227 ws->buffer_base = NULL;
1228 }
1229 }
1230
1231 // Helper utilities for generating the audio used for testing.
1232
1233 // Return a random value in the range: 0.0 <= n < 1.0
1234
frandom(void)1235 static double frandom (void)
1236 {
1237 static uint64_t random = 0x3141592653589793;
1238 random = ((random << 4) - random) ^ 1;
1239 random = ((random << 4) - random) ^ 1;
1240 random = ((random << 4) - random) ^ 1;
1241 return (random >> 32) / 4294967296.0;
1242 }
1243
tone_generator_init(struct audio_generator * cxt,int sample_rate,int low_freq,int high_freq)1244 static void tone_generator_init (struct audio_generator *cxt, int sample_rate, int low_freq, int high_freq)
1245 {
1246 struct tone_generator *tone_cxt = &cxt->u.tone_cxt;
1247
1248 memset (cxt, 0, sizeof (*cxt));
1249 cxt->type = tone;
1250
1251 tone_cxt->sample_rate = sample_rate;
1252 tone_cxt->high_frequency = high_freq;
1253 tone_cxt->low_frequency = low_freq;
1254 tone_cxt->samples_per_update = sample_rate / low_freq * 4;
1255 }
1256
tone_generator_run(struct tone_generator * cxt,float * samples,int num_samples)1257 static void tone_generator_run (struct tone_generator *cxt, float *samples, int num_samples)
1258 {
1259 float target_frequency, target_velocity;
1260
1261 while (num_samples--) {
1262 if (!cxt->samples_left) {
1263 cxt->samples_left = cxt->samples_per_update;
1264
1265 target_frequency = cxt->low_frequency * pow (cxt->high_frequency / cxt->low_frequency, frandom ());
1266 target_velocity = (M_PI * 2.0) / ((float) cxt->sample_rate / target_frequency);
1267 cxt->acceleration = (target_velocity - cxt->velocity) / cxt->samples_left;
1268 }
1269
1270 *samples++ = sin (cxt->angle += cxt->velocity += cxt->acceleration);
1271 if (cxt->angle > M_PI) cxt->angle -= M_PI * 2.0;
1272 cxt->samples_left--;
1273 }
1274 }
1275
noise_generator_init(struct audio_generator * cxt,float factor)1276 static void noise_generator_init (struct audio_generator *cxt, float factor)
1277 {
1278 struct noise_generator *noise_cxt = &cxt->u.noise_cxt;
1279
1280 memset (cxt, 0, sizeof (*cxt));
1281 cxt->type = noise;
1282
1283 noise_cxt->scalar = factor * factor * factor * sqrt (factor) / (2.0 + factor * factor);
1284 noise_cxt->factor = factor;
1285 }
1286
noise_generator_run(struct noise_generator * cxt,float * samples,int num_samples)1287 static void noise_generator_run (struct noise_generator *cxt, float *samples, int num_samples)
1288 {
1289 while (num_samples--) {
1290 float source = (frandom () - 0.5) * cxt->scalar;
1291 cxt->sum1 += (source - cxt->sum1) / cxt->factor;
1292 cxt->sum2 += (cxt->sum1 - cxt->sum2) / cxt->factor;
1293 *samples++ = cxt->sum2 - cxt->sum2p;
1294 cxt->sum2p = cxt->sum2;
1295 }
1296 }
1297
audio_generator_run(struct audio_generator * cxt,float * samples,int num_samples)1298 static void audio_generator_run (struct audio_generator *cxt, float *samples, int num_samples)
1299 {
1300 switch (cxt->type) {
1301 case noise:
1302 noise_generator_run (&cxt->u.noise_cxt, samples, num_samples);
1303 break;
1304
1305 case tone:
1306 tone_generator_run (&cxt->u.tone_cxt, samples, num_samples);
1307 break;
1308
1309 default:
1310 printf ("bad audio generator type!\n");
1311 exit (-1);
1312 }
1313 }
1314
mix_samples_with_gain(float * destin,float * source,int num_samples,int num_chans,float initial_gain,float final_gain)1315 static void mix_samples_with_gain (float *destin, float *source, int num_samples, int num_chans, float initial_gain, float final_gain)
1316 {
1317 float delta_gain = (final_gain - initial_gain) / num_samples;
1318 float gain = initial_gain - delta_gain;
1319
1320 while (num_samples--) {
1321 *destin += *source++ * (gain += delta_gain);
1322 destin += num_chans;
1323 }
1324 }
1325
truncate_float_samples(float * samples,int num_samples,int bits)1326 static void truncate_float_samples (float *samples, int num_samples, int bits)
1327 {
1328 int isample, imin = -(1 << (bits - 1)), imax = (1 << (bits - 1)) - 1;
1329 float scalar = (float) (1 << (bits - 1));
1330
1331 while (num_samples--) {
1332 if (*samples >= 1.0)
1333 isample = imax;
1334 else if (*samples <= -1.0)
1335 isample = imin;
1336 else
1337 isample = floor (*samples * scalar);
1338
1339 *samples++ = isample / scalar;
1340 }
1341 }
1342
float_to_integer_samples(float * samples,int num_samples,int bits)1343 static void float_to_integer_samples (float *samples, int num_samples, int bits)
1344 {
1345 int isample, imin = -(1 << (bits - 1)), imax = (1 << (bits - 1)) - 1;
1346 float scalar = (float) (1 << (bits - 1));
1347 int ishift = (8 - (bits & 0x7)) & 0x7;
1348
1349 while (num_samples--) {
1350 if (*samples >= 1.0)
1351 isample = imax;
1352 else if (*samples <= -1.0)
1353 isample = imin;
1354 else
1355 isample = floor (*samples * scalar);
1356
1357 *(int32_t *)samples = (uint32_t) isample << ishift;
1358 samples++;
1359 }
1360 }
1361
float_to_32bit_integer_samples(float * samples,int num_samples)1362 static void float_to_32bit_integer_samples (float *samples, int num_samples)
1363 {
1364 int isample, imin = 0x8000000, imax = 0x7fffffff;
1365 float scalar = 2147483648.0;
1366
1367 while (num_samples--) {
1368 if (*samples >= 1.0)
1369 isample = imax;
1370 else if (*samples <= -1.0)
1371 isample = imin;
1372 else
1373 isample = floor (*samples * scalar);
1374
1375 // if there are trailing zeros, fill them in with random data
1376
1377 if (isample && !(isample & 1)) {
1378 int tzeros = 1;
1379
1380 while (!((isample >>= 1) & 1))
1381 tzeros++;
1382
1383 while (tzeros--)
1384 isample = ((unsigned int) isample << 1) + ((frandom() > 0.5) ? 1 : 0);
1385 }
1386
1387 *(int32_t *)samples = isample;
1388 samples++;
1389 }
1390 }
1391
1392 // Code to store samples. Source is an array of int32_t data (which is what WavPack uses
1393 // internally), but the destination can have from 1 to 4 bytes per sample. Also, the destination
1394 // data is assumed to be little-endian and signed, except for byte data which is unsigned (these
1395 // are WAV file defaults). The endian and signedness can be overridden with the qmode flags
1396 // to support other formats.
1397
1398 static void *store_little_endian_unsigned_samples (void *dst, int32_t *src, int bps, int count);
1399 static void *store_little_endian_signed_samples (void *dst, int32_t *src, int bps, int count);
1400 static void *store_big_endian_unsigned_samples (void *dst, int32_t *src, int bps, int count);
1401 static void *store_big_endian_signed_samples (void *dst, int32_t *src, int bps, int count);
1402
store_samples(void * dst,int32_t * src,int qmode,int bps,int count)1403 static void *store_samples (void *dst, int32_t *src, int qmode, int bps, int count)
1404 {
1405 if (qmode & QMODE_BIG_ENDIAN) {
1406 if ((qmode & QMODE_UNSIGNED_WORDS) || (bps == 1 && !(qmode & QMODE_SIGNED_BYTES)))
1407 return store_big_endian_unsigned_samples (dst, src, bps, count);
1408 else
1409 return store_big_endian_signed_samples (dst, src, bps, count);
1410 }
1411 else if ((qmode & QMODE_UNSIGNED_WORDS) || (bps == 1 && !(qmode & (QMODE_SIGNED_BYTES | QMODE_DSD_AUDIO))))
1412 return store_little_endian_unsigned_samples (dst, src, bps, count);
1413 else
1414 return store_little_endian_signed_samples (dst, src, bps, count);
1415 }
1416
store_little_endian_unsigned_samples(void * dst,int32_t * src,int bps,int count)1417 static void *store_little_endian_unsigned_samples (void *dst, int32_t *src, int bps, int count)
1418 {
1419 unsigned char *dptr = dst;
1420 int32_t temp;
1421
1422 switch (bps) {
1423
1424 case 1:
1425 while (count--)
1426 *dptr++ = *src++ + 0x80;
1427
1428 break;
1429
1430 case 2:
1431 while (count--) {
1432 *dptr++ = (unsigned char) (temp = *src++ + 0x8000);
1433 *dptr++ = (unsigned char) (temp >> 8);
1434 }
1435
1436 break;
1437
1438 case 3:
1439 while (count--) {
1440 *dptr++ = (unsigned char) (temp = *src++ + 0x800000);
1441 *dptr++ = (unsigned char) (temp >> 8);
1442 *dptr++ = (unsigned char) (temp >> 16);
1443 }
1444
1445 break;
1446
1447 case 4:
1448 while (count--) {
1449 *dptr++ = (unsigned char) (temp = *src++ + 0x80000000);
1450 *dptr++ = (unsigned char) (temp >> 8);
1451 *dptr++ = (unsigned char) (temp >> 16);
1452 *dptr++ = (unsigned char) (temp >> 24);
1453 }
1454
1455 break;
1456 }
1457
1458 return dptr;
1459 }
1460
store_little_endian_signed_samples(void * dst,int32_t * src,int bps,int count)1461 static void *store_little_endian_signed_samples (void *dst, int32_t *src, int bps, int count)
1462 {
1463 unsigned char *dptr = dst;
1464 int32_t temp;
1465
1466 switch (bps) {
1467
1468 case 1:
1469 while (count--)
1470 *dptr++ = *src++;
1471
1472 break;
1473
1474 case 2:
1475 while (count--) {
1476 *dptr++ = (unsigned char) (temp = *src++);
1477 *dptr++ = (unsigned char) (temp >> 8);
1478 }
1479
1480 break;
1481
1482 case 3:
1483 while (count--) {
1484 *dptr++ = (unsigned char) (temp = *src++);
1485 *dptr++ = (unsigned char) (temp >> 8);
1486 *dptr++ = (unsigned char) (temp >> 16);
1487 }
1488
1489 break;
1490
1491 case 4:
1492 while (count--) {
1493 *dptr++ = (unsigned char) (temp = *src++);
1494 *dptr++ = (unsigned char) (temp >> 8);
1495 *dptr++ = (unsigned char) (temp >> 16);
1496 *dptr++ = (unsigned char) (temp >> 24);
1497 }
1498
1499 break;
1500 }
1501
1502 return dptr;
1503 }
1504
store_big_endian_unsigned_samples(void * dst,int32_t * src,int bps,int count)1505 static void *store_big_endian_unsigned_samples (void *dst, int32_t *src, int bps, int count)
1506 {
1507 unsigned char *dptr = dst;
1508 int32_t temp;
1509
1510 switch (bps) {
1511
1512 case 1:
1513 while (count--)
1514 *dptr++ = *src++ + 0x80;
1515
1516 break;
1517
1518 case 2:
1519 while (count--) {
1520 *dptr++ = (unsigned char) ((temp = *src++ + 0x8000) >> 8);
1521 *dptr++ = (unsigned char) temp;
1522 }
1523
1524 break;
1525
1526 case 3:
1527 while (count--) {
1528 *dptr++ = (unsigned char) ((temp = *src++ + 0x800000) >> 16);
1529 *dptr++ = (unsigned char) (temp >> 8);
1530 *dptr++ = (unsigned char) temp;
1531 }
1532
1533 break;
1534
1535 case 4:
1536 while (count--) {
1537 *dptr++ = (unsigned char) ((temp = *src++ + 0x80000000) >> 24);
1538 *dptr++ = (unsigned char) (temp >> 16);
1539 *dptr++ = (unsigned char) (temp >> 8);
1540 *dptr++ = (unsigned char) temp;
1541 }
1542
1543 break;
1544 }
1545
1546 return dptr;
1547 }
1548
store_big_endian_signed_samples(void * dst,int32_t * src,int bps,int count)1549 static void *store_big_endian_signed_samples (void *dst, int32_t *src, int bps, int count)
1550 {
1551 unsigned char *dptr = dst;
1552 int32_t temp;
1553
1554 switch (bps) {
1555
1556 case 1:
1557 while (count--)
1558 *dptr++ = *src++;
1559
1560 break;
1561
1562 case 2:
1563 while (count--) {
1564 *dptr++ = (unsigned char) ((temp = *src++) >> 8);
1565 *dptr++ = (unsigned char) temp;
1566 }
1567
1568 break;
1569
1570 case 3:
1571 while (count--) {
1572 *dptr++ = (unsigned char) ((temp = *src++) >> 16);
1573 *dptr++ = (unsigned char) (temp >> 8);
1574 *dptr++ = (unsigned char) temp;
1575 }
1576
1577 break;
1578
1579 case 4:
1580 while (count--) {
1581 *dptr++ = (unsigned char) ((temp = *src++) >> 24);
1582 *dptr++ = (unsigned char) (temp >> 16);
1583 *dptr++ = (unsigned char) (temp >> 8);
1584 *dptr++ = (unsigned char) temp;
1585 }
1586
1587 break;
1588 }
1589
1590 return dptr;
1591 }
1592