//////////////////////////////////////////////////////////////////////////// // **** WAVPACK **** // // Hybrid Lossless Wavefile Compressor // // Copyright (c) 1998 - 2020 David Bryant. // // All Rights Reserved. // // Distributed under the BSD Software License (see license.txt) // //////////////////////////////////////////////////////////////////////////// // wvtest.c // This is the main module for the WavPack command-line library tester. #include #include #include #include #include #include #include "wavpack.h" #include "utils.h" // for PACKAGE_VERSION, etc. #include "md5.h" #define CLEAR(destin) memset (&destin, 0, sizeof (destin)); #ifndef M_PI #define M_PI 3.14159265358979323 #endif static const char *sign_on = "\n" " WVTEST libwavpack Tester/Exerciser for WavPack %s Version %s\n" " Copyright (c) 2020 David Bryant. All Rights Reserved.\n\n"; static const char *version_warning = "\n" " WARNING: WVTEST using libwavpack version %s, expected %s (see README)\n\n"; static const char *usage = " Usage: WVTEST --default|--exhaustive [-options]\n" " WVTEST --seektest[=n] file.wv [...] (n=runs per file, def=1)\n\n" " Options: --default = perform the default test suite\n" " --exhaustive = perform the exhaustive test suite\n" " --short = perform shorter runs of each test\n" " --long = perform longer runs of each test\n" " --no-decode = skip the decoding process\n" " --no-extras = skip the \"extra\" modes\n" " --no-hybrid = skip the hybrid modes\n" " --no-floats = skip the float modes\n" " --no-lossy = skip the lossy modes\n" " --no-speeds = skip the speed modes (fast, high, etc.)\n" " --help = display this message\n" " --version = write the version to stdout\n" " --write=n[-n][,...] = write specific test(s) (or range(s)) to disk\n\n" " Web: Visit www.wavpack.com for latest version and info\n"; #define TEST_FLAG_EXTRA_MODE(x) ((x) & TEST_FLAG_EXTRA_MASK) #define TEST_FLAG_EXTRA_MASK 0x7 #define TEST_FLAG_FLOAT_DATA 0x8 #define TEST_FLAG_WRITE_FILE 0x10 #define TEST_FLAG_DEFAULT 0x20 #define TEST_FLAG_EXHAUSTIVE 0x40 #define TEST_FLAG_NO_FLOATS 0x80 #define TEST_FLAG_NO_HYBRID 0x100 #define TEST_FLAG_NO_EXTRAS 0x200 #define TEST_FLAG_NO_LOSSY 0x400 #define TEST_FLAG_NO_SPEEDS 0x800 #define TEST_FLAG_STORE_FLOAT_AS_INT32 0x1000 #define TEST_FLAG_STORE_INT32_AS_FLOAT 0x2000 #define TEST_FLAG_IGNORE_WVC 0x4000 #define TEST_FLAG_NO_DECODE 0x8000 static int run_test_size_modes (int wpconfig_flags, int test_flags, int base_minutes); static int run_test_speed_modes (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds); static int run_test_extra_modes (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds); static int run_test (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds); #define NUM_WRITE_RANGES 10 static struct { int start, stop; } write_ranges [NUM_WRITE_RANGES]; int number_of_ranges; enum generator_type { noise, tone }; struct audio_generator { enum generator_type type; union { struct noise_generator { float sum1, sum2, sum2p; // these are changing float factor, scalar; // these are constant } noise_cxt; struct tone_generator { int sample_rate, samples_per_update; // these are constant int high_frequency, low_frequency; // these are constant float angle, velocity, acceleration; // these are changing int samples_left; } tone_cxt; } u; }; static int seeking_test (char *filename, uint32_t test_count); static void tone_generator_init (struct audio_generator *cxt, int sample_rate, int low_freq, int high_freq); static void noise_generator_init (struct audio_generator *cxt, float factor); static void audio_generator_run (struct audio_generator *cxt, float *samples, int num_samples); static void mix_samples_with_gain (float *destin, float *source, int num_samples, int num_chans, float initial_gain, float final_gain); static void truncate_float_samples (float *samples, int num_samples, int bits); static void float_to_integer_samples (float *samples, int num_samples, int bits); static void float_to_32bit_integer_samples (float *samples, int num_samples); static void *store_samples (void *dst, int32_t *src, int qmode, int bps, int count); static double frandom (void); typedef struct { uint32_t buffer_size, bytes_written, bytes_read, first_block_size; volatile unsigned char *buffer_base, *buffer_head, *buffer_tail; int push_back, done, error, empty_waits, full_waits; pthread_cond_t cond_read, cond_write; pthread_mutex_t mutex; FILE *file; } StreamingFile; typedef struct { StreamingFile *wv_stream, *wvc_stream; unsigned char md5_decoded [16]; uint32_t sample_count; int num_errors; } WavpackDecoder; static void initialize_stream (StreamingFile *ws, int buffer_size); static int write_block (void *id, void *data, int32_t length); static void flush_stream (StreamingFile *ws); static void free_stream (StreamingFile *ws); static void *decode_thread (void *threadid); static WavpackStreamReader freader; //////////////////////////////////////// main () function for CLI ////////////////////////////////////// int main (argc, argv) int argc; char **argv; { int wpconfig_flags = CONFIG_MD5_CHECKSUM | CONFIG_OPTIMIZE_MONO, test_flags = 0, base_minutes = 2, res = 0; int seektest = 0; // loop through command-line arguments while (--argc) { if (**++argv == '-' && (*argv)[1] == '-' && (*argv)[2]) { char *long_option = *argv + 2, *long_param = long_option; while (*long_param) if (*long_param++ == '=') break; if (!strcmp (long_option, "help")) { // --help printf ("%s", usage); return 0; } else if (!strcmp (long_option, "version")) { // --version printf ("wvtest %s\n", PACKAGE_VERSION); printf ("libwavpack %s\n", WavpackGetLibraryVersionString ()); return 0; } else if (!strcmp (long_option, "short")) { // --short base_minutes = 1; } else if (!strcmp (long_option, "long")) { // --long base_minutes = 5; } else if (!strcmp (long_option, "default")) { // --default test_flags |= TEST_FLAG_DEFAULT; } else if (!strcmp (long_option, "exhaustive")) { // --exhaustive test_flags |= TEST_FLAG_EXHAUSTIVE; } else if (!strcmp (long_option, "no-extras")) { // --no-extras test_flags |= TEST_FLAG_NO_EXTRAS; } else if (!strcmp (long_option, "no-hybrid")) { // --no-hybrid test_flags |= TEST_FLAG_NO_HYBRID; } else if (!strcmp (long_option, "no-lossy")) { // --no-lossy test_flags |= TEST_FLAG_NO_LOSSY; } else if (!strcmp (long_option, "no-speeds")) { // --no-speeds test_flags |= TEST_FLAG_NO_SPEEDS; } else if (!strcmp (long_option, "no-floats")) { // --no-floats test_flags |= TEST_FLAG_NO_FLOATS; } else if (!strcmp (long_option, "no-decode")) { // --no-decode test_flags |= TEST_FLAG_NO_DECODE; } else if (!strncmp (long_option, "write", 5)) { // --write for (number_of_ranges = 0; *long_param && isdigit (*long_param) && number_of_ranges < NUM_WRITE_RANGES;) { write_ranges [number_of_ranges].start = strtol (long_param, &long_param, 10); if (*long_param == '-') { long_param++; if (isdigit (*long_param)) write_ranges [number_of_ranges].stop = strtol (long_param, &long_param, 10); else break; } else write_ranges [number_of_ranges].stop = write_ranges [number_of_ranges].start; number_of_ranges++; if (*long_param == ',') long_param++; else break; } if (*long_param || !number_of_ranges) { printf ("syntax error in write specification!\n"); return 1; } else test_flags |= TEST_FLAG_WRITE_FILE; } else if (!strncmp (long_option, "seektest", 8)) { // --seektest[=n] if (*long_param) seektest = strtol (long_param, NULL, 10); else seektest = 1; if (seektest) break; } else { printf ("unknown option: %s !\n", long_option); return 1; } } else { printf ("unknown option: %s !\n", *argv); return 1; } } if (strcmp (WavpackGetLibraryVersionString (), PACKAGE_VERSION)) printf (version_warning, WavpackGetLibraryVersionString (), PACKAGE_VERSION); else printf (sign_on, VERSION_OS, WavpackGetLibraryVersionString ()); if (!seektest && !(test_flags & (TEST_FLAG_DEFAULT | TEST_FLAG_EXHAUSTIVE))) { puts (usage); return 1; } if (seektest) { while (--argc) if ((res = seeking_test (*++argv, seektest))) break; } else { printf ("\n\n ****** pure lossless ******\n"); res = run_test_size_modes (wpconfig_flags, test_flags, base_minutes); if (res) goto done; if (!(test_flags & TEST_FLAG_NO_HYBRID)) { printf ("\n\n ****** hybrid lossless ******\n"); res = run_test_size_modes (wpconfig_flags | CONFIG_HYBRID_FLAG | CONFIG_CREATE_WVC, test_flags, base_minutes); if (res) goto done; if (!(test_flags & TEST_FLAG_NO_LOSSY)) { printf ("\n\n ****** hybrid lossy ******\n"); res = run_test_size_modes (wpconfig_flags | CONFIG_HYBRID_FLAG, test_flags, base_minutes); if (res) goto done; printf ("\n\n ****** hybrid lossless (but ignore wvc on decode) ******\n"); res = run_test_size_modes (wpconfig_flags | CONFIG_HYBRID_FLAG | CONFIG_CREATE_WVC, test_flags | TEST_FLAG_IGNORE_WVC, base_minutes); if (res) goto done; } } } done: if (res) printf ("\ntest failed!\n\n"); else printf ("\nall tests pass\n\n"); return res; } // Function to stress-test the WavpackSeekSample() API. Given the specified WavPack file, perform // the specified number of seektest runs on that file. For each test run, a different, random // seek interval is chosen. Note that MD5 sums are calculated for each chunk interval so we // actually verify that every sample decoded is correct. For each test run, we decode the entire // file 4 times over, on average. static int seeking_test (char *filename, uint32_t test_count) { char error [80]; WavpackContext *wpc = WavpackOpenFileInput (filename, error, OPEN_WVC | OPEN_DSD_NATIVE | OPEN_ALT_TYPES, 0); int64_t min_chunk_size = 256, total_samples, sample_count = 0; char md5_string1 [] = "????????????????????????????????"; char md5_string2 [] = "????????????????????????????????"; int32_t *decoded_samples, num_chans, bps, test_index, qmode; unsigned char md5_initial [16], md5_stored [16]; MD5_CTX md5_global, md5_local; unsigned char *chunked_md5; printf ("\n-------------------- file: %s %s--------------------\n", filename, (WavpackGetMode (wpc) & MODE_WVC) ? "(+wvc) " : ""); if (!wpc) { printf ("seeking_test(): error \"%s\" opening input file \"%s\"\n", error, filename); return -1; } num_chans = WavpackGetNumChannels (wpc); total_samples = WavpackGetNumSamples64 (wpc); bps = WavpackGetBytesPerSample (wpc); qmode = WavpackGetQualifyMode (wpc); if (total_samples < 2 || total_samples == -1) { printf ("seeking_test(): can't determine file size!\n"); return -1; } if (qmode & QMODE_DSD_IN_BLOCKS) { printf ("seeking_test(): can't handle blocked DSD audio (i.e., from .dsf files)!\n"); return -1; } // For very short files, reduce the minimum chunk size while (min_chunk_size > 1 && total_samples / min_chunk_size < 256) min_chunk_size /= 2; for (test_index = 0; test_index < test_count; test_index++) { uint32_t chunk_samples, total_chunks, chunk_count = 0, seek_count = 0; chunk_samples = min_chunk_size + frandom () * min_chunk_size; // 256 - 511 (unless reduced) total_chunks = (total_samples + chunk_samples - 1) / chunk_samples; decoded_samples = malloc (sizeof (int32_t) * chunk_samples * num_chans); chunked_md5 = malloc (total_chunks * 16); if (!chunked_md5 || !decoded_samples) { printf ("seeking_test(): can't allocate memory!\n"); return -1; } sample_count = chunk_count = 0; MD5_Init (&md5_global); // read the entire file, calculating the MD5 sums for the whole file and for each "chunk" while (1) { int samples = WavpackUnpackSamples (wpc, decoded_samples, chunk_samples); if (!samples) break; if ((sample_count += samples) > total_samples) { printf ("seeking_test(): sample count is not correct!\n"); return -1; } store_samples (decoded_samples, decoded_samples, qmode, bps, samples * num_chans); MD5_Update (&md5_global, (unsigned char *) decoded_samples, bps * samples * num_chans); MD5_Init (&md5_local); MD5_Update (&md5_local, (unsigned char *) decoded_samples, bps * samples * num_chans); MD5_Final (chunked_md5 + chunk_count * 16, &md5_local); chunk_count++; } if (WavpackGetNumErrors (wpc)) { printf ("seeking_test(): decoder reported %d errors!\n", WavpackGetNumErrors (wpc)); return -1; } if (total_samples != sample_count) { printf ("seeking_test(): sample count is not correct!\n"); return -1; } if (total_chunks != chunk_count) { printf ("seeking_test(): chunk count is not correct (not sure if this can happen)!\n"); return -1; } // The first time through the file we verify that the MD5 sum matches what's stored in the file // (if one is stored there). On subsequent tests, we verify that the whole-file MD5 sum matches // what we got the first time. if (!test_index) { int file_has_md5 = WavpackGetMD5Sum (wpc, md5_stored), i; MD5_Final (md5_initial, &md5_global); for (i = 0; i < 16; ++i) { sprintf (md5_string1 + (i * 2), "%02x", md5_stored [i]); sprintf (md5_string2 + (i * 2), "%02x", md5_initial [i]); } printf ("stored/actual sample count: %lld / %lld\n", (long long int) total_samples, (long long int) sample_count); if (file_has_md5) printf ("stored md5: %s\n", md5_string1); printf ("actual md5: %s\n", md5_string2); if (WavpackGetMode (wpc) & MODE_LOSSLESS) if (file_has_md5 && memcmp (md5_stored, md5_initial, sizeof (md5_stored))) { printf ("seeking_test(): MD5 does not match MD5 stored in file!\n"); return -1; } } else { unsigned char md5_subsequent [16]; MD5_Final (md5_subsequent, &md5_global); if (memcmp (md5_subsequent, md5_initial, sizeof (md5_stored))) { printf ("seeking_test(): MD5 does not match MD5 read initially!\n"); return -1; } } // Half the time, reopen the file. This lets us catch errors caused by seeking to locations // that have never been decoded (at least not for this open call). if (frandom() < 0.5) { WavpackCloseFile (wpc); wpc = WavpackOpenFileInput (filename, error, OPEN_WVC | OPEN_DSD_NATIVE | OPEN_ALT_TYPES, 0); if (!wpc) { printf ("seeking_test(): error \"%s\" reopening input file \"%s\"\n", error, filename); return -1; } } chunk_count *= 4; // decode each chunk 4 times, on average while (chunk_count) { int start_chunk = 0, stop_chunk, current_chunk, num_chunks = 1; start_chunk = floor (frandom () * total_chunks); if (start_chunk == total_chunks) start_chunk--; // At a minimum, we very one chunk after the seek. However, we also random chose additional // chunks to verify so that sometimes we verify lots of data after the seek. while (start_chunk + num_chunks < total_chunks && frandom () < 0.667) num_chunks *= 2; if (start_chunk + num_chunks > total_chunks) num_chunks = total_chunks - start_chunk; stop_chunk = start_chunk + num_chunks - 1; if (!WavpackSeekSample64 (wpc, (int64_t) start_chunk * chunk_samples)) { printf ("seeking_test(): seek error!\n"); return -1; } for (current_chunk = start_chunk; current_chunk <= stop_chunk; ++current_chunk) { int samples = WavpackUnpackSamples (wpc, decoded_samples, chunk_samples); unsigned char md5_chunk [16]; if (!samples) { printf ("seeking_test(): seek error!\n"); return -1; } store_samples (decoded_samples, decoded_samples, qmode, bps, samples * num_chans); // if (frandom() < 0.0001) // decoded_samples [(int) floor (samples * frandom())] ^= 1; MD5_Init (&md5_local); MD5_Update (&md5_local, (unsigned char *) decoded_samples, bps * samples * num_chans); MD5_Final (md5_chunk, &md5_local); if (memcmp (chunked_md5 + current_chunk * 16, md5_chunk, sizeof (md5_chunk))) { printf ("seeking_test(): seek+decode error at %lld!\n", (long long int) current_chunk * chunk_samples); return -1; } if (chunk_count) chunk_count--; else break; } // display the "dot" every 10 seeks if (++seek_count % 10 == 0) { if (seek_count % 640) { putchar ('.'); fflush (stdout); } else puts ("."); } } printf ("\nresult: %u successful seeks on %u-sample boundaries\n", seek_count, chunk_samples); if (!WavpackSeekSample (wpc, 0)) { printf ("seeking_test(): rewind error!\n"); return -1; } free (chunked_md5); free (decoded_samples); } WavpackCloseFile (wpc); return 0; } // Given a WavPack configuration and test flags, run the various combinations of // bit-depth and channel configurations. A return value of FALSE indicates an error. static int run_test_size_modes (int wpconfig_flags, int test_flags, int base_minutes) { int res; printf ("\n *** 8-bit, mono ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags, 8, 1, base_minutes*5*60); if (res) return res; if (test_flags & TEST_FLAG_EXHAUSTIVE) { printf ("\n *** 16-bit, mono ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags, 16, 1, base_minutes*5*60); if (res) return res; } printf ("\n *** 16-bit, stereo ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags, 16, 2, base_minutes*3*60); if (res) return res; if ((test_flags & TEST_FLAG_EXHAUSTIVE) && !(test_flags & TEST_FLAG_NO_FLOATS)) { printf ("\n *** 16-bit (converted to float), stereo ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_FLOAT_DATA, 16, 2, base_minutes*3*60); if (res) return res; } printf ("\n *** 24-bit, 5.1 channels ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags, 24, 6, base_minutes*60); if (res) return res; if (test_flags & TEST_FLAG_EXHAUSTIVE) { if (!(test_flags & TEST_FLAG_NO_FLOATS)) { printf ("\n *** 24-bit (converted to float), 5.1 channels ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_FLOAT_DATA, 24, 6, base_minutes*60); if (res) return res; } printf ("\n *** 32-bit integer, 5.1 channels ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags, 32, 6, base_minutes*60); if (res) return res; if (!(test_flags & TEST_FLAG_NO_FLOATS)) { printf ("\n *** 32-bit float stored as integer (pathological), 5.1 channels ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_STORE_FLOAT_AS_INT32, 32, 6, base_minutes*60); if (res) return res; if (!(wpconfig_flags & CONFIG_HYBRID_FLAG)) { printf ("\n *** 32-bit integer stored as float (pathological), 5.1 channels ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_STORE_INT32_AS_FLOAT, 32, 6, base_minutes*60); if (res) return res; } } } if (!(test_flags & TEST_FLAG_NO_FLOATS)) { printf ("\n *** 32-bit float, 5.1 channels ***\n"); res = run_test_speed_modes (wpconfig_flags, test_flags | TEST_FLAG_FLOAT_DATA, 32, 6, base_minutes*60); if (res) return res; } return 0; } // Given a WavPack configuration and test flags, run the various combinations of // speed modes (i.e, fast, high, etc). A return value of FALSE indicates an error. static int run_test_speed_modes (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds) { int res; if (!(test_flags & TEST_FLAG_NO_SPEEDS)) { res = run_test_extra_modes (wpconfig_flags | CONFIG_FAST_FLAG, test_flags, bits, num_chans, num_seconds); if (res) return res; } res = run_test_extra_modes (wpconfig_flags, test_flags, bits, num_chans, num_seconds); if (res) return res; if (!(test_flags & TEST_FLAG_NO_SPEEDS)) { res = run_test_extra_modes (wpconfig_flags | CONFIG_HIGH_FLAG, test_flags, bits, num_chans, num_seconds); if (res) return res; } if (!(test_flags & TEST_FLAG_NO_SPEEDS)) { res = run_test_extra_modes (wpconfig_flags | CONFIG_VERY_HIGH_FLAG, test_flags, bits, num_chans, num_seconds); if (res) return res; } return 0; } // Given a WavPack configuration and test flags, run the various combinations of "extra" modes (0-6). // Note that except for the base mode (no extra), the "default" and "exhaustive" configurations do // different extra modes. Combining the "default" and "exhaustive" configurations does all the extra // modes. A return value of FALSE indicates an error. static int run_test_extra_modes (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds) { int res; res = run_test (wpconfig_flags, test_flags, bits, num_chans, num_seconds); if (res) return res; if (test_flags & TEST_FLAG_NO_EXTRAS) return 0; if (test_flags & TEST_FLAG_EXHAUSTIVE) { res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (1), bits, num_chans, num_seconds); if (res) return res; } if (test_flags & TEST_FLAG_DEFAULT) { res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (2), bits, num_chans, num_seconds); if (res) return res; } if (test_flags & TEST_FLAG_EXHAUSTIVE) { res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (3), bits, num_chans, num_seconds); if (res) return res; } if (test_flags & TEST_FLAG_EXHAUSTIVE) { res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (4), bits, num_chans, num_seconds); if (res) return res; } if (test_flags & TEST_FLAG_DEFAULT) { res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (5), bits, num_chans, num_seconds); if (res) return res; } if (test_flags & TEST_FLAG_EXHAUSTIVE) { res = run_test (wpconfig_flags, test_flags | TEST_FLAG_EXTRA_MODE (6), bits, num_chans, num_seconds); if (res) return res; } return 0; } // Given a WavPack configuration and test flags, actually run the specified test. This entails // generating the actual audio test data, creating the "virtual" WavPack file and writing to it, // and spawning the thread that will read the "virtual" file and do the decoding (which is obviously // required to verify the entire encode/decode chain). For lossless modes, and MD5 hash is used to // verify the result, otherwise the decoder is trusted to detect and report errors (although the // total number of samples is verified). #define BUFFER_SIZE 1000000 #define NUM_GENERATORS 6 struct audio_channel { float audio_gain_hist [NUM_GENERATORS], audio_gain [NUM_GENERATORS], angle_offset; int lfe_flag; }; #define SAMPLE_RATE 44100 #define ENCODE_SAMPLES 128 #define NOISE_GAIN 0.6667 #define TONE_GAIN 0.3333 static int run_test (int wpconfig_flags, int test_flags, int bits, int num_chans, int num_seconds) { static int test_number; float sequencing_angle = 0.0, speed = 60.0, width = 200.0, *source, *destin, ratio, bps; int lossless = !(wpconfig_flags & CONFIG_HYBRID_FLAG) || ((wpconfig_flags & CONFIG_CREATE_WVC) && !(test_flags & TEST_FLAG_IGNORE_WVC)); char md5_string1 [] = "????????????????????????????????"; char md5_string2 [] = "????????????????????????????????"; uint32_t total_encoded_bytes, total_encoded_samples; struct audio_generator generators [NUM_GENERATORS]; int seconds = 0, samples = 0, wc = 0, chan_mask; char *filename = NULL, mode_string [32] = "-"; struct audio_channel *channels; pthread_t pthread; WavpackContext *out_wpc; WavpackConfig wpconfig; StreamingFile wv_stream, wvc_stream; WavpackDecoder wv_decoder; unsigned char md5_encoded [16]; MD5_CTX md5_context; void *term_value; int i, j, k; if (wpconfig_flags & CONFIG_FAST_FLAG) strcat (mode_string, "f"); else if (wpconfig_flags & CONFIG_HIGH_FLAG) strcat (mode_string, "h"); else if (wpconfig_flags & CONFIG_VERY_HIGH_FLAG) strcat (mode_string, "hh"); printf ("test %04d...", ++test_number); fflush (stdout); MD5_Init (&md5_context); noise_generator_init (&generators [0], 128.0); tone_generator_init (&generators [1], SAMPLE_RATE, 20, 200); noise_generator_init (&generators [2], 12.0); tone_generator_init (&generators [3], SAMPLE_RATE, 200, 2000); noise_generator_init (&generators [4], 1.75); tone_generator_init (&generators [5], SAMPLE_RATE, 2000, 20000); CLEAR (wpconfig); CLEAR (wv_decoder); CLEAR (wv_stream); CLEAR (wvc_stream); channels = malloc (num_chans * sizeof (*channels)); source = malloc (ENCODE_SAMPLES * sizeof (*source)); destin = malloc (ENCODE_SAMPLES * num_chans * sizeof (*destin)); if (!channels || !source || !destin) { printf ("run_test(): can't allocate memory!\n"); exit (-1); } memset (channels, 0, num_chans * sizeof (*channels)); switch (num_chans) { case 1: channels [0].angle_offset = 0.0; chan_mask = 0x4; break; case 2: channels [0].angle_offset -= M_PI / 24.0; channels [1].angle_offset += M_PI / 24.0; chan_mask = 0x3; break; case 4: channels [0].angle_offset -= M_PI / 24.0; channels [1].angle_offset += M_PI / 24.0; channels [2].angle_offset -= 23.0 * M_PI / 24.0; channels [3].angle_offset += 23.0 * M_PI / 24.0; chan_mask = 0x33; break; case 6: channels [0].angle_offset -= M_PI / 24.0; channels [1].angle_offset += M_PI / 24.0; channels [3].lfe_flag = 1; channels [4].angle_offset -= 23.0 * M_PI / 24.0; channels [5].angle_offset += 23.0 * M_PI / 24.0; chan_mask = 0x3F; break; default: printf ("invalid channel count = %d\n", num_chans); exit (-1); } if (!(test_flags & TEST_FLAG_NO_DECODE)) { initialize_stream (&wv_stream, BUFFER_SIZE); wv_decoder.wv_stream = &wv_stream; } else initialize_stream (&wv_stream, 0); if (test_flags & TEST_FLAG_WRITE_FILE) { int i; for (i = 0; i < number_of_ranges; ++i) if (test_number >= write_ranges [i].start && test_number <= write_ranges [i].stop) { filename = malloc (32); if (!filename) { printf ("run_test(): can't allocate memory!\n"); exit (-1); } sprintf (filename, "testfile-%04d.wv", test_number); if (((wv_stream.file = fopen (filename, "w+b")) == NULL)) { printf ("can't create file %s!\n", filename); free_stream (&wv_stream); return 1; } break; } } if (wpconfig_flags & CONFIG_CREATE_WVC) { if (!(test_flags & (TEST_FLAG_IGNORE_WVC | TEST_FLAG_NO_DECODE))) { initialize_stream (&wvc_stream, BUFFER_SIZE); wv_decoder.wvc_stream = &wvc_stream; } else initialize_stream (&wvc_stream, 0); if (filename) { char *filename_c = malloc (strlen (filename) + 10); strcpy (filename_c, filename); strcat (filename_c, "c"); if ((wvc_stream.file = fopen (filename_c, "w+b")) == NULL) { printf ("can't create file %s!\n", filename_c); free_stream (&wv_stream); free_stream (&wvc_stream); return 1; } free (filename_c); } } out_wpc = WavpackOpenFileOutput (write_block, &wv_stream, (wpconfig_flags & CONFIG_CREATE_WVC) ? &wvc_stream : NULL); if (!(test_flags & TEST_FLAG_NO_DECODE)) pthread_create (&pthread, NULL, decode_thread, (void *) &wv_decoder); if (test_flags & (TEST_FLAG_FLOAT_DATA | TEST_FLAG_STORE_INT32_AS_FLOAT)) { wpconfig.float_norm_exp = 127; wpconfig.bytes_per_sample = 4; wpconfig.bits_per_sample = 32; } else { wpconfig.bytes_per_sample = (bits + 7) >> 3; wpconfig.bits_per_sample = bits; } if (test_flags & TEST_FLAG_EXTRA_MASK) { sprintf (mode_string + strlen (mode_string), "x%c", '0' + (test_flags & TEST_FLAG_EXTRA_MASK)); wpconfig.xmode = test_flags & TEST_FLAG_EXTRA_MASK; wpconfig_flags |= CONFIG_EXTRA_MODE; } wpconfig.sample_rate = SAMPLE_RATE; wpconfig.num_channels = num_chans; wpconfig.channel_mask = chan_mask; wpconfig.flags = wpconfig_flags; if (wpconfig_flags & CONFIG_HYBRID_FLAG) { if (wpconfig_flags & CONFIG_CREATE_WVC) { if (test_flags & TEST_FLAG_IGNORE_WVC) { strcat (mode_string, "b4c"); wpconfig.bitrate = 4.0; } else { strcat (mode_string, "b3c"); wpconfig.bitrate = 3.0; } } else { strcat (mode_string, "b5"); wpconfig.bitrate = 5.0; } } WavpackSetConfiguration64 (out_wpc, &wpconfig, -1, NULL); WavpackPackInit (out_wpc); while (seconds < num_seconds) { double translated_angle = cos (sequencing_angle) * 100.0; double width_scalar = pow (2.0, -width); for (k = 0; k < num_chans; ++k) { channels [k].audio_gain [0] = pow (sin (translated_angle + channels [k].angle_offset - M_PI * 1.6667) + 1.0, width) * width_scalar * NOISE_GAIN; channels [k].audio_gain [1] = pow (sin (translated_angle + channels [k].angle_offset - M_PI * 0.6667) + 1.0, width) * width_scalar * TONE_GAIN; channels [k].audio_gain [2] = pow (sin (translated_angle + channels [k].angle_offset - M_PI * 0.3333) + 1.0, width) * width_scalar * NOISE_GAIN; channels [k].audio_gain [3] = pow (sin (translated_angle + channels [k].angle_offset - M_PI * 1.3333) + 1.0, width) * width_scalar * TONE_GAIN; channels [k].audio_gain [4] = pow (sin (translated_angle + channels [k].angle_offset - M_PI) + 1.0, width) * width_scalar * NOISE_GAIN; channels [k].audio_gain [5] = pow (sin (translated_angle + channels [k].angle_offset) + 1.0, width) * width_scalar * TONE_GAIN; } memset (destin, 0, ENCODE_SAMPLES * num_chans * sizeof (*destin)); for (j = 0; j < NUM_GENERATORS; ++j) { audio_generator_run (&generators [j], source, ENCODE_SAMPLES); for (k = 0; k < num_chans; ++k) { if (!channels [k].lfe_flag || j < 2) mix_samples_with_gain (destin + k, source, ENCODE_SAMPLES, num_chans, channels [k].audio_gain_hist [j], channels [k].audio_gain [j]); channels [k].audio_gain_hist [j] = channels [k].audio_gain [j]; } } if (test_flags & TEST_FLAG_FLOAT_DATA) { if (bits <= 25) truncate_float_samples (destin, ENCODE_SAMPLES * num_chans, bits); else if (bits != 32) { printf ("invalid bits configuration\n"); exit (-1); } } else if (!(test_flags & TEST_FLAG_STORE_FLOAT_AS_INT32)) { if (bits < 32) float_to_integer_samples (destin, ENCODE_SAMPLES * num_chans, bits); else if (bits == 32) float_to_32bit_integer_samples (destin, ENCODE_SAMPLES * num_chans); else { printf ("invalid bits configuration\n"); exit (-1); } } WavpackPackSamples (out_wpc, (int32_t *) destin, ENCODE_SAMPLES); store_samples (destin, (int32_t *) destin, 0, wpconfig.bytes_per_sample, ENCODE_SAMPLES * num_chans); MD5_Update (&md5_context, (unsigned char *) destin, wpconfig.bytes_per_sample * ENCODE_SAMPLES * num_chans); sequencing_angle += 2.0 * M_PI / SAMPLE_RATE / speed * ENCODE_SAMPLES; if (sequencing_angle > M_PI) sequencing_angle -= M_PI * 2.0; if ((samples += ENCODE_SAMPLES) >= SAMPLE_RATE) { samples -= SAMPLE_RATE; ++seconds; if (!(wc & 1)) { if (width > 1.0) width *= 0.875; else if (width > 0.125) width -= 0.125; else { width = 0.0; wc++; } } else { if (width < 1.0) width += 0.125; else if (width < 200.0) width *= 1.125; else wc++; } } } WavpackFlushSamples (out_wpc); MD5_Final (md5_encoded, &md5_context); if (wpconfig.flags & CONFIG_MD5_CHECKSUM) { WavpackStoreMD5Sum (out_wpc, md5_encoded); WavpackFlushSamples (out_wpc); } WavpackCloseFile (out_wpc); free (channels); free (source); free (destin); if ((wpconfig_flags & CONFIG_CREATE_WVC) && !(test_flags & TEST_FLAG_IGNORE_WVC)) total_encoded_bytes = wv_stream.bytes_written + wvc_stream.bytes_written; else total_encoded_bytes = wv_stream.bytes_written; total_encoded_samples = seconds * SAMPLE_RATE + samples; ratio = total_encoded_bytes / ((float) total_encoded_samples * wpconfig.bytes_per_sample * num_chans); bps = total_encoded_bytes * 8 / ((float) total_encoded_samples * num_chans); flush_stream (&wv_stream); flush_stream (&wvc_stream); if (!(test_flags & TEST_FLAG_NO_DECODE)) { pthread_join (pthread, &term_value); if (term_value) { printf ("decode_thread() returned error\n"); return 1; } } if (!(test_flags & TEST_FLAG_NO_DECODE)) { for (i = 0; i < 16; ++i) { sprintf (md5_string1 + (i * 2), "%02x", md5_encoded [i]); sprintf (md5_string2 + (i * 2), "%02x", wv_decoder.md5_decoded [i]); } if (wv_decoder.num_errors || wv_decoder.sample_count != total_encoded_samples || (lossless && memcmp (md5_encoded, wv_decoder.md5_decoded, sizeof (md5_encoded)))) { printf ("\n---------------------------------------------\n"); printf ("enc/dec sample count: %u / %u\n", total_encoded_samples, wv_decoder.sample_count); printf ("encoded md5: %s\n", md5_string1); printf ("decoded md5: %s\n", md5_string2); printf ("reported decode errors: %d\n", wv_decoder.num_errors); printf ("---------------------------------------------\n"); return wv_decoder.num_errors + 1; } } free_stream (&wv_stream); free_stream (&wvc_stream); printf ("pass (%8s, %.2f%%, %.2f bps, %s)\n", mode_string, 100.0 - ratio * 100.0, bps, md5_string2); return 0; } // Thread / function that opens a virtual WavPack file, decodes it and calculates the MD5 hash of the // decoded audio data. #define DECODE_SAMPLES 1000 static void *decode_thread (void *threadid) { WavpackDecoder *wd = (WavpackDecoder *) threadid; char error [80]; WavpackContext *wpc = WavpackOpenFileInputEx (&freader, wd->wv_stream, wd->wvc_stream, error, 0, 0); int32_t *decoded_samples, num_chans, bps; MD5_CTX md5_context; if (!wpc) { printf ("decode_thread(): error \"%s\" opening input file\n", error); wd->num_errors = 1; pthread_exit (NULL); } MD5_Init (&md5_context); num_chans = WavpackGetNumChannels (wpc); bps = WavpackGetBytesPerSample (wpc); decoded_samples = malloc (sizeof (int32_t) * DECODE_SAMPLES * num_chans); if (!decoded_samples) { printf ("decode_thread(): can't allocate memory!\n"); exit (-1); } while (1) { int samples = WavpackUnpackSamples (wpc, decoded_samples, DECODE_SAMPLES); if (!samples) break; store_samples (decoded_samples, decoded_samples, 0, bps, samples * num_chans); MD5_Update (&md5_context, (unsigned char *) decoded_samples, bps * samples * num_chans); wd->sample_count += samples; } MD5_Final (wd->md5_decoded, &md5_context); wd->num_errors = WavpackGetNumErrors (wpc); free (decoded_samples); WavpackCloseFile (wpc); pthread_exit (NULL); return NULL; } // This code implements a simple virtual "file" so that we can have a WavPack encoding process and // a WavPack decoding process running at the same time (using Pthreads). static int write_block (void *id, void *data, int32_t length) { StreamingFile *ws = (StreamingFile *) id; unsigned char *data_ptr = data; if (!ws || !data || !length) return 0; // if (frandom() < .0001) // ((char *) data) [(int) floor (length * frandom())] ^= 1; if (!ws->first_block_size) ws->first_block_size = length; ws->bytes_written += length; if (ws->file && !ws->error) { if (!fwrite (data, 1, length, ws->file)) { ws->error = 1; fclose (ws->file); ws->file = NULL; } } if (!ws->buffer_size) // if no buffer, just swallow data silently return 1; pthread_mutex_lock (&ws->mutex); while (length) { int32_t bytes_available = ws->buffer_tail - ws->buffer_head - 1; int32_t bytes_to_copy = length; if (bytes_available < 0) bytes_available += ws->buffer_size; if (bytes_available < bytes_to_copy) bytes_to_copy = bytes_available; if (ws->buffer_head + bytes_to_copy > ws->buffer_base + ws->buffer_size) bytes_to_copy = ws->buffer_base + ws->buffer_size - ws->buffer_head; if (!bytes_to_copy) { ws->full_waits++; pthread_cond_wait (&ws->cond_read, &ws->mutex); continue; } memcpy ((void *) ws->buffer_head, data_ptr, bytes_to_copy); if ((ws->buffer_head += bytes_to_copy) == ws->buffer_base + ws->buffer_size) ws->buffer_head = ws->buffer_base; data_ptr += bytes_to_copy; length -= bytes_to_copy; } pthread_cond_signal (&ws->cond_write); pthread_mutex_unlock (&ws->mutex); return 1; } static int32_t read_bytes (void *id, void *data, int32_t bcount) { StreamingFile *ws = (StreamingFile *) id; unsigned char *data_ptr = data; pthread_mutex_lock (&ws->mutex); while (bcount) { if (ws->push_back) { *data_ptr++ = ws->push_back; ws->push_back = 0; bcount--; } else if (ws->buffer_head != ws->buffer_tail) { int bytes_available = ws->buffer_head - ws->buffer_tail; int32_t bytes_to_copy = bcount; if (bytes_available < 0) bytes_available += ws->buffer_size; if (bytes_available < bytes_to_copy) bytes_to_copy = bytes_available; if (ws->buffer_tail + bytes_to_copy > ws->buffer_base + ws->buffer_size) bytes_to_copy = ws->buffer_base + ws->buffer_size - ws->buffer_tail; memcpy (data_ptr, (void *) ws->buffer_tail, bytes_to_copy); if ((ws->buffer_tail += bytes_to_copy) == ws->buffer_base + ws->buffer_size) ws->buffer_tail = ws->buffer_base; ws->bytes_read += bytes_to_copy; data_ptr += bytes_to_copy; bcount -= bytes_to_copy; } else if (ws->done) break; else { ws->empty_waits++; pthread_cond_wait (&ws->cond_write, &ws->mutex); } } pthread_cond_signal (&ws->cond_read); pthread_mutex_unlock (&ws->mutex); return data_ptr - (unsigned char *) data; } static uint32_t get_pos (void *id) { return -1; } static int set_pos_abs (void *id, uint32_t pos) { return 0; } static int set_pos_rel (void *id, int32_t delta, int mode) { return -1; } static int push_back_byte (void *id, int c) { StreamingFile *ws = (StreamingFile *) id; if (!ws->push_back) return ws->push_back = c; else return EOF; } static uint32_t get_length (void *id) { return 0; } static int can_seek (void *id) { return 0; } static WavpackStreamReader freader = { read_bytes, get_pos, set_pos_abs, set_pos_rel, push_back_byte, get_length, can_seek, }; static void initialize_stream (StreamingFile *ws, int buffer_size) { if (buffer_size) { ws->buffer_base = malloc (ws->buffer_size = buffer_size); ws->buffer_head = ws->buffer_tail = ws->buffer_base; pthread_cond_init (&ws->cond_write, NULL); pthread_cond_init (&ws->cond_read, NULL); pthread_mutex_init (&ws->mutex, NULL); } } static void flush_stream (StreamingFile *ws) { if (ws->buffer_base) { pthread_mutex_lock (&ws->mutex); ws->done = 1; pthread_cond_signal (&ws->cond_write); pthread_mutex_unlock (&ws->mutex); } } static void free_stream (StreamingFile *ws) { if (ws->file) { fclose (ws->file); ws->file = NULL; } if (ws->buffer_base) { free ((void *) ws->buffer_base); ws->buffer_base = NULL; } } // Helper utilities for generating the audio used for testing. // Return a random value in the range: 0.0 <= n < 1.0 static double frandom (void) { static uint64_t random = 0x3141592653589793ULL; random = ((random << 4) - random) ^ 1; random = ((random << 4) - random) ^ 1; random = ((random << 4) - random) ^ 1; return (random >> 32) / 4294967296.0; } static void tone_generator_init (struct audio_generator *cxt, int sample_rate, int low_freq, int high_freq) { struct tone_generator *tone_cxt = &cxt->u.tone_cxt; memset (cxt, 0, sizeof (*cxt)); cxt->type = tone; tone_cxt->sample_rate = sample_rate; tone_cxt->high_frequency = high_freq; tone_cxt->low_frequency = low_freq; tone_cxt->samples_per_update = sample_rate / low_freq * 4; } static void tone_generator_run (struct tone_generator *cxt, float *samples, int num_samples) { float target_frequency, target_velocity; while (num_samples--) { if (!cxt->samples_left) { cxt->samples_left = cxt->samples_per_update; target_frequency = cxt->low_frequency * pow (cxt->high_frequency / cxt->low_frequency, frandom ()); target_velocity = (M_PI * 2.0) / ((float) cxt->sample_rate / target_frequency); cxt->acceleration = (target_velocity - cxt->velocity) / cxt->samples_left; } *samples++ = sin (cxt->angle += cxt->velocity += cxt->acceleration); if (cxt->angle > M_PI) cxt->angle -= M_PI * 2.0; cxt->samples_left--; } } static void noise_generator_init (struct audio_generator *cxt, float factor) { struct noise_generator *noise_cxt = &cxt->u.noise_cxt; memset (cxt, 0, sizeof (*cxt)); cxt->type = noise; noise_cxt->scalar = factor * factor * factor * sqrt (factor) / (2.0 + factor * factor); noise_cxt->factor = factor; } static void noise_generator_run (struct noise_generator *cxt, float *samples, int num_samples) { while (num_samples--) { float source = (frandom () - 0.5) * cxt->scalar; cxt->sum1 += (source - cxt->sum1) / cxt->factor; cxt->sum2 += (cxt->sum1 - cxt->sum2) / cxt->factor; *samples++ = cxt->sum2 - cxt->sum2p; cxt->sum2p = cxt->sum2; } } static void audio_generator_run (struct audio_generator *cxt, float *samples, int num_samples) { switch (cxt->type) { case noise: noise_generator_run (&cxt->u.noise_cxt, samples, num_samples); break; case tone: tone_generator_run (&cxt->u.tone_cxt, samples, num_samples); break; default: printf ("bad audio generator type!\n"); exit (-1); } } static void mix_samples_with_gain (float *destin, float *source, int num_samples, int num_chans, float initial_gain, float final_gain) { float delta_gain = (final_gain - initial_gain) / num_samples; float gain = initial_gain - delta_gain; while (num_samples--) { *destin += *source++ * (gain += delta_gain); destin += num_chans; } } static void truncate_float_samples (float *samples, int num_samples, int bits) { int isample, imin = -(1 << (bits - 1)), imax = (1 << (bits - 1)) - 1; float scalar = (float) (1 << (bits - 1)); while (num_samples--) { if (*samples >= 1.0) isample = imax; else if (*samples <= -1.0) isample = imin; else isample = floor (*samples * scalar); *samples++ = isample / scalar; } } static void float_to_integer_samples (float *samples, int num_samples, int bits) { int isample, imin = -(1 << (bits - 1)), imax = (1 << (bits - 1)) - 1; float scalar = (float) (1 << (bits - 1)); int ishift = (8 - (bits & 0x7)) & 0x7; while (num_samples--) { if (*samples >= 1.0) isample = imax; else if (*samples <= -1.0) isample = imin; else isample = floor (*samples * scalar); *(int32_t *)samples = (uint32_t) isample << ishift; samples++; } } static void float_to_32bit_integer_samples (float *samples, int num_samples) { int isample, imin = 0x8000000, imax = 0x7fffffff; float scalar = 2147483648.0; while (num_samples--) { if (*samples >= 1.0) isample = imax; else if (*samples <= -1.0) isample = imin; else isample = floor (*samples * scalar); // if there are trailing zeros, fill them in with random data if (isample && !(isample & 1)) { int tzeros = 1; while (!((isample >>= 1) & 1)) tzeros++; while (tzeros--) isample = ((unsigned int) isample << 1) + ((frandom() > 0.5) ? 1 : 0); } *(int32_t *)samples = isample; samples++; } } // Code to store samples. Source is an array of int32_t data (which is what WavPack uses // internally), but the destination can have from 1 to 4 bytes per sample. Also, the destination // data is assumed to be little-endian and signed, except for byte data which is unsigned (these // are WAV file defaults). The endian and signedness can be overridden with the qmode flags // to support other formats. static void *store_little_endian_unsigned_samples (void *dst, int32_t *src, int bps, int count); static void *store_little_endian_signed_samples (void *dst, int32_t *src, int bps, int count); static void *store_big_endian_unsigned_samples (void *dst, int32_t *src, int bps, int count); static void *store_big_endian_signed_samples (void *dst, int32_t *src, int bps, int count); static void *store_samples (void *dst, int32_t *src, int qmode, int bps, int count) { if (qmode & QMODE_BIG_ENDIAN) { if ((qmode & QMODE_UNSIGNED_WORDS) || (bps == 1 && !(qmode & QMODE_SIGNED_BYTES))) return store_big_endian_unsigned_samples (dst, src, bps, count); else return store_big_endian_signed_samples (dst, src, bps, count); } else if ((qmode & QMODE_UNSIGNED_WORDS) || (bps == 1 && !(qmode & (QMODE_SIGNED_BYTES | QMODE_DSD_AUDIO)))) return store_little_endian_unsigned_samples (dst, src, bps, count); else return store_little_endian_signed_samples (dst, src, bps, count); } static void *store_little_endian_unsigned_samples (void *dst, int32_t *src, int bps, int count) { unsigned char *dptr = dst; int32_t temp; switch (bps) { case 1: while (count--) *dptr++ = *src++ + 0x80; break; case 2: while (count--) { *dptr++ = (unsigned char) (temp = *src++ + 0x8000); *dptr++ = (unsigned char) (temp >> 8); } break; case 3: while (count--) { *dptr++ = (unsigned char) (temp = *src++ + 0x800000); *dptr++ = (unsigned char) (temp >> 8); *dptr++ = (unsigned char) (temp >> 16); } break; case 4: while (count--) { *dptr++ = (unsigned char) (temp = *src++ + 0x80000000); *dptr++ = (unsigned char) (temp >> 8); *dptr++ = (unsigned char) (temp >> 16); *dptr++ = (unsigned char) (temp >> 24); } break; } return dptr; } static void *store_little_endian_signed_samples (void *dst, int32_t *src, int bps, int count) { unsigned char *dptr = dst; int32_t temp; switch (bps) { case 1: while (count--) *dptr++ = *src++; break; case 2: while (count--) { *dptr++ = (unsigned char) (temp = *src++); *dptr++ = (unsigned char) (temp >> 8); } break; case 3: while (count--) { *dptr++ = (unsigned char) (temp = *src++); *dptr++ = (unsigned char) (temp >> 8); *dptr++ = (unsigned char) (temp >> 16); } break; case 4: while (count--) { *dptr++ = (unsigned char) (temp = *src++); *dptr++ = (unsigned char) (temp >> 8); *dptr++ = (unsigned char) (temp >> 16); *dptr++ = (unsigned char) (temp >> 24); } break; } return dptr; } static void *store_big_endian_unsigned_samples (void *dst, int32_t *src, int bps, int count) { unsigned char *dptr = dst; int32_t temp; switch (bps) { case 1: while (count--) *dptr++ = *src++ + 0x80; break; case 2: while (count--) { *dptr++ = (unsigned char) ((temp = *src++ + 0x8000) >> 8); *dptr++ = (unsigned char) temp; } break; case 3: while (count--) { *dptr++ = (unsigned char) ((temp = *src++ + 0x800000) >> 16); *dptr++ = (unsigned char) (temp >> 8); *dptr++ = (unsigned char) temp; } break; case 4: while (count--) { *dptr++ = (unsigned char) ((temp = *src++ + 0x80000000) >> 24); *dptr++ = (unsigned char) (temp >> 16); *dptr++ = (unsigned char) (temp >> 8); *dptr++ = (unsigned char) temp; } break; } return dptr; } static void *store_big_endian_signed_samples (void *dst, int32_t *src, int bps, int count) { unsigned char *dptr = dst; int32_t temp; switch (bps) { case 1: while (count--) *dptr++ = *src++; break; case 2: while (count--) { *dptr++ = (unsigned char) ((temp = *src++) >> 8); *dptr++ = (unsigned char) temp; } break; case 3: while (count--) { *dptr++ = (unsigned char) ((temp = *src++) >> 16); *dptr++ = (unsigned char) (temp >> 8); *dptr++ = (unsigned char) temp; } break; case 4: while (count--) { *dptr++ = (unsigned char) ((temp = *src++) >> 24); *dptr++ = (unsigned char) (temp >> 16); *dptr++ = (unsigned char) (temp >> 8); *dptr++ = (unsigned char) temp; } break; } return dptr; }