//////////////////////////////////////////////////////////////////////////// // **** WAVPACK **** // // Hybrid Lossless Wavefile Compressor // // Copyright (c) 1998 - 2004 Conifer Software. // // All Rights Reserved. // // Distributed under the BSD Software License (see license.txt) // //////////////////////////////////////////////////////////////////////////// // wvparser.c // Demonstration WavPack block parser. // WavPack file to stdin, output to stdout. #include #include #include #include #include typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned long ulong; typedef unsigned int uint; typedef long (*read_stream)(void *, long); // Note that this is the ONLY structure that is written to (or read from) // WavPack 4.0 files, and is the preamble to every block in both the .wv // and .wvc files. typedef struct { char ckID [4]; ulong ckSize; short version; uchar track_no, index_no; ulong total_samples, block_index, block_samples, flags, crc; } WavpackHeader; #define WavpackHeaderFormat "4LS2LLLLL" // or-values for "flags" #define BYTES_STORED 3 // 1-4 bytes/sample #define MONO_FLAG 4 // not stereo #define HYBRID_FLAG 8 // hybrid mode #define JOINT_STEREO 0x10 // joint stereo #define CROSS_DECORR 0x20 // no-delay cross decorrelation #define HYBRID_SHAPE 0x40 // noise shape (hybrid mode only) #define FLOAT_DATA 0x80 // ieee 32-bit floating point data #define INT32_DATA 0x100 // special extended int handling #define HYBRID_BITRATE 0x200 // bitrate noise (hybrid mode only) #define HYBRID_BALANCE 0x400 // balance noise (hybrid stereo mode only) #define INITIAL_BLOCK 0x800 // initial block of multichannel segment #define FINAL_BLOCK 0x1000 // final block of multichannel segment #define SHIFT_LSB 13 #define SHIFT_MASK (0x1fL << SHIFT_LSB) #define MAG_LSB 18 #define MAG_MASK (0x1fL << MAG_LSB) #define SRATE_LSB 23 #define SRATE_MASK (0xfL << SRATE_LSB) #define IGNORED_FLAGS 0x18000000 // reserved, but ignore if encountered #define NEW_SHAPING 0x20000000 // use IIR filter for negative shaping #define UNKNOWN_FLAGS 0xC0000000 // also reserved, but refuse decode if // encountered static long read_bytes (void *buff, long bcount); static ulong read_next_header (read_stream infile, WavpackHeader *wphdr); static void little_endian_to_native (void *data, char *format); const ulong sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 }; int main () { ulong bcount, total_bytes, sample_rate, first_sample, last_sample = -1L; int channel_count, block_count; WavpackHeader wphdr; setmode (fileno (stdin), O_BINARY); while (1) { // read next WavPack header bcount = read_next_header (read_bytes, &wphdr); if (bcount == (ulong) -1) { printf ("end of file\n"); break; } if (bcount) printf ("%d bytes of unknown data skipped\n", bcount); // if there's audio samples in there... if (wphdr.block_samples) { if ((wphdr.flags & INITIAL_BLOCK) && wphdr.block_index != last_sample + 1) printf ("warning: discontinuity detected!\n"); if (!(wphdr.flags & INITIAL_BLOCK)) if (first_sample != wphdr.block_index || last_sample != wphdr.block_index + wphdr.block_samples - 1) printf ("warning: multichannel block mismatch detected!\n"); last_sample = (first_sample = wphdr.block_index) + wphdr.block_samples - 1; sample_rate = (wphdr.flags & SRATE_MASK) >> SRATE_LSB; if (sample_rate == 15) printf ("warning: unknown sample rate!\n"); else sample_rate = sample_rates [sample_rate]; if ((wphdr.flags & INITIAL_BLOCK) && (wphdr.flags & FINAL_BLOCK)) printf ("%s block: time = %.2f - %.2f secs, %s, %d bytes\n", (wphdr.flags & MONO_FLAG) ? "mono" : "stereo", (double) first_sample / sample_rate, (double) last_sample / sample_rate, (wphdr.flags & HYBRID_FLAG) ? "hybrid" : "lossless", wphdr.ckSize + 8); else if (wphdr.flags & INITIAL_BLOCK) { channel_count = (wphdr.flags & MONO_FLAG) ? 1 : 2; total_bytes = wphdr.ckSize + 8; block_count = 1; } else { channel_count += (wphdr.flags & MONO_FLAG) ? 1 : 2; total_bytes += wphdr.ckSize + 8; block_count++; if (wphdr.flags & FINAL_BLOCK) printf ("%d chans in %d blocks: time = %.2f - %.2f secs, %s, %d bytes\n", channel_count, block_count, (double) first_sample / sample_rate, (double) last_sample / sample_rate, (wphdr.flags & HYBRID_FLAG) ? "hybrid" : "lossless", total_bytes); } } else printf ("non-audio block found\n"); // swallow actual block data if (wphdr.ckSize > sizeof (WavpackHeader) - 8) { ulong data_size = wphdr.ckSize - sizeof (WavpackHeader) + 8; char *temp = malloc (data_size); read_bytes (temp, data_size); free (temp); } } return 0; } static long read_bytes (void *buff, long bcount) { return fread (buff, 1, bcount, stdin); } // Read from current file position until a valid 32-byte WavPack 4.0 header is // found and read into the specified pointer. The number of bytes skipped is // returned. If no WavPack header is found within 1 meg, then a -1 is returned // to indicate the error. No additional bytes are read past the header and it // is returned in the processor's native endian mode. Seeking is not required. static ulong read_next_header (read_stream infile, WavpackHeader *wphdr) { char buffer [sizeof (*wphdr)], *sp = buffer + sizeof (*wphdr), *ep = sp; ulong bytes_skipped = 0; int bleft; while (1) { if (sp < ep) { bleft = ep - sp; memcpy (buffer, sp, bleft); } else bleft = 0; if (infile (buffer + bleft, sizeof (*wphdr) - bleft) != (long) sizeof (*wphdr) - bleft) return -1; sp = buffer; if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' && !(*++sp & 1) && sp [2] < 16 && !sp [3] && sp [5] == 4 && sp [4] >= 2 && sp [4] <= 0xf) { memcpy (wphdr, buffer, sizeof (*wphdr)); little_endian_to_native (wphdr, WavpackHeaderFormat); return bytes_skipped; } while (sp < ep && *sp != 'w') sp++; if ((bytes_skipped += sp - buffer) > 1024 * 1024) return -1; } } static void little_endian_to_native (void *data, char *format) { uchar *cp = (uchar *) data; long temp; while (*format) { switch (*format) { case 'L': temp = cp [0] + ((long) cp [1] << 8) + ((long) cp [2] << 16) + ((long) cp [3] << 24); * (long *) cp = temp; cp += 4; break; case 'S': temp = cp [0] + (cp [1] << 8); * (short *) cp = (short) temp; cp += 2; break; default: if (isdigit (*format)) cp += *format - '0'; break; } format++; } }