Index: configure.ac
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/configure.ac,v
retrieving revision 1.524.2.14
diff -u -r1.524.2.14 configure.ac
--- configure.ac	31 Mar 2005 21:51:26 -0000	1.524.2.14
+++ configure.ac	1 Apr 2005 17:25:51 -0000
@@ -1746,6 +1746,13 @@
   CFLAGS="$ac_cflags_save"
 fi
 
+dnl *** wavpack ***
+dnl We ship our own version of the library
+translit(dnm, m, l) AM_CONDITIONAL(USE_WAVPACK, true)
+GST_CHECK_FEATURE(WAVPACK, [wavpack plug-in], wavpack,  [
+  HAVE_WAVPACK=yes
+])
+
 dnl *** xine ***
 translit(dnm, m, l) AM_CONDITIONAL(USE_XINE, true)
 GST_CHECK_FEATURE(XINE, [xine wrapper], xine, [
@@ -2123,6 +2130,7 @@
 ext/tarkin/Makefile
 ext/theora/Makefile
 ext/vorbis/Makefile
+ext/wavpack/Makefile
 ext/xine/Makefile
 ext/xvid/Makefile
 gst-libs/Makefile
Index: ext/Makefile.am
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/ext/Makefile.am,v
retrieving revision 1.122.2.3
diff -u -r1.122.2.3 Makefile.am
--- ext/Makefile.am	26 Mar 2005 15:18:02 -0000	1.122.2.3
+++ ext/Makefile.am	1 Apr 2005 17:25:51 -0000
@@ -406,6 +406,12 @@
 SPEEX_DIR=
 endif
 
+if USE_WAVPACK
+WAVPACK_DIR=wavpack
+else
+WAVPACK_DIR=
+endif
+
 if USE_XINE
 XINE_DIR=xine
 else
@@ -478,6 +484,7 @@
 	$(THEORA_DIR) \
 	$(IVORBIS_DIR) \
 	$(VORBIS_DIR) \
+	$(WAVPACK_DIR) \
 	$(XINE_DIR) \
 	$(XVID_DIR)
 
@@ -547,5 +554,6 @@
 	tarkin \
 	theora \
 	vorbis \
+	wavpack \
 	xine \
 	xvid
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/Makefile.am	2005-03-31 09:57:54.000000000 +0100
@@ -0,0 +1,10 @@
+
+plugin_LTLIBRARIES = libgstwavpack.la
+
+libgstwavpack_la_SOURCES = bits.c extra1.c extra2.c float.c metadata.c pack.c unpack.c words.c wputils.c gstwavpackdec.c gstwavpackreader.c
+
+libgstwavpack_la_CFLAGS = $(GST_CFLAGS) -DUNPACK -DPACK -DSEEKING
+libgstwavpack_la_LIBADD = 
+libgstwavpack_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -no-undefined
+
+noinst_HEADERS = wavpack.h gstwavpackreader.h
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/bits.c	2005-03-30 10:17:50.000000000 +0100
@@ -0,0 +1,259 @@
+////////////////////////////////////////////////////////////////////////////
+//                           **** WAVPACK ****                            //
+//                  Hybrid Lossless Wavefile Compressor                   //
+//              Copyright (c) 1998 - 2004 Conifer Software.               //
+//                          All Rights Reserved.                          //
+//      Distributed under the BSD Software License (see license.txt)      //
+////////////////////////////////////////////////////////////////////////////
+
+// bits.c
+
+// This module provides utilities to support the BitStream structure which is
+// used to read and write all WavPack audio data streams. It also contains a
+// wrapper for the stream I/O functions and a set of functions dealing with
+// endian-ness, both for enhancing portability. Finally, a debug wrapper for
+// the malloc() system is provided.
+
+#include "wavpack.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#if defined(WIN32)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+////////////////////////// Bitstream functions ////////////////////////////////
+
+#if defined(UNPACK) || defined(INFO_ONLY)
+
+// Open the specified BitStream and associate with the specified buffer.
+
+static void bs_read (Bitstream *bs);
+
+void bs_open_read (Bitstream *bs, uchar *buffer_start, uchar *buffer_end)
+{
+    bs->error = bs->sr = bs->bc = 0;
+    bs->ptr = (bs->buf = buffer_start) - 1;
+    bs->end = buffer_end;
+    bs->wrap = bs_read;
+}
+
+// This function is only called from the getbit() and getbits() macros when
+// the BitStream has been exhausted and more data is required. Sinve these
+// bistreams no longer access files, this function simple sets an error and
+// resets the buffer.
+
+static void bs_read (Bitstream *bs)
+{
+    bs->ptr = bs->buf - 1;
+    bs->error = 1;
+}
+
+// This function is called to close the bitstream. It returns the number of
+// full bytes actually read as bits.
+
+ulong bs_close_read (Bitstream *bs)
+{
+    ulong bytes_read;
+
+    if (bs->bc < 8)
+	bs->ptr++;
+
+    if ((bs->buf - bs->ptr) & 1)
+	bs->ptr++;
+
+    bytes_read = bs->ptr - bs->buf;
+    CLEAR (*bs);
+    return bytes_read;
+}
+
+#endif
+
+#ifdef PACK
+
+// Open the specified BitStream using the specified buffer pointers. It is
+// assumed that enough buffer space has been allocated for all data that will
+// be written, otherwise an error will be generated.
+
+static void bs_write (Bitstream *bs);
+
+void bs_open_write (Bitstream *bs, uchar *buffer_start, uchar *buffer_end)
+{
+    bs->error = bs->sr = bs->bc = 0;
+    bs->ptr = bs->buf = buffer_start;
+    bs->end = buffer_end;
+    bs->wrap = bs_write;
+}
+
+// This function is only called from the putbit() and putbits() macros when
+// the buffer is full, which is now flagged as an error.
+
+static void bs_write (Bitstream *bs)
+{
+    bs->ptr = bs->buf;
+    bs->error = 1;
+}
+
+// This function forces a flushing write of the specified BitStream, and
+// returns the total number of bytes written into the buffer.
+
+ulong bs_close_write (Bitstream *bs)
+{
+    ulong bytes_written;
+
+    if (bs->error)
+	return (ulong) -1;
+
+    while (bs->bc || ((bs->ptr - bs->buf) & 1)) putbit_1 (bs);
+    bytes_written = bs->ptr - bs->buf;
+    CLEAR (*bs);
+    return bytes_written;
+}
+
+#endif
+
+/////////////////////// Endian Correction Routines ////////////////////////////
+
+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++;
+    }
+}
+
+void native_to_little_endian (void *data, char *format)
+{
+    uchar *cp = (uchar *) data;
+    long temp;
+
+    while (*format) {
+	switch (*format) {
+	    case 'L':
+		temp = * (long *) cp;
+		*cp++ = (uchar) temp;
+		*cp++ = (uchar) (temp >> 8);
+		*cp++ = (uchar) (temp >> 16);
+		*cp++ = (uchar) (temp >> 24);
+		break;
+
+	    case 'S':
+		temp = * (short *) cp;
+		*cp++ = (uchar) temp;
+		*cp++ = (uchar) (temp >> 8);
+		break;
+
+	    default:
+		if (isdigit (*format))
+		    cp += *format - '0';
+
+		break;
+	}
+
+	format++;
+    }
+}
+
+////////////////////////// Debug Wrapper for Malloc ///////////////////////////
+
+#ifdef DEBUG_ALLOC
+
+void *vptrs [512];
+
+static void *add_ptr (void *ptr)
+{
+    int i;
+
+    for (i = 0; i < 512; ++i)
+	if (!vptrs [i]) {
+	    vptrs [i] = ptr;
+	    break;
+	}
+
+    if (i == 512)
+	error_line ("too many mallocs!");
+
+    return ptr;
+}
+
+static void *del_ptr (void *ptr)
+{
+    int i;
+
+    for (i = 0; i < 512; ++i)
+	if (vptrs [i] == ptr) {
+	    vptrs [i] = NULL;
+	    break;
+	}
+
+    if (i == 512)
+	error_line ("free invalid ptr!");
+
+    return ptr;
+}
+
+void *malloc_db (ulong size)
+{
+    if (size)
+	return add_ptr (malloc (size));
+    else
+	return NULL;
+}
+
+void free_db (void *ptr)
+{
+    if (ptr)
+	free (del_ptr (ptr));
+}
+
+void *realloc_db (void *ptr, ulong size)
+{
+    if (ptr && size)
+	return add_ptr (realloc (del_ptr (ptr), size));
+    else if (size)
+	return malloc_db (size);
+    else
+	free_db (ptr);
+
+    return NULL;
+}
+
+long dump_alloc (void)
+{
+    int i, j;
+
+    for (j = i = 0; i < 512; ++i)
+	if (vptrs [i])
+	    j++;
+
+    return j;
+}
+
+#endif
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/extra1.c	2005-03-30 10:17:50.000000000 +0100
@@ -0,0 +1,563 @@
+////////////////////////////////////////////////////////////////////////////
+//			     **** WAVPACK ****				  //
+//		    Hybrid Lossless Wavefile Compressor			  //
+//		Copyright (c) 1998 - 2004 Conifer Software.		  //
+//			    All Rights Reserved.			  //
+//      Distributed under the BSD Software License (see license.txt)      //
+////////////////////////////////////////////////////////////////////////////
+
+// extra1.c
+
+// This module handles the "extra" mode for mono files.
+
+#include "wavpack.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+// #define EXTRA_DUMP
+
+#ifdef DEBUG_ALLOC
+#define malloc malloc_db
+#define realloc realloc_db
+#define free free_db
+void *malloc_db (ulong size);
+void *realloc_db (void *ptr, ulong size);
+void free_db (void *ptr);
+long dump_alloc (void);
+#endif
+
+//////////////////////////////// local tables ///////////////////////////////
+
+extern const char default_terms [], high_terms [], fast_terms [];
+
+// #define MINMAX_WEIGHTS
+
+#ifdef MINMAX_WEIGHTS
+static long min_weight, max_weight;
+static int min_term, max_term;
+#endif
+
+static void decorr_mono_pass (long *in_samples, long *out_samples, ulong num_samples, struct decorr_pass *dpp, int dir)
+{
+    int m = 0;
+
+    dpp->sum_A = 0;
+
+#ifdef MINMAX_WEIGHTS
+    dpp->min = dpp->max = 0;
+#endif
+
+    if (dir < 0) {
+	out_samples += (num_samples - 1);
+	in_samples += (num_samples - 1);
+	dir = -1;
+    }
+    else
+	dir = 1;
+
+    if (dpp->term > MAX_TERM) {
+	while (num_samples--) {
+	    long left, sam_A;
+
+	    if (dpp->term & 1)
+		sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+	    else
+		sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+
+	    dpp->samples_A [1] = dpp->samples_A [0];
+	    dpp->samples_A [0] = left = in_samples [0];
+
+	    left -= apply_weight (dpp->weight_A, sam_A);
+	    update_weight (dpp->weight_A, dpp->delta, sam_A, left);
+	    dpp->sum_A += dpp->weight_A;
+#ifdef MINMAX_WEIGHTS
+	    if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A;
+	    if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A;
+#endif
+	    out_samples [0] = left;
+	    in_samples += dir;
+	    out_samples += dir;
+	}
+    }
+    else if (dpp->term > 0) {
+	while (num_samples--) {
+	    int k = (m + dpp->term) & (MAX_TERM - 1);
+	    long left, sam_A;
+
+	    sam_A = dpp->samples_A [m];
+	    dpp->samples_A [k] = left = in_samples [0];
+	    m = (m + 1) & (MAX_TERM - 1);
+
+	    left -= apply_weight (dpp->weight_A, sam_A);
+	    update_weight (dpp->weight_A, dpp->delta, sam_A, left);
+	    dpp->sum_A += dpp->weight_A;
+#ifdef MINMAX_WEIGHTS
+	    if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A;
+	    if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A;
+#endif
+	    out_samples [0] = left;
+	    in_samples += dir;
+	    out_samples += dir;
+	}
+    }
+
+#ifdef MINMAX_WEIGHTS
+    if (dpp->term != 0) {
+	if (dpp->max > max_weight) { max_weight = dpp->max; max_term = dpp->term; }
+	if (dpp->min < min_weight) { min_weight = dpp->min; min_term = dpp->term; }
+    }
+#endif
+
+    if (m && dpp->term > 0 && dpp->term <= MAX_TERM) {
+	long temp_A [MAX_TERM];
+	int k;
+
+	memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A));
+
+	for (k = 0; k < MAX_TERM; k++) {
+	    dpp->samples_A [k] = temp_A [m];
+	    m = (m + 1) & (MAX_TERM - 1);
+	}
+    }
+}
+
+static void reverse_mono_decorr (struct decorr_pass *dpp)
+{
+    if (dpp->term > MAX_TERM) {
+	long sam_A;
+
+	if (dpp->term & 1)
+	    sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+	else
+	    sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+
+	dpp->samples_A [1] = dpp->samples_A [0];
+	dpp->samples_A [0] = sam_A;
+
+	if (dpp->term & 1)
+	    sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+	else
+	    sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+
+	dpp->samples_A [1] = sam_A;
+    }
+    else if (dpp->term > 1) {
+	int i = 0, j = dpp->term - 1, cnt = dpp->term / 2;
+
+	while (cnt--) {
+	    i &= (MAX_TERM - 1);
+	    j &= (MAX_TERM - 1);
+	    dpp->samples_A [i] ^= dpp->samples_A [j];
+	    dpp->samples_A [j] ^= dpp->samples_A [i];
+	    dpp->samples_A [i++] ^= dpp->samples_A [j--];
+	}
+
+	CLEAR (dpp->samples_A);
+    }
+}
+
+static void decorr_mono_buffer (long *samples, long *outsamples, ulong num_samples, struct decorr_pass *dpp)
+{
+    int delta = dpp->delta, pre_delta, term = dpp->term;
+    struct decorr_pass dp;
+
+    if (delta == 7)
+	pre_delta = 7;
+    else if (delta < 2)
+	pre_delta = 3;
+    else
+	pre_delta = delta + 1;
+
+    CLEAR (dp);
+    dp.term = term;
+    dp.delta = pre_delta;
+    decorr_mono_pass (samples, outsamples, num_samples > 2048 ? 2048 : num_samples, &dp, -1);
+    dp.delta = delta;
+    reverse_mono_decorr (&dp);
+    memcpy (dpp->samples_A, dp.samples_A, sizeof (dp.samples_A));
+    dpp->weight_A = dp.weight_A;
+
+    if (delta == 0) {
+	dp.delta = 1;
+	decorr_mono_pass (samples, outsamples, num_samples, &dp, 1);
+	dp.delta = 0;
+	memcpy (dp.samples_A, dpp->samples_A, sizeof (dp.samples_A));
+	dpp->weight_A = dp.weight_A = dp.sum_A / num_samples;
+    }
+
+//    if (memcmp (dpp, &dp, sizeof (dp)))
+//	error_line ("decorr_passes don't match, delta = %d", delta);
+
+    decorr_mono_pass (samples, outsamples, num_samples, &dp, 1);
+}
+
+static void recurse_mono (WavpackContext *wpc, long *sampleptrs[], struct decorr_pass dps[],
+    int depth, int nterms, int delta, ulong input_bits, ulong *best_bits)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    int term, branches = ((wpc->config.extra_flags & EXTRA_BRANCHES) >> 6) - depth;
+    long *samples, *outsamples;
+    ulong term_bits [22], bits;
+
+    if (branches < 1 || depth + 1 == nterms)
+	branches = 1;
+
+    CLEAR (term_bits);
+    samples = sampleptrs [depth];
+    outsamples = sampleptrs [depth + 1];
+
+    for (term = 1; term <= 18; ++term) {
+	if (term == 17 && branches == 1 && depth + 1 < nterms)
+	    continue;
+
+	if (term >= 9 && term <= 16)
+	    if (term > MAX_TERM || !(wpc->config.flags & CONFIG_HIGH_FLAG) || (wpc->config.extra_flags & EXTRA_SKIP_8TO16))
+		continue;
+
+	if ((wpc->config.flags & CONFIG_FAST_FLAG) && (term >= 5 && term <= 16))
+	    continue;
+
+	dps [depth].term = term;
+	dps [depth].delta = delta;
+	decorr_mono_buffer (samples, outsamples, wps->wphdr.block_samples, &dps [depth]);
+	bits = log2buffer (outsamples, wps->wphdr.block_samples);
+
+	if (bits < *best_bits) {
+	    *best_bits = bits;
+	    CLEAR (wps->decorr_passes);
+	    memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * (depth + 1));
+	    memcpy (sampleptrs [nterms + 1], sampleptrs [depth + 1], wps->wphdr.block_samples * 4);
+	}
+
+	term_bits [term + 3] = bits;
+    }
+
+    while (depth + 1 < nterms && branches--) {
+	ulong local_best_bits = input_bits;
+	int best_term = 0, i;
+
+	for (i = 0; i < 22; ++i)
+	    if (term_bits [i] && term_bits [i] < local_best_bits) {
+		local_best_bits = term_bits [i];
+		term_bits [i] = 0;
+		best_term = i - 3;
+	    }
+
+	if (!best_term)
+	    break;
+
+	dps [depth].term = best_term;
+	dps [depth].delta = delta;
+	decorr_mono_buffer (samples, outsamples, wps->wphdr.block_samples, &dps [depth]);
+
+//	if (log2buffer (outsamples, wps->wphdr.block_samples * 2) != local_best_bits)
+//	    error_line ("data doesn't match!");
+
+	recurse_mono (wpc, sampleptrs, dps, depth + 1, nterms, delta, local_best_bits, best_bits);
+    }
+}
+
+static void delta_mono (WavpackContext *wpc, long *sampleptrs[], struct decorr_pass dps[],
+    int nterms, ulong *best_bits)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    int lower = FALSE, delta, d;
+    ulong bits;
+
+    if (wps->decorr_passes [0].term)
+	delta = wps->decorr_passes [0].delta;
+    else
+	return;
+
+    for (d = delta - 1; d >= 0; --d) {
+	int i;
+
+	if (!d && (wps->wphdr.flags & HYBRID_FLAG))
+	    break;
+
+	for (i = 0; i < nterms && wps->decorr_passes [i].term; ++i) {
+	    dps [i].term = wps->decorr_passes [i].term;
+	    dps [i].delta = d;
+	    decorr_mono_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]);
+	}
+
+	bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples);
+
+	if (bits < *best_bits) {
+	    lower = TRUE;
+	    *best_bits = bits;
+	    CLEAR (wps->decorr_passes);
+	    memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+	    memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4);
+	}
+	else
+	    break;
+    }
+
+    for (d = delta + 1; !lower && d <= 7; ++d) {
+	int i;
+
+	for (i = 0; i < nterms && wps->decorr_passes [i].term; ++i) {
+	    dps [i].term = wps->decorr_passes [i].term;
+	    dps [i].delta = d;
+	    decorr_mono_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]);
+	}
+
+	bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples);
+
+	if (bits < *best_bits) {
+	    *best_bits = bits;
+	    CLEAR (wps->decorr_passes);
+	    memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+	    memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4);
+	}
+	else
+	    break;
+    }
+}
+
+static void sort_mono (WavpackContext *wpc, long *sampleptrs[], struct decorr_pass dps[],
+    int nterms, ulong *best_bits)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    int reversed = TRUE;
+    ulong bits;
+
+    while (reversed) {
+	int ri, i;
+
+	memcpy (dps, wps->decorr_passes, sizeof (wps->decorr_passes));
+	reversed = FALSE;
+
+	for (ri = 0; ri < nterms && wps->decorr_passes [ri].term; ++ri) {
+
+	    if (ri + 1 >= nterms || !wps->decorr_passes [ri+1].term)
+		break;
+
+	    if (wps->decorr_passes [ri].term == wps->decorr_passes [ri+1].term) {
+		decorr_mono_buffer (sampleptrs [ri], sampleptrs [ri+1], wps->wphdr.block_samples, &dps [ri]);
+		continue;
+	    }
+
+	    dps [ri] = wps->decorr_passes [ri+1];
+	    dps [ri+1] = wps->decorr_passes [ri];
+
+	    for (i = ri; i < nterms && wps->decorr_passes [i].term; ++i)
+		decorr_mono_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]);
+
+	    bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples);
+
+	    if (bits < *best_bits) {
+		reversed = TRUE;
+		*best_bits = bits;
+		CLEAR (wps->decorr_passes);
+		memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+		memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4);
+	    }
+	    else {
+		dps [ri] = wps->decorr_passes [ri];
+		dps [ri+1] = wps->decorr_passes [ri+1];
+		decorr_mono_buffer (sampleptrs [ri], sampleptrs [ri+1], wps->wphdr.block_samples, &dps [ri]);
+	    }
+	}
+    }
+}
+
+#define EXTRA_ADVANCED (EXTRA_BRANCHES | EXTRA_SORT_FIRST | EXTRA_SORT_LAST | EXTRA_TRY_DELTAS)
+
+void analyze_mono (WavpackContext *wpc, long *samples)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+#ifdef EXTRA_DUMP
+    ulong bits, best_bits, default_bits, cnt;
+#else
+    ulong bits, best_bits, cnt;
+#endif
+    const char *decorr_terms = default_terms, *tp;
+    long *sampleptrs [MAX_NTERMS+2], *lptr;
+    struct decorr_pass dps [MAX_NTERMS];
+    int nterms, i;
+
+    CLEAR (wps->decorr_passes);
+    cnt = wps->wphdr.block_samples;
+    lptr = samples;
+
+    while (cnt--)
+	if (*lptr++)
+	    break;
+
+    if (cnt == (ulong) -1) {
+	scan_word (wps, samples, wps->wphdr.block_samples, -1);
+	wps->num_terms = 0;
+	return;
+    }
+
+    if (wpc->config.flags & CONFIG_HIGH_FLAG)
+	decorr_terms = high_terms;
+    else if (wpc->config.flags & CONFIG_FAST_FLAG)
+	decorr_terms = fast_terms;
+
+    for (nterms = 0, tp = decorr_terms; *tp; tp++)
+	if (*tp > 0)
+	    ++nterms;
+
+    if (wpc->config.extra_flags & EXTRA_TERMS)
+	if ((nterms += (wpc->config.extra_flags & EXTRA_TERMS) >> 10) > MAX_NTERMS)
+	    nterms = MAX_NTERMS;
+
+    for (i = 0; i < nterms + 2; ++i)
+	sampleptrs [i] = malloc (wps->wphdr.block_samples * 4);
+
+    memcpy (sampleptrs [nterms + 1], samples, wps->wphdr.block_samples * 4);
+    best_bits = log2buffer (sampleptrs [nterms + 1], wps->wphdr.block_samples);
+    memcpy (sampleptrs [0], samples, wps->wphdr.block_samples * 4);
+    CLEAR (dps);
+
+    for (tp = decorr_terms, i = 0; *tp; tp++)
+	if (*tp > 0) {
+	    dps [i].term = *tp;
+	    dps [i].delta = 2;
+	    decorr_mono_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]);
+	    ++i;
+	}
+
+#ifdef EXTRA_DUMP
+    default_bits = bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples);
+#else
+    bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples);
+#endif
+
+    if (bits < best_bits) {
+	best_bits = bits;
+	CLEAR (wps->decorr_passes);
+	memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+	memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4);
+    }
+
+    if ((wps->wphdr.flags & HYBRID_FLAG) && (wpc->config.extra_flags & EXTRA_ADVANCED)) {
+	int shaping_weight, new = wps->wphdr.flags & NEW_SHAPING;
+	long *rptr = sampleptrs [nterms + 1], error = 0, temp;
+
+	scan_word (wps, rptr, wps->wphdr.block_samples, -1);
+	cnt = wps->wphdr.block_samples;
+	lptr = sampleptrs [0];
+
+	if (wps->wphdr.flags & HYBRID_SHAPE) {
+	    while (cnt--) {
+		shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
+		temp = -apply_weight (shaping_weight, error);
+
+		if (new && shaping_weight < 0 && temp) {
+		    if (temp == error)
+			temp = (temp < 0) ? temp + 1 : temp - 1;
+
+		    lptr [0] += (error = nosend_word (wps, rptr [0], 0) - rptr [0] + temp);
+		}
+		else
+		    lptr [0] += (error = nosend_word (wps, rptr [0], 0) - rptr [0]) + temp;
+
+		lptr++;
+		rptr++;
+	    }
+
+	    wps->dc.shaping_acc [0] -= wps->dc.shaping_delta [0] * wps->wphdr.block_samples;
+	}
+	else
+	    while (cnt--) {
+		lptr [0] += nosend_word (wps, rptr [0], 0) - rptr [0];
+		lptr++;
+		rptr++;
+	    }
+
+	memcpy (dps, wps->decorr_passes, sizeof (dps));
+
+	for (i = 0; i < nterms && dps [i].term; ++i)
+	    decorr_mono_buffer (sampleptrs [i], sampleptrs [i + 1], wps->wphdr.block_samples, dps + i);
+
+#ifdef EXTRA_DUMP
+	best_bits = default_bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples);
+#else
+	best_bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples);
+#endif
+
+	CLEAR (wps->decorr_passes);
+	memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+	memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4);
+    }
+
+    if (wpc->config.extra_flags & EXTRA_BRANCHES)
+	recurse_mono (wpc, sampleptrs, dps, 0, nterms, (int) floor (wps->delta_decay + 0.5),
+	    log2buffer (sampleptrs [0], wps->wphdr.block_samples), &best_bits);
+
+    if (wpc->config.extra_flags & EXTRA_SORT_FIRST)
+	sort_mono (wpc, sampleptrs, dps, nterms, &best_bits);
+
+    if (wpc->config.extra_flags & EXTRA_TRY_DELTAS) {
+	delta_mono (wpc, sampleptrs, dps, nterms, &best_bits);
+
+	if ((wpc->config.extra_flags & EXTRA_ADJUST_DELTAS) && wps->decorr_passes [0].term)
+	    wps->delta_decay = (wps->delta_decay * 2.0 + wps->decorr_passes [0].delta) / 3.0;
+	else
+	    wps->delta_decay = 2.0;
+    }
+
+    if (wpc->config.extra_flags & EXTRA_SORT_LAST)
+	sort_mono (wpc, sampleptrs, dps, nterms, &best_bits);
+
+#if 0
+    memcpy (dps, wps->decorr_passes, sizeof (dps));
+
+    for (i = 0; i < nterms && dps [i].term; ++i)
+	decorr_mono_pass (sampleptrs [i], sampleptrs [i + 1], wps->wphdr.block_samples, dps + i, 1);
+
+    if (log2buffer (sampleptrs [i], wps->wphdr.block_samples) != best_bits)
+	error_line ("(1) samples do not match!");
+
+    if (log2buffer (sampleptrs [nterms + 1], wps->wphdr.block_samples) != best_bits)
+	error_line ("(2) samples do not match!");
+#endif
+
+    scan_word (wps, sampleptrs [nterms + 1], wps->wphdr.block_samples, -1);
+
+#ifdef EXTRA_DUMP
+    if (wpc->config.extra_flags & EXTRA_DUMP_TERMS) {
+	char string [256], substring [20];
+	int i;
+
+	sprintf (string, "M: delta = %.4f%%, terms =",
+	    ((double) best_bits - default_bits) / 256.0 / wps->wphdr.block_samples / 32.0 * 100.0);
+
+	for (i = 0; i < nterms; ++i) {
+	    if (wps->decorr_passes [i].term) {
+		if (i && wps->decorr_passes [i-1].delta == wps->decorr_passes [i].delta)
+		    sprintf (substring, " %d", wps->decorr_passes [i].term);
+		else
+		    sprintf (substring, " %d->%d", wps->decorr_passes [i].term,
+			wps->decorr_passes [i].delta);
+	    }
+	    else
+		sprintf (substring, " *");
+
+	    strcat (string, substring);
+	}
+
+	error_line (string);
+    }
+#endif
+
+    for (i = 0; i < nterms; ++i)
+	if (!wps->decorr_passes [i].term)
+	    break;
+
+    wps->num_terms = i;
+
+    for (i = 0; i < nterms + 2; ++i)
+	free (sampleptrs [i]);
+
+#ifdef MINMAX_WEIGHTS
+    error_line ("weight range = %ld (%d) to %ld (%d)", min_weight, min_term, max_weight, max_term);
+#endif
+}
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/extra2.c	2005-03-30 10:17:50.000000000 +0100
@@ -0,0 +1,768 @@
+////////////////////////////////////////////////////////////////////////////
+//			     **** WAVPACK ****				  //
+//		    Hybrid Lossless Wavefile Compressor			  //
+//		Copyright (c) 1998 - 2004 Conifer Software.		  //
+//			    All Rights Reserved.			  //
+//      Distributed under the BSD Software License (see license.txt)      //
+////////////////////////////////////////////////////////////////////////////
+
+// extra2.c
+
+// This module handles the "extra" mode for stereo files.
+
+#include "wavpack.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+// #define EXTRA_DUMP
+
+#ifdef DEBUG_ALLOC
+#define malloc malloc_db
+#define realloc realloc_db
+#define free free_db
+void *malloc_db (ulong size);
+void *realloc_db (void *ptr, ulong size);
+void free_db (void *ptr);
+long dump_alloc (void);
+#endif
+
+//////////////////////////////// local tables ///////////////////////////////
+
+extern const char default_terms [], high_terms [], fast_terms [];
+
+// #define MINMAX_WEIGHTS
+
+#ifdef MINMAX_WEIGHTS
+static long min_weight, max_weight;
+static int min_term, max_term;
+#endif
+
+static void decorr_stereo_pass (long *in_samples, long *out_samples, long num_samples, struct decorr_pass *dpp, int dir)
+{
+    int m = 0;
+
+    dpp->sum_A = dpp->sum_B = 0;
+
+#ifdef MINMAX_WEIGHTS
+    dpp->min = dpp->max = 0;
+#endif
+
+    if (dir < 0) {
+	out_samples += (num_samples - 1) * 2;
+	in_samples += (num_samples - 1) * 2;
+	dir = -2;
+    }
+    else
+	dir = 2;
+
+    if (dpp->term > MAX_TERM) {
+	while (num_samples--) {
+	    long left, right;
+	    long sam_A, sam_B;
+
+	    if (dpp->term & 1) {
+		sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+		sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
+	    }
+	    else {
+		sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+		sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
+	    }
+
+	    dpp->samples_A [1] = dpp->samples_A [0];
+	    dpp->samples_B [1] = dpp->samples_B [0];
+	    dpp->samples_A [0] = left = in_samples [0];
+	    dpp->samples_B [0] = right = in_samples [1];
+
+	    left -= apply_weight (dpp->weight_A, sam_A);
+	    right -= apply_weight (dpp->weight_B, sam_B);
+	    update_weight (dpp->weight_A, dpp->delta, sam_A, left);
+	    update_weight (dpp->weight_B, dpp->delta, sam_B, right);
+	    dpp->sum_A += dpp->weight_A;
+	    dpp->sum_B += dpp->weight_B;
+#ifdef MINMAX_WEIGHTS
+	    if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A;
+	    if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B;
+	    if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A;
+	    if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B;
+#endif
+	    out_samples [0] = left;
+	    out_samples [1] = right;
+	    in_samples += dir;
+	    out_samples += dir;
+	}
+    }
+    else if (dpp->term > 0) {
+	while (num_samples--) {
+	    int k = (m + dpp->term) & (MAX_TERM - 1);
+	    long left, right;
+	    long sam_A, sam_B;
+
+	    sam_A = dpp->samples_A [m];
+	    sam_B = dpp->samples_B [m];
+	    dpp->samples_A [k] = left = in_samples [0];
+	    dpp->samples_B [k] = right = in_samples [1];
+	    m = (m + 1) & (MAX_TERM - 1);
+
+	    left -= apply_weight (dpp->weight_A, sam_A);
+	    right -= apply_weight (dpp->weight_B, sam_B);
+	    update_weight (dpp->weight_A, dpp->delta, sam_A, left);
+	    update_weight (dpp->weight_B, dpp->delta, sam_B, right);
+	    dpp->sum_A += dpp->weight_A;
+	    dpp->sum_B += dpp->weight_B;
+#ifdef MINMAX_WEIGHTS
+	    if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A;
+	    if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B;
+	    if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A;
+	    if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B;
+#endif
+	    out_samples [0] = left;
+	    out_samples [1] = right;
+	    in_samples += dir;
+	    out_samples += dir;
+	}
+    }
+    else if (dpp->term == -1) {
+	while (num_samples--) {
+	    long left = in_samples [0];
+	    long right = in_samples [1];
+	    long sam_A = dpp->samples_A [0], sam_B = left;
+
+	    dpp->samples_A [0] = right;
+	    right -= apply_weight (dpp->weight_B, sam_B);
+	    update_weight_clip (dpp->weight_B, dpp->delta, sam_B, right);
+	    left -= apply_weight (dpp->weight_A, sam_A);
+	    update_weight_clip (dpp->weight_A, dpp->delta, sam_A, left);
+	    dpp->sum_A += dpp->weight_A;
+	    dpp->sum_B += dpp->weight_B;
+#ifdef MINMAX_WEIGHTS
+	    if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A;
+	    if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B;
+	    if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A;
+	    if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B;
+#endif
+	    out_samples [0] = left;
+	    out_samples [1] = right;
+	    in_samples += dir;
+	    out_samples += dir;
+	}
+    }
+    else if (dpp->term == -2) {
+	while (num_samples--) {
+	    long left = in_samples [0];
+	    long right = in_samples [1];
+	    long sam_B = dpp->samples_B [0], sam_A = right;
+
+	    dpp->samples_B [0] = left;
+	    left -= apply_weight (dpp->weight_A, sam_A);
+	    update_weight_clip (dpp->weight_A, dpp->delta, sam_A, left);
+	    right -= apply_weight (dpp->weight_B, sam_B);
+	    update_weight_clip (dpp->weight_B, dpp->delta, sam_B, right);
+	    dpp->sum_A += dpp->weight_A;
+	    dpp->sum_B += dpp->weight_B;
+#ifdef MINMAX_WEIGHTS
+	    if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A;
+	    if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B;
+	    if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A;
+	    if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B;
+#endif
+	    out_samples [0] = left;
+	    out_samples [1] = right;
+	    in_samples += dir;
+	    out_samples += dir;
+	}
+    }
+    else if (dpp->term == -3) {
+	while (num_samples--) {
+	    long left = in_samples [0];
+	    long right = in_samples [1];
+	    long sam_A = dpp->samples_A [0], sam_B = dpp->samples_B [0];
+
+	    dpp->samples_A [0] = right;
+	    dpp->samples_B [0] = left;
+	    left -= apply_weight (dpp->weight_A, sam_A);
+	    update_weight_clip (dpp->weight_A, dpp->delta, sam_A, left);
+	    right -= apply_weight (dpp->weight_B, sam_B);
+	    update_weight_clip (dpp->weight_B, dpp->delta, sam_B, right);
+	    dpp->sum_A += dpp->weight_A;
+	    dpp->sum_B += dpp->weight_B;
+#ifdef MINMAX_WEIGHTS
+	    if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A;
+	    if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B;
+	    if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A;
+	    if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B;
+#endif
+	    out_samples [0] = left;
+	    out_samples [1] = right;
+	    in_samples += dir;
+	    out_samples += dir;
+	}
+    }
+
+#ifdef MINMAX_WEIGHTS
+    if (dpp->term != 0) {
+	if (dpp->max > max_weight) { max_weight = dpp->max; max_term = dpp->term; }
+	if (dpp->min < min_weight) { min_weight = dpp->min; min_term = dpp->term; }
+    }
+#endif
+
+    if (m && dpp->term > 0 && dpp->term <= MAX_TERM) {
+	long temp_A [MAX_TERM], temp_B [MAX_TERM];
+	int k;
+
+	memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A));
+	memcpy (temp_B, dpp->samples_B, sizeof (dpp->samples_B));
+
+	for (k = 0; k < MAX_TERM; k++) {
+	    dpp->samples_A [k] = temp_A [m];
+	    dpp->samples_B [k] = temp_B [m];
+	    m = (m + 1) & (MAX_TERM - 1);
+	}
+    }
+}
+
+static void reverse_decorr (struct decorr_pass *dpp)
+{
+    if (dpp->term > MAX_TERM) {
+	long sam_A, sam_B;
+
+	if (dpp->term & 1) {
+	    sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+	    sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
+	}
+	else {
+	    sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+	    sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
+	}
+
+	dpp->samples_A [1] = dpp->samples_A [0];
+	dpp->samples_B [1] = dpp->samples_B [0];
+	dpp->samples_A [0] = sam_A;
+	dpp->samples_B [0] = sam_B;
+
+	if (dpp->term & 1) {
+	    sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+	    sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
+	}
+	else {
+	    sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+	    sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
+	}
+
+	dpp->samples_A [1] = sam_A;
+	dpp->samples_B [1] = sam_B;
+    }
+    else if (dpp->term > 1) {
+	int i = 0, j = dpp->term - 1, cnt = dpp->term / 2;
+
+	while (cnt--) {
+	    i &= (MAX_TERM - 1);
+	    j &= (MAX_TERM - 1);
+	    dpp->samples_A [i] ^= dpp->samples_A [j];
+	    dpp->samples_A [j] ^= dpp->samples_A [i];
+	    dpp->samples_A [i] ^= dpp->samples_A [j];
+	    dpp->samples_B [i] ^= dpp->samples_B [j];
+	    dpp->samples_B [j] ^= dpp->samples_B [i];
+	    dpp->samples_B [i++] ^= dpp->samples_B [j--];
+	}
+
+	CLEAR (dpp->samples_A);
+	CLEAR (dpp->samples_B);
+    }
+    else if (dpp->term == -1) {
+    }
+    else if (dpp->term == -2) {
+    }
+    else if (dpp->term == -3) {
+    }
+}
+
+static void decorr_stereo_buffer (long *samples, long *outsamples, long num_samples, struct decorr_pass *dpp)
+{
+    int delta = dpp->delta, pre_delta;
+    int term = dpp->term;
+    struct decorr_pass dp;
+
+    if (delta == 7)
+	pre_delta = 7;
+    else if (delta < 2)
+	pre_delta = 3;
+    else
+	pre_delta = delta + 1;
+
+    CLEAR (dp);
+    dp.term = term;
+    dp.delta = pre_delta;
+    decorr_stereo_pass (samples, outsamples, num_samples > 2048 ? 2048 : num_samples, &dp, -1);
+    dp.delta = delta;
+    reverse_decorr (&dp);
+    memcpy (dpp->samples_A, dp.samples_A, sizeof (dp.samples_A));
+    memcpy (dpp->samples_B, dp.samples_B, sizeof (dp.samples_B));
+    dpp->weight_A = dp.weight_A;
+    dpp->weight_B = dp.weight_B;
+
+    if (delta == 0) {
+	dp.delta = 1;
+	decorr_stereo_pass (samples, outsamples, num_samples, &dp, 1);
+	dp.delta = 0;
+	memcpy (dp.samples_A, dpp->samples_A, sizeof (dp.samples_A));
+	memcpy (dp.samples_B, dpp->samples_B, sizeof (dp.samples_B));
+	dpp->weight_A = dp.weight_A = dp.sum_A / num_samples;
+	dpp->weight_B = dp.weight_B = dp.sum_B / num_samples;
+    }
+
+//    if (memcmp (dpp, &dp, sizeof (dp)))
+//	error_line ("decorr_passes don't match, delta = %d", delta);
+
+    decorr_stereo_pass (samples, outsamples, num_samples, &dp, 1);
+}
+
+static void recurse_stereo (WavpackContext *wpc, long *sampleptrs[], struct decorr_pass dps[],
+    int depth, int nterms, int delta, ulong input_bits, ulong *best_bits)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    int term, branches = ((wpc->config.extra_flags & EXTRA_BRANCHES) >> 6) - depth;
+    long *samples, *outsamples;
+    ulong term_bits [22], bits;
+
+    if (branches < 1 || depth + 1 == nterms)
+	branches = 1;
+
+    CLEAR (term_bits);
+    samples = sampleptrs [depth];
+    outsamples = sampleptrs [depth + 1];
+
+    for (term = -3; term <= 18; ++term) {
+	if (!term)
+	    continue;
+
+	if (term == 17 && branches == 1 && depth + 1 < nterms)
+	    continue;
+
+	if (term == -1 || term == -2)
+	    if (!(wps->wphdr.flags & CROSS_DECORR))
+		continue;
+
+	if (term >= 9 && term <= 16)
+	    if (term > MAX_TERM || !(wpc->config.flags & CONFIG_HIGH_FLAG) || (wpc->config.extra_flags & EXTRA_SKIP_8TO16))
+		continue;
+
+	if ((wpc->config.flags & CONFIG_FAST_FLAG) && (term >= 5 && term <= 16))
+	    continue;
+
+	dps [depth].term = term;
+	dps [depth].delta = delta;
+	decorr_stereo_buffer (samples, outsamples, wps->wphdr.block_samples, &dps [depth]);
+	bits = log2buffer (outsamples, wps->wphdr.block_samples * 2);
+
+	if (bits < *best_bits) {
+	    *best_bits = bits;
+	    CLEAR (wps->decorr_passes);
+	    memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * (depth + 1));
+	    memcpy (sampleptrs [nterms + 1], sampleptrs [depth + 1], wps->wphdr.block_samples * 8);
+	}
+
+	term_bits [term + 3] = bits;
+    }
+
+    while (depth + 1 < nterms && branches--) {
+	ulong local_best_bits = input_bits;
+	int best_term = 0, i;
+
+	for (i = 0; i < 22; ++i)
+	    if (term_bits [i] && term_bits [i] < local_best_bits) {
+		local_best_bits = term_bits [i];
+		term_bits [i] = 0;
+		best_term = i - 3;
+	    }
+
+	if (!best_term)
+	    break;
+
+	dps [depth].term = best_term;
+	dps [depth].delta = delta;
+	decorr_stereo_buffer (samples, outsamples, wps->wphdr.block_samples, &dps [depth]);
+
+//	if (log2buffer (outsamples, wps->wphdr.block_samples * 2) != local_best_bits)
+//	    error_line ("data doesn't match!");
+
+	recurse_stereo (wpc, sampleptrs, dps, depth + 1, nterms, delta, local_best_bits, best_bits);
+    }
+}
+
+static void delta_stereo (WavpackContext *wpc, long *sampleptrs[], struct decorr_pass dps[],
+    int nterms, ulong *best_bits)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    int lower = FALSE;
+    int delta, d;
+    ulong bits;
+
+    if (wps->decorr_passes [0].term)
+	delta = wps->decorr_passes [0].delta;
+    else
+	return;
+
+    for (d = delta - 1; d >= 0; --d) {
+	int i;
+
+	if (!d && (wps->wphdr.flags & HYBRID_FLAG))
+	    break;
+
+	for (i = 0; i < nterms && wps->decorr_passes [i].term; ++i) {
+	    dps [i].term = wps->decorr_passes [i].term;
+	    dps [i].delta = d;
+	    decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]);
+	}
+
+	bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2);
+
+	if (bits < *best_bits) {
+	    lower = TRUE;
+	    *best_bits = bits;
+	    CLEAR (wps->decorr_passes);
+	    memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+	    memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8);
+	}
+	else
+	    break;
+    }
+
+    for (d = delta + 1; !lower && d <= 7; ++d) {
+	int i;
+
+	for (i = 0; i < nterms && wps->decorr_passes [i].term; ++i) {
+	    dps [i].term = wps->decorr_passes [i].term;
+	    dps [i].delta = d;
+	    decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]);
+	}
+
+	bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2);
+
+	if (bits < *best_bits) {
+	    *best_bits = bits;
+	    CLEAR (wps->decorr_passes);
+	    memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+	    memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8);
+	}
+	else
+	    break;
+    }
+}
+
+static void sort_stereo (WavpackContext *wpc, long *sampleptrs[], struct decorr_pass dps[],
+    int nterms, ulong *best_bits)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    int reversed = TRUE;
+    ulong bits;
+
+    while (reversed) {
+	int ri, i;
+
+	memcpy (dps, wps->decorr_passes, sizeof (wps->decorr_passes));
+	reversed = FALSE;
+
+	for (ri = 0; ri < nterms && wps->decorr_passes [ri].term; ++ri) {
+
+	    if (ri + 1 >= nterms || !wps->decorr_passes [ri+1].term)
+		break;
+
+	    if (wps->decorr_passes [ri].term == wps->decorr_passes [ri+1].term) {
+		decorr_stereo_buffer (sampleptrs [ri], sampleptrs [ri+1], wps->wphdr.block_samples, &dps [ri]);
+		continue;
+	    }
+
+	    dps [ri] = wps->decorr_passes [ri+1];
+	    dps [ri+1] = wps->decorr_passes [ri];
+
+	    for (i = ri; i < nterms && wps->decorr_passes [i].term; ++i)
+		decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]);
+
+	    bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2);
+
+	    if (bits < *best_bits) {
+		reversed = TRUE;
+		*best_bits = bits;
+		CLEAR (wps->decorr_passes);
+		memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+		memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8);
+	    }
+	    else {
+		dps [ri] = wps->decorr_passes [ri];
+		dps [ri+1] = wps->decorr_passes [ri+1];
+		decorr_stereo_buffer (sampleptrs [ri], sampleptrs [ri+1], wps->wphdr.block_samples, &dps [ri]);
+	    }
+	}
+    }
+}
+
+#define EXTRA_ADVANCED (EXTRA_BRANCHES | EXTRA_SORT_FIRST | EXTRA_SORT_LAST | EXTRA_TRY_DELTAS)
+
+void analyze_stereo (WavpackContext *wpc, long *samples)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+#ifdef EXTRA_DUMP
+    ulong bits, best_bits, default_bits, cnt;
+#else
+    ulong bits, best_bits, cnt;
+#endif
+    const char *decorr_terms = default_terms, *tp;
+    long *sampleptrs [MAX_NTERMS+2], *lptr;
+    struct decorr_pass dps [MAX_NTERMS];
+    int nterms, i;
+
+
+    CLEAR (wps->decorr_passes);
+    cnt = wps->wphdr.block_samples * 2;
+    lptr = samples;
+
+    while (cnt--)
+	if (*lptr++)
+	    break;
+
+    if (cnt == (ulong) -1) {
+	scan_word (wps, samples, wps->wphdr.block_samples, -1);
+	wps->num_terms = 0;
+	return;
+    }
+
+    if (wpc->config.flags & CONFIG_HIGH_FLAG)
+	decorr_terms = high_terms;
+    else if (wpc->config.flags & CONFIG_FAST_FLAG)
+	decorr_terms = fast_terms;
+
+    nterms = strlen (decorr_terms);
+
+    if (wpc->config.extra_flags & EXTRA_TERMS)
+	if ((nterms += (wpc->config.extra_flags & EXTRA_TERMS) >> 10) > MAX_NTERMS)
+	    nterms = MAX_NTERMS;
+
+    for (i = 0; i < nterms + 2; ++i)
+	sampleptrs [i] = malloc (wps->wphdr.block_samples * 8);
+
+    memcpy (sampleptrs [nterms + 1], samples, wps->wphdr.block_samples * 8);
+    best_bits = log2buffer (sampleptrs [nterms + 1], wps->wphdr.block_samples * 2);
+
+    if ((wpc->config.extra_flags & EXTRA_STEREO_MODES) || !(wps->wphdr.flags & JOINT_STEREO)) {
+	memcpy (sampleptrs [0], samples, wps->wphdr.block_samples * 8);
+
+	CLEAR (dps);
+
+	for (tp = decorr_terms, i = 0; *tp;) {
+	    if (*tp > 0 || (wps->wphdr.flags & CROSS_DECORR))
+		dps [i].term = *tp++;
+	    else {
+		dps [i].term = -3;
+		tp++;
+	    }
+
+	    dps [i].delta = 2;
+	    decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]);
+	    ++i;
+	}
+
+#ifdef EXTRA_DUMP
+	default_bits = bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2);
+#else
+	bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2);
+#endif
+
+	wps->wphdr.flags &= ~JOINT_STEREO;
+
+	if (bits < best_bits) {
+	    best_bits = bits;
+	    CLEAR (wps->decorr_passes);
+	    memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+	    memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8);
+	}
+    }
+
+    if ((wpc->config.extra_flags & EXTRA_STEREO_MODES) || (wps->wphdr.flags & JOINT_STEREO)) {
+	memcpy (sampleptrs [0], samples, wps->wphdr.block_samples * 8);
+	cnt = wps->wphdr.block_samples;
+	lptr = sampleptrs [0];
+
+	while (cnt--) {
+	    lptr [1] += ((lptr [0] -= lptr [1]) >> 1);
+	    lptr += 2;
+	}
+
+	CLEAR (dps);
+
+	for (tp = decorr_terms, i = 0; *tp;) {
+	    if (*tp > 0 || (wps->wphdr.flags & CROSS_DECORR))
+		dps [i].term = *tp++;
+	    else {
+		dps [i].term = -3;
+		tp++;
+	    }
+
+	    dps [i].delta = 2;
+	    decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]);
+	    ++i;
+	}
+
+#ifdef EXTRA_DUMP
+	default_bits = bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2);
+#else
+	bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2);
+#endif
+
+	wps->wphdr.flags |= JOINT_STEREO;
+
+	if (bits < best_bits) {
+	    best_bits = bits;
+	    CLEAR (wps->decorr_passes);
+	    memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+	    memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8);
+	}
+	else {
+	    memcpy (sampleptrs [0], samples, wps->wphdr.block_samples * 8);
+	    wps->wphdr.flags &= ~JOINT_STEREO;
+	}
+    }
+
+    if ((wps->wphdr.flags & HYBRID_FLAG) && (wpc->config.extra_flags & EXTRA_ADVANCED)) {
+	int shaping_weight, new = wps->wphdr.flags & NEW_SHAPING;
+	long *rptr = sampleptrs [nterms + 1], error [2], temp;
+
+	scan_word (wps, rptr, wps->wphdr.block_samples, -1);
+	cnt = wps->wphdr.block_samples;
+	lptr = sampleptrs [0];
+	CLEAR (error);
+
+	if (wps->wphdr.flags & HYBRID_SHAPE) {
+	    while (cnt--) {
+		shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
+		temp = -apply_weight (shaping_weight, error [0]);
+
+		if (new && shaping_weight < 0 && temp) {
+		    if (temp == error [0])
+			temp = (temp < 0) ? temp + 1 : temp - 1;
+
+		    lptr [0] += (error [0] = nosend_word (wps, rptr [0], 0) - rptr [0] + temp);
+		}
+		else
+		    lptr [0] += (error [0] = nosend_word (wps, rptr [0], 0) - rptr [0]) + temp;
+
+		shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16;
+		temp = -apply_weight (shaping_weight, error [1]);
+
+		if (new && shaping_weight < 0 && temp) {
+		    if (temp == error [1])
+			temp = (temp < 0) ? temp + 1 : temp - 1;
+
+		    lptr [1] += (error [1] = nosend_word (wps, rptr [1], 1) - rptr [1] + temp);
+		}
+		else
+		    lptr [1] += (error [1] = nosend_word (wps, rptr [1], 1) - rptr [1]) + temp;
+
+		lptr += 2;
+		rptr += 2;
+	    }
+
+	    wps->dc.shaping_acc [0] -= wps->dc.shaping_delta [0] * wps->wphdr.block_samples;
+	    wps->dc.shaping_acc [1] -= wps->dc.shaping_delta [1] * wps->wphdr.block_samples;
+	}
+	else
+	    while (cnt--) {
+		lptr [0] += nosend_word (wps, rptr [0], 0) - rptr [0];
+		lptr [1] += nosend_word (wps, rptr [1], 1) - rptr [1];
+		lptr += 2;
+		rptr += 2;
+	    }
+
+	memcpy (dps, wps->decorr_passes, sizeof (dps));
+
+	for (i = 0; i < nterms && dps [i].term; ++i)
+	    decorr_stereo_buffer (sampleptrs [i], sampleptrs [i + 1], wps->wphdr.block_samples, dps + i);
+
+#ifdef EXTRA_DUMP
+	best_bits = default_bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2);
+#else
+	best_bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2);
+#endif
+
+	CLEAR (wps->decorr_passes);
+	memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i);
+	memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8);
+    }
+
+    if (wpc->config.extra_flags & EXTRA_BRANCHES)
+	recurse_stereo (wpc, sampleptrs, dps, 0, nterms, (int) floor (wps->delta_decay + 0.5),
+	    log2buffer (sampleptrs [0], wps->wphdr.block_samples * 2), &best_bits);
+
+    if (wpc->config.extra_flags & EXTRA_SORT_FIRST)
+	sort_stereo (wpc, sampleptrs, dps, nterms, &best_bits);
+
+    if (wpc->config.extra_flags & EXTRA_TRY_DELTAS) {
+	delta_stereo (wpc, sampleptrs, dps, nterms, &best_bits);
+
+	if ((wpc->config.extra_flags & EXTRA_ADJUST_DELTAS) && wps->decorr_passes [0].term)
+	    wps->delta_decay = (wps->delta_decay * 2.0 + wps->decorr_passes [0].delta) / 3.0;
+	else
+	    wps->delta_decay = 2.0;
+    }
+
+    if (wpc->config.extra_flags & EXTRA_SORT_LAST)
+	sort_stereo (wpc, sampleptrs, dps, nterms, &best_bits);
+
+#if 0
+    memcpy (dps, wps->decorr_passes, sizeof (dps));
+
+    for (i = 0; i < nterms && dps [i].term; ++i)
+	decorr_stereo_pass (sampleptrs [i], sampleptrs [i + 1], wps->wphdr.block_samples, dps + i, 1);
+
+    if (log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2) != best_bits)
+	error_line ("(1) samples do not match!");
+
+    if (log2buffer (sampleptrs [nterms + 1], wps->wphdr.block_samples * 2) != best_bits)
+	error_line ("(2) samples do not match!");
+#endif
+
+    scan_word (wps, sampleptrs [nterms + 1], wps->wphdr.block_samples, -1);
+
+#ifdef EXTRA_DUMP
+    if (1) {
+	char string [256], substring [20];
+	int i;
+
+	sprintf (string, "%s: delta = %.4f%%, terms =",
+	    (wps->wphdr.flags & JOINT_STEREO) ? "JS" : "TS",
+		((double) best_bits - default_bits) / 256.0 / wps->wphdr.block_samples / 32.0 * 100.0);
+
+	for (i = 0; i < nterms; ++i) {
+	    if (wps->decorr_passes [i].term) {
+		if (i && wps->decorr_passes [i-1].delta == wps->decorr_passes [i].delta)
+		    sprintf (substring, " %d", wps->decorr_passes [i].term);
+		else
+		    sprintf (substring, " %d->%d", wps->decorr_passes [i].term,
+			wps->decorr_passes [i].delta);
+	    }
+	    else
+		sprintf (substring, " *");
+
+	    strcat (string, substring);
+	}
+
+	error_line (string);
+    }
+#endif
+
+    for (i = 0; i < nterms; ++i)
+	if (!wps->decorr_passes [i].term)
+	    break;
+
+    wps->num_terms = i;
+
+    for (i = 0; i < nterms + 2; ++i)
+	free (sampleptrs [i]);
+
+#ifdef MINMAX_WEIGHTS
+    error_line ("weight range = %ld (%d) to %ld (%d)", min_weight, min_term, max_weight, max_term);
+#endif
+}
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/float.c	2005-03-30 10:17:50.000000000 +0100
@@ -0,0 +1,371 @@
+////////////////////////////////////////////////////////////////////////////
+//			     **** WAVPACK ****				  //
+//		    Hybrid Lossless Wavefile Compressor			  //
+//		Copyright (c) 1998 - 2004 Conifer Software.		  //
+//			    All Rights Reserved.			  //
+//      Distributed under the BSD Software License (see license.txt)      //
+////////////////////////////////////////////////////////////////////////////
+
+// float.c
+
+#include "wavpack.h"
+
+#include <stdlib.h>
+
+#ifdef DEBUG_ALLOC
+#define malloc malloc_db
+#define realloc realloc_db
+#define free free_db
+void *malloc_db (ulong size);
+void *realloc_db (void *ptr, ulong size);
+void free_db (void *ptr);
+long dump_alloc (void);
+#endif
+
+#ifdef PACK
+
+void write_float_info (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    char *byteptr;
+
+    byteptr = wpmd->data = malloc (4);
+    wpmd->id = ID_FLOAT_INFO;
+    *byteptr++ = wps->float_flags;
+    *byteptr++ = wps->float_shift;
+    *byteptr++ = wps->float_max_exp;
+    *byteptr++ = wps->float_norm_exp;
+    wpmd->byte_length = byteptr - (char *) wpmd->data;
+}
+
+int scan_float_data (WavpackStream *wps, f32 *values, long num_values)
+{
+    long shifted_ones = 0, shifted_zeros = 0, shifted_both = 0;
+    long false_zeros = 0, neg_zeros = 0;
+    ulong ordata = 0, crc = 0xffffffff;
+    int max_exp = 0, shift_count;
+    long count, value;
+    f32 *dp;
+
+    wps->float_shift = wps->float_flags = 0;
+
+    for (dp = values, count = num_values; count--; dp++) {
+	crc = crc * 27 + dp->mantissa * 9 + dp->exponent * 3 + dp->sign;
+
+	if (dp->exponent > max_exp && dp->exponent < 255)
+	    max_exp = dp->exponent;
+    }
+
+    wps->crc_x = crc;
+
+    for (dp = values, count = num_values; count--; dp++) {
+	if (dp->exponent == 255) {
+	    wps->float_flags |= FLOAT_EXCEPTIONS;
+	    value = 0x1000000;
+	    shift_count = 0;
+	}
+	else if (dp->exponent) {
+	    shift_count = max_exp - dp->exponent;
+	    value = 0x800000 + dp->mantissa;
+	}
+	else {
+	    shift_count = max_exp ? max_exp - 1 : 0;
+	    value = dp->mantissa;
+
+//	    if (dp->mantissa)
+//		denormals++;
+	}
+
+	if (shift_count < 25)
+	    value >>= shift_count;
+	else
+	    value = 0;
+
+	if (!value) {
+	    if (dp->exponent || dp->mantissa)
+		++false_zeros;
+	    else if (dp->sign)
+		++neg_zeros;
+	}
+	else if (shift_count) {
+	    long mask = (1 << shift_count) - 1;
+
+	    if (!(dp->mantissa & mask))
+		shifted_zeros++;
+	    else if ((dp->mantissa & mask) == mask)
+		shifted_ones++;
+	    else
+		shifted_both++;
+	}
+
+	ordata |= value;
+	* (long *) dp = (dp->sign) ? -value : value;
+    }
+
+    wps->float_max_exp = max_exp;
+
+    if (shifted_both)
+	wps->float_flags |= FLOAT_SHIFT_SENT;
+    else if (shifted_ones && !shifted_zeros)
+	wps->float_flags |= FLOAT_SHIFT_ONES;
+    else if (shifted_ones && shifted_zeros)
+	wps->float_flags |= FLOAT_SHIFT_SAME;
+    else if (ordata && !(ordata & 1)) {
+	while (!(ordata & 1)) {
+	    wps->float_shift++;
+	    ordata >>= 1;
+	}
+
+	for (dp = values, count = num_values; count--; dp++)
+	    * (long *) dp >>= wps->float_shift;
+    }
+
+    wps->wphdr.flags &= ~MAG_MASK;
+
+    while (ordata) {
+	wps->wphdr.flags += 1 << MAG_LSB;
+	ordata >>= 1;
+    }
+
+    if (false_zeros || neg_zeros)
+	wps->float_flags |= FLOAT_ZEROS_SENT;
+
+    if (neg_zeros)
+	wps->float_flags |= FLOAT_NEG_ZEROS;
+
+//  error_line ("samples = %d, max exp = %d, pre-shift = %d, denormals = %d",
+//	num_values, max_exp, wps->float_shift, denormals);
+//  if (wps->float_flags & FLOAT_EXCEPTIONS)
+//	error_line ("exceptions!");
+//  error_line ("shifted ones/zeros/both = %d/%d/%d, true/neg/false zeros = %d/%d/%d",
+//	shifted_ones, shifted_zeros, shifted_both, true_zeros, neg_zeros, false_zeros);
+
+    return wps->float_flags & (FLOAT_EXCEPTIONS | FLOAT_ZEROS_SENT | FLOAT_SHIFT_SENT | FLOAT_SHIFT_SAME);
+}
+
+void send_float_data (WavpackStream *wps, f32 *values, long num_values)
+{
+    int max_exp = wps->float_max_exp;
+    long count, value, shift_count;
+    f32 *dp;
+
+    for (dp = values, count = num_values; count--; dp++) {
+	if (dp->exponent == 255) {
+	    if (dp->mantissa) {
+		putbit_1 (&wps->wvxbits);
+		putbits (dp->mantissa, 23, &wps->wvxbits);
+	    }
+	    else {
+		putbit_0 (&wps->wvxbits);
+	    }
+
+	    value = 0x1000000;
+	    shift_count = 0;
+	}
+	else if (dp->exponent) {
+	    shift_count = max_exp - dp->exponent;
+	    value = 0x800000 + dp->mantissa;
+	}
+	else {
+	    shift_count = max_exp ? max_exp - 1 : 0;
+	    value = dp->mantissa;
+	}
+
+	if (shift_count < 25)
+	    value >>= shift_count;
+	else
+	    value = 0;
+
+	if (!value) {
+	    if (wps->float_flags & FLOAT_ZEROS_SENT) {
+		if (dp->exponent || dp->mantissa) {
+		    putbit_1 (&wps->wvxbits);
+		    putbits (dp->mantissa, 23, &wps->wvxbits);
+
+		    if (max_exp >= 25) {
+			putbits (dp->exponent, 8, &wps->wvxbits);
+		    }
+
+		    putbit (dp->sign, &wps->wvxbits);
+		}
+		else {
+		    putbit_0 (&wps->wvxbits);
+
+		    if (wps->float_flags & FLOAT_NEG_ZEROS)
+			putbit (dp->sign, &wps->wvxbits);
+		}
+	    }
+	}
+	else if (shift_count) {
+	    if (wps->float_flags & FLOAT_SHIFT_SENT) {
+		long data = dp->mantissa & ((1 << shift_count) - 1);
+		putbits (data, shift_count, &wps->wvxbits);
+	    }
+	    else if (wps->float_flags & FLOAT_SHIFT_SAME) {
+		putbit (dp->mantissa & 1, &wps->wvxbits);
+	    }
+	}
+    }
+}
+
+#endif
+
+#if defined(UNPACK) || defined(INFO_ONLY)
+
+int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    int bytecnt = wpmd->byte_length;
+    char *byteptr = wpmd->data;
+
+    if (bytecnt != 4)
+	return FALSE;
+
+    wps->float_flags = *byteptr++;
+    wps->float_shift = *byteptr++;
+    wps->float_max_exp = *byteptr++;
+    wps->float_norm_exp = *byteptr;
+    return TRUE;
+}
+
+#endif
+
+#ifdef UNPACK
+
+static void float_values_nowvx (WavpackStream *wps, long *values, long num_values);
+
+void float_values (WavpackStream *wps, long *values, long num_values)
+{
+    ulong crc = wps->crc_x;
+
+    if (!bs_is_open (&wps->wvxbits)) {
+	float_values_nowvx (wps, values, num_values);
+	return;
+    }
+
+    while (num_values--) {
+	int shift_count = 0, exp = wps->float_max_exp;
+	f32 outval = { 0, 0, 0 };
+	ulong temp;
+
+	if (*values == 0) {
+	    if (wps->float_flags & FLOAT_ZEROS_SENT) {
+		if (getbit (&wps->wvxbits)) {
+		    getbits (&temp, 23, &wps->wvxbits);
+		    outval.mantissa = temp;
+
+		    if (exp >= 25) {
+			getbits (&temp, 8, &wps->wvxbits);
+			outval.exponent = temp;
+		    }
+
+		    outval.sign = getbit (&wps->wvxbits);
+		}
+		else if (wps->float_flags & FLOAT_NEG_ZEROS)
+		    outval.sign = getbit (&wps->wvxbits);
+	    }
+	}
+	else {
+	    *values <<= wps->float_shift;
+
+	    if (*values < 0) {
+		*values = -*values;
+		outval.sign = 1;
+	    }
+
+	    if (*values == 0x1000000) {
+		if (getbit (&wps->wvxbits)) {
+		    getbits (&temp, 23, &wps->wvxbits);
+		    outval.mantissa = temp;
+		}
+
+		outval.exponent = 255;
+	    }
+	    else {
+		if (exp)
+		    while (!(*values & 0x800000) && --exp) {
+			shift_count++;
+			*values <<= 1;
+		    }
+
+		if (shift_count) {
+		    if ((wps->float_flags & FLOAT_SHIFT_ONES) ||
+			((wps->float_flags & FLOAT_SHIFT_SAME) && getbit (&wps->wvxbits)))
+			    *values |= ((1 << shift_count) - 1);
+		    else if (wps->float_flags & FLOAT_SHIFT_SENT) {
+			getbits (&temp, shift_count, &wps->wvxbits);
+			*values |= temp & ((1 << shift_count) - 1);
+		    }
+		}
+
+		outval.mantissa = *values;
+		outval.exponent = exp;
+	    }
+	}
+
+	crc = crc * 27 + outval.mantissa * 9 + outval.exponent * 3 + outval.sign;
+	* (f32 *) values++ = outval;
+    }
+
+    wps->crc_x = crc;
+}
+
+static void float_values_nowvx (WavpackStream *wps, long *values, long num_values)
+{
+    while (num_values--) {
+	int shift_count = 0, exp = wps->float_max_exp;
+	f32 outval = { 0, 0, 0 };
+
+	if (*values) {
+	    *values <<= wps->float_shift;
+
+	    if (*values < 0) {
+		*values = -*values;
+		outval.sign = 1;
+	    }
+
+	    if (*values >= 0x1000000) {
+		while (*values & 0xf000000) {
+		    *values >>= 1;
+		    ++exp;
+		}
+	    }
+	    else if (exp) {
+		while (!(*values & 0x800000) && --exp) {
+		    shift_count++;
+		    *values <<= 1;
+		}
+
+		if (shift_count && (wps->float_flags & FLOAT_SHIFT_ONES))
+		    *values |= ((1 << shift_count) - 1);
+	    }
+
+	    outval.mantissa = *values;
+	    outval.exponent = exp;
+	}
+
+	* (f32 *) values++ = outval;
+    }
+}
+
+void float_normalize (long *values, long num_values, int delta_exp)
+{
+    f32 *fvalues = (f32 *) values, fzero = { 0, 0, 0 };
+    int exp;
+
+    if (!delta_exp)
+	return;
+
+    while (num_values--) {
+	if ((exp = fvalues->exponent) == 0 || exp + delta_exp <= 0)
+	    *fvalues = fzero;
+	else if (exp == 255 || (exp += delta_exp) >= 255) {
+	    fvalues->exponent = 255;
+	    fvalues->mantissa = 0;
+	}
+	else
+	    fvalues->exponent = exp;
+
+	fvalues++;
+    }
+}
+
+#endif
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/gstwavpackdec.c	2005-04-01 17:14:27.000000000 +0100
@@ -0,0 +1,615 @@
+/* GStreamer
+ * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstwavpackreader.h"
+
+#include <gst/gst.h>
+
+#include <string.h>
+
+#define SAMPLES_PER_BUFFER  2048
+
+#define GST_TYPE_WAVPACKDEC \
+  (wavpackdec_get_type())
+#define GST_WAVPACKDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WAVPACKDEC,GstWavpackDec))
+#define GST_WAVPACKDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_WAVPACKDEC,GstWavpackDec))
+#define GST_IS_WAVPACKDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WAVPACKDEC))
+#define GST_IS_WAVPACKDEC_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_WAVPACKDEC))
+
+
+typedef struct _GstWavpackDec GstWavpackDec;
+typedef struct _GstWavpackDecClass GstWavpackDecClass;
+
+struct _GstWavpackDec
+{
+  GstElement element;
+
+  /* pads */
+  GstPad *sinkpad, *srcpad;
+
+  /* state */
+  stream_reader   reader;
+  WavpackContext *ctx;
+  GstByteStream  *bs;
+
+  gboolean        seek_pending;
+  guint64         seek_offset; /* in samples */
+
+  gboolean        need_discont;
+  gboolean        need_flush;
+  guint64         offset;
+
+  glong          *decodebuf;
+
+  gchar           err_string[128];
+
+  gint            rate;
+  gint            channels;
+  gint            width;
+  gint            depth;
+  gint            total_samples;
+};
+
+struct _GstWavpackDecClass
+{
+  GstElementClass parent_class;
+};
+
+/* elementfactory information */
+static GstElementDetails wavpackdec_details =
+GST_ELEMENT_DETAILS ("wavpack decoder",
+    "Codec/Decoder/Audio",
+    "Decodes wavpack streams",
+    "Tim-Philipp Müller <tim centricular net>");
+
+
+GST_DEBUG_CATEGORY_STATIC (wavpackdec_debug);
+#define GST_CAT_DEFAULT wavpackdec_debug
+
+static GstStaticPadTemplate wavpackdec_src_template_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw-int, "
+        "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
+        "signed = (boolean) true, "
+        "width = (int) { 8, 16, 32 }, "
+        "depth = (int) { 8, 16, 24, 32 }, "
+        "rate = (int) { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }, "
+        "channels = (int) [ 1, 2 ]")
+    );
+
+static GstStaticPadTemplate wavpackdec_sink_template_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-wavpack")
+    );
+
+static GType wavpackdec_get_type (void) G_GNUC_CONST;
+static void wavpackdec_base_init (gpointer g_class);
+static void wavpackdec_class_init (GstWavpackDecClass * klass);
+static void wavpackdec_instance_init (GstWavpackDec * wavpackdec);
+
+static gboolean wavpackdec_src_event (GstPad * pad, GstEvent * event);
+static const GstEventMask *wavpackdec_get_event_masks (GstPad * pad);
+static const GstQueryType *wavpackdec_get_query_types (GstPad * pad);
+static gboolean wavpackdec_src_query (GstPad * pad, GstQueryType type,
+    GstFormat * format, gint64 * value);
+static void wavpackdec_loop (GstElement *element);
+static GstElementStateReturn wavpackdec_change_state (GstElement * element);
+
+static GstElementClass *parent_class; /* NULL */
+
+/***************************************************************************
+ *
+ *   wavpackdec_get_type
+ *
+ ***************************************************************************/
+
+static GType
+wavpackdec_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type) {
+    static const GTypeInfo info = {
+      sizeof (GstWavpackDecClass),
+      wavpackdec_base_init,
+      NULL,
+      (GClassInitFunc) wavpackdec_class_init,
+      NULL,
+      NULL,
+      sizeof (GstWavpackDec),
+      0,
+      (GInstanceInitFunc) wavpackdec_instance_init,
+    };
+
+    type = g_type_register_static (GST_TYPE_ELEMENT, "GstWavpackDec", &info, 0);
+  }
+  GST_DEBUG_CATEGORY_INIT (wavpackdec_debug, "wavpackdec", 0, "wavpack decoding");
+  return type;
+}
+
+/***************************************************************************
+ *
+ *   wavpackdec_base_init
+ *
+ ***************************************************************************/
+
+static void
+wavpackdec_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&wavpackdec_sink_template_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&wavpackdec_src_template_factory));
+  gst_element_class_set_details (element_class, &wavpackdec_details);
+}
+
+/***************************************************************************
+ *
+ *   wavpackdec_class_init
+ *
+ ***************************************************************************/
+
+static void
+wavpackdec_class_init (GstWavpackDecClass * klass)
+{
+  GstElementClass *gstelement_class;
+
+  gstelement_class = (GstElementClass *) klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gstelement_class->change_state = wavpackdec_change_state;
+}
+
+/***************************************************************************
+ *
+ *   wavpackdec_instance_init
+ *
+ ***************************************************************************/
+
+static void
+wavpackdec_instance_init (GstWavpackDec *wavpackdec)
+{
+  /* GST_FLAG_SET (wavpackdec, GST_ELEMENT_EVENT_AWARE); */
+
+  /* create the sink and src pads */
+  wavpackdec->sinkpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&wavpackdec_sink_template_factory), "sink");
+  gst_element_add_pad (GST_ELEMENT (wavpackdec), wavpackdec->sinkpad);
+
+  wavpackdec->srcpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&wavpackdec_src_template_factory), "src");
+  gst_element_add_pad (GST_ELEMENT (wavpackdec), wavpackdec->srcpad);
+  gst_pad_set_event_function (wavpackdec->srcpad,
+      GST_DEBUG_FUNCPTR (wavpackdec_src_event));
+  gst_pad_set_event_mask_function (wavpackdec->srcpad,
+      GST_DEBUG_FUNCPTR (wavpackdec_get_event_masks));
+  gst_pad_set_query_function (wavpackdec->srcpad,
+      GST_DEBUG_FUNCPTR (wavpackdec_src_query));
+  gst_pad_set_query_type_function (wavpackdec->srcpad,
+      GST_DEBUG_FUNCPTR (wavpackdec_get_query_types));
+  gst_pad_use_explicit_caps (wavpackdec->srcpad);
+
+  gst_element_set_loop_function (GST_ELEMENT (wavpackdec), GST_DEBUG_FUNCPTR (wavpackdec_loop));
+
+  wavpackdec->ctx = NULL;
+
+  GST_FLAG_SET (wavpackdec, GST_ELEMENT_EVENT_AWARE);
+}
+
+/***************************************************************************
+ *
+ *   wavpackdec_get_query_types
+ *
+ ***************************************************************************/
+
+static const GstQueryType *
+wavpackdec_get_query_types (GstPad *pad)
+{
+  static const GstQueryType wavpackdec_src_query_types[] = {
+    GST_QUERY_TOTAL,
+    GST_QUERY_POSITION,
+    0
+  };
+
+  return wavpackdec_src_query_types;
+}
+
+/***************************************************************************
+ *
+ *   wavpackdec_src_query
+ *
+ ***************************************************************************/
+
+static gboolean
+wavpackdec_src_query (GstPad *pad, GstQueryType type,
+    GstFormat *format, gint64 *value)
+{
+  GstWavpackDec *wavpackdec = GST_WAVPACKDEC (gst_pad_get_parent (pad));
+  gint64 val;
+
+  if (wavpackdec->ctx == NULL)
+    return FALSE;
+
+  if (type == GST_QUERY_TOTAL)
+    val = wavpackdec->total_samples;
+  else if (type == GST_QUERY_POSITION)
+    val = wavpackdec->offset;
+  else
+    return FALSE;
+
+  switch (*format) {
+    case GST_FORMAT_DEFAULT:
+      *value = val;
+      break;
+    case GST_FORMAT_BYTES:
+      /* *value = val * (wavpackdec->width/8); */ /* or depth? */
+      return FALSE; /* can be lossy or lossless, just leave it for now */
+    case GST_FORMAT_TIME:
+      *value = val * GST_SECOND / wavpackdec->rate;
+      break;
+    default:
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+
+/***************************************************************************
+ *
+ *   wavpackdec_src_event
+ *
+ ***************************************************************************/
+
+static const GstEventMask *
+wavpackdec_get_event_masks (GstPad * pad)
+{
+  static const GstEventMask wavpackdec_src_event_masks[] = {
+    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
+    {0,}
+  };
+
+  return wavpackdec_src_event_masks;
+}
+
+/***************************************************************************
+ *
+ *   wavpackdec_src_event
+ *
+ ***************************************************************************/
+
+static gboolean
+wavpackdec_src_event (GstPad *pad, GstEvent *event)
+{
+  GstWavpackDec *wavpackdec;
+  GstSeekType    method;
+  GstFormat      format;
+  gboolean       need_flush;
+  gint64         offset, dest;
+
+  wavpackdec = GST_WAVPACKDEC (gst_pad_get_parent (pad));
+
+  if (GST_EVENT_TYPE (event) != GST_EVENT_SEEK) {
+    GST_DEBUG ("forwarding event of type %d to sink pad", GST_EVENT_TYPE (event));
+    return gst_pad_send_event (GST_PAD_PEER (wavpackdec->sinkpad), event);
+  }
+
+  format = GST_EVENT_SEEK_FORMAT (event);
+  offset = GST_EVENT_SEEK_OFFSET (event);
+  method = GST_EVENT_SEEK_METHOD (event);
+  need_flush = ((GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH));
+
+  gst_data_unref (GST_DATA (event));
+  event = NULL;
+
+  if (offset < 0 || method != GST_SEEK_METHOD_SET)
+    return FALSE;
+
+  if (wavpackdec->ctx == NULL)
+    return FALSE;
+
+  /* we can only seek to samples, so we convert
+   * the requested offsets to samples first */
+  if (format == GST_FORMAT_TIME) {
+    dest = offset * wavpackdec->rate / GST_SECOND;
+  } else if (format == GST_FORMAT_BYTES) {
+    /* dest = offset / (wavpackdec->width/8); */ /* or depth? */
+    return FALSE; /* can be lossy or lossless, just leave it for now */
+  } else if (format == GST_FORMAT_DEFAULT) {
+    dest = offset;
+  } else {
+    return FALSE;
+  }
+
+  if (dest < 0 || dest >= wavpackdec->total_samples) {
+    GST_DEBUG ("seek out of range (dest=%u, total_samples=%d)", 
+        (guint)dest, wavpackdec->total_samples);
+    return FALSE;
+  }
+
+  wavpackdec->need_discont = TRUE;
+  wavpackdec->need_flush = need_flush;
+
+  wavpackdec->seek_pending = TRUE;
+  wavpackdec->seek_offset = dest;
+
+  return TRUE;
+}
+
+/***************************************************************************
+ *
+ *   wavpackdec_init_stream
+ *
+ ***************************************************************************/
+
+static gboolean
+wavpackdec_init_stream (GstWavpackDec *wavpackdec)
+{
+  GstCaps *caps;
+
+  wavpackdec->err_string[0] = 0x00;
+
+  wavpackdec->ctx = WavpackOpenFileInputEx (&wavpackdec->reader,
+      wavpackdec->bs, NULL, wavpackdec->err_string, 
+      OPEN_2CH_MAX | OPEN_NORMALIZE, 0);
+
+  if (wavpackdec->err_string[0]) {
+    GST_ELEMENT_ERROR (wavpackdec, STREAM, WRONG_TYPE, 
+        ("%s", wavpackdec->err_string), (NULL));
+    return FALSE;
+  }
+
+  wavpackdec->depth = WavpackGetBytesPerSample (wavpackdec->ctx) * 8;
+  wavpackdec->width = (wavpackdec->depth == 24) ? 32 : wavpackdec->depth;
+  wavpackdec->rate = WavpackGetSampleRate (wavpackdec->ctx);
+  wavpackdec->channels = WavpackGetNumChannels (wavpackdec->ctx);
+  wavpackdec->total_samples = WavpackGetNumSamples (wavpackdec->ctx);
+  caps = gst_caps_new_simple ("audio/x-raw-int",
+      "signed", G_TYPE_BOOLEAN, TRUE,
+      "endianness", G_TYPE_INT, G_BYTE_ORDER,
+      "channels", G_TYPE_INT, wavpackdec->channels,
+      "rate", G_TYPE_INT, wavpackdec->rate, 
+      "width", G_TYPE_INT, wavpackdec->width, 
+      "depth", G_TYPE_INT, wavpackdec->depth, 
+      NULL);
+
+  if (!gst_pad_set_explicit_caps (wavpackdec->srcpad, caps)) {
+    GST_ELEMENT_ERROR (wavpackdec, CORE, NEGOTIATION, (NULL), (NULL));
+    gst_caps_free (caps);
+    return FALSE;
+  }
+
+  wavpackdec->decodebuf = g_new0 (long, SAMPLES_PER_BUFFER * wavpackdec->channels);
+
+  wavpackdec->offset = 0;
+  wavpackdec->need_discont = TRUE;
+  wavpackdec->need_flush = FALSE;
+  wavpackdec->seek_pending = FALSE;
+
+  gst_caps_free (caps);
+  return TRUE;
+}
+
+/***************************************************************************
+ *
+ *   wavpackdec_repack_samples
+ *
+ ***************************************************************************/
+
+static inline GstBuffer *
+wavpackdec_repack_samples (GstWavpackDec *wavpackdec, long *samples, guint num_samples)
+{
+  GstBuffer *buf;
+  gint i;
+
+  buf = gst_buffer_new_and_alloc (num_samples * wavpackdec->width/8 * wavpackdec->channels);
+
+  switch (wavpackdec->width) {
+    case 8:
+    {
+      guint8 *d  =(guint8*) GST_BUFFER_DATA (buf);
+      for (i = 0; i < num_samples * wavpackdec->channels; ++i)
+        *(d++) = (guint8) *(samples++);
+    }
+    break;
+
+    case 16:
+    {
+      guint16 *d  =(guint16*) GST_BUFFER_DATA (buf);
+      for (i = 0; i < num_samples * wavpackdec->channels; ++i)
+        *(d++) = (guint16) *(samples++);
+    }
+    break;
+
+    case 32:
+    {
+      guint32 *d  =(guint32*) GST_BUFFER_DATA (buf);
+      for (i = 0; i < num_samples * wavpackdec->channels; ++i)
+        *(d++) = (guint32) *(samples++);
+    }
+    break;
+
+    default:
+      g_warning ("wavpackdec->width = %u", wavpackdec->width);
+      g_return_val_if_reached (NULL);
+  }
+
+  return buf;
+}
+
+/***************************************************************************
+ *
+ *   wavpackdec_loop
+ *
+ ***************************************************************************/
+
+static void
+wavpackdec_loop (GstElement *element)
+{
+  GstWavpackDec *wavpackdec;
+  GstBuffer *buf;
+  ulong ret;
+
+  wavpackdec = GST_WAVPACKDEC (element);
+
+  if (wavpackdec->ctx == NULL) {
+    if (!wavpackdec_init_stream (wavpackdec))
+      return;
+  }
+
+  if (wavpackdec->seek_pending) {
+    wavpackdec->seek_pending = FALSE;
+    if (WavpackSeekSample (wavpackdec->ctx, (ulong) wavpackdec->seek_offset)) {
+      if (wavpackdec->need_flush) {
+        GstEvent *flush_event = gst_event_new (GST_EVENT_FLUSH);
+        gst_pad_push (wavpackdec->srcpad, GST_DATA (flush_event));
+        wavpackdec->need_flush = FALSE;
+      }
+      wavpackdec->need_discont = TRUE;
+      wavpackdec->offset = wavpackdec->seek_offset;
+    } else {
+      GST_DEBUG ("seek to sample %" G_GUINT64_FORMAT "/%d failed", 
+                 wavpackdec->seek_offset, wavpackdec->total_samples);
+    }
+  }
+
+  if (wavpackdec->need_discont) {
+    guint64 timestamp = wavpackdec->offset * GST_SECOND / wavpackdec->rate;
+    gst_pad_push (wavpackdec->srcpad,
+        GST_DATA (gst_event_new_discontinuous (FALSE,
+                GST_FORMAT_TIME, timestamp,
+                GST_FORMAT_DEFAULT, wavpackdec->offset,
+                GST_FORMAT_UNDEFINED)));
+    wavpackdec->need_discont = FALSE;
+  }
+
+  ret = WavpackUnpackSamples (wavpackdec->ctx, wavpackdec->decodebuf, SAMPLES_PER_BUFFER);
+
+  if (ret <= 0) {
+    if (ret == 0) {
+      gst_element_set_eos (element);
+      gst_pad_push (wavpackdec->srcpad,
+          GST_DATA (gst_event_new (GST_EVENT_EOS)));
+    } else {
+      GST_ERROR_OBJECT (wavpackdec, "Failed to decode samples: %s", wavpackdec->err_string);
+    }
+    return;
+  }
+
+  buf = wavpackdec_repack_samples (wavpackdec, wavpackdec->decodebuf, (guint) ret);
+  g_return_if_fail (buf != NULL);
+
+  GST_BUFFER_OFFSET (buf) = wavpackdec->offset;
+  GST_BUFFER_DURATION (buf) = ret * GST_SECOND / wavpackdec->rate;
+  GST_BUFFER_TIMESTAMP (buf) = wavpackdec->offset * GST_SECOND / wavpackdec->rate;
+
+  gst_pad_push (wavpackdec->srcpad, GST_DATA (buf));
+
+  wavpackdec->offset += ret;
+}
+
+static GstElementStateReturn
+wavpackdec_change_state (GstElement * element)
+{
+  GstWavpackDec *wavpackdec;
+
+  wavpackdec = GST_WAVPACKDEC (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_NULL_TO_READY:
+      wavpackdec->bs = gst_bytestream_new (wavpackdec->sinkpad);
+      gst_wavpack_reader_init (&wavpackdec->reader);
+      break;
+    case GST_STATE_READY_TO_PAUSED:
+      wavpackdec->need_discont = TRUE;
+      break;
+    case GST_STATE_PAUSED_TO_PLAYING:
+      break;
+    case GST_STATE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      wavpackdec->seek_pending = FALSE;
+      break;
+    case GST_STATE_READY_TO_NULL:
+      WavpackCloseFile (wavpackdec->ctx);
+      gst_bytestream_destroy (wavpackdec->bs);
+      g_free (wavpackdec->decodebuf);
+      wavpackdec->decodebuf = NULL;
+      wavpackdec->ctx = NULL;
+      wavpackdec->bs = NULL;
+      wavpackdec->depth = 0;
+      wavpackdec->width = 0;
+      wavpackdec->rate = 0;
+      wavpackdec->channels = 0;
+      wavpackdec->total_samples = 0;
+      wavpackdec->offset = 0;
+      break;
+  }
+
+  return parent_class->change_state (element);
+}
+
+/***************************************************************************
+ *
+ *   PLUGIN INITIALIZATION
+ *
+ ***************************************************************************/
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  if (!gst_library_load ("gstbytestream")
+   || !gst_element_register (plugin, "wavpackdec", GST_RANK_PRIMARY, 
+                             wavpackdec_get_type ()))
+    return FALSE;
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE 
+(
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "wavpack",
+  "wavpack decoding and encoding",
+  plugin_init, 
+  VERSION, 
+  "LGPL", 
+  GST_PACKAGE, 
+  GST_ORIGIN
+)
+
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/gstwavpackreader.c	2005-04-01 16:51:24.000000000 +0100
@@ -0,0 +1,231 @@
+/* GStreamer Wavpack decoder plugin
+ * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstwavpackreader.h"
+
+#include <gst/gst.h>
+#include <string.h>
+#include <stdio.h>
+
+/***************************************************************************
+ *
+ *   gst_wavpack_reader_peek
+ *
+ ***************************************************************************/
+
+static int
+gst_wavpack_reader_peek (void *this, void *ptr, gint size)
+{
+  GstByteStream *bs = (GstByteStream *) this;
+  guint8 *data;
+  gint read;
+
+  do {
+    read = gst_bytestream_peek_bytes (bs, &data, size);
+
+    if (read != size) {
+      GstEvent *event;
+      guint32 remaining;
+
+      gst_bytestream_get_status (bs, &remaining, &event);
+      if (!event) {
+        GST_ELEMENT_ERROR (gst_pad_get_parent (bs->pad),
+            RESOURCE, READ, (NULL), (NULL));
+        goto done;
+      }
+
+      switch (GST_EVENT_TYPE (event)) {
+        case GST_EVENT_INTERRUPT:
+          gst_event_unref (event);
+          goto done;
+        case GST_EVENT_EOS:
+          gst_event_unref (event);
+          goto done;
+        case GST_EVENT_FLUSH:
+          gst_event_unref (event);
+          break;
+        case GST_EVENT_DISCONTINUOUS:
+          gst_event_unref (event);
+          break;
+        default:
+          gst_pad_event_default (bs->pad, event);
+          break;
+      }
+    }
+  } while (read != size);
+
+done:
+  if (read != 0) {
+    memcpy (ptr, data, read);
+  }
+
+  return read;
+}
+
+/***************************************************************************
+ *
+ *   gst_wavpack_reader_read_bytes
+ *
+ ***************************************************************************/
+
+static long
+gst_wavpack_reader_read_bytes (void *id, void *data, long bcount)
+{
+  GstByteStream *bs = (GstByteStream *) id;
+  gulong read;
+
+  /* read = peek + flush */
+  read = gst_wavpack_reader_peek (bs, data, bcount);
+  if (read > 0) {
+    gst_bytestream_flush_fast (bs, read);
+  }
+
+  return (long) read;
+}
+
+/***************************************************************************
+ *
+ *   gst_wavpack_reader_get_pos
+ *
+ ***************************************************************************/
+
+static ulong
+gst_wavpack_reader_get_pos (void *id)
+{
+  return gst_bytestream_tell ((GstByteStream *) id);
+}
+
+/***************************************************************************
+ *
+ *   gst_wavpack_reader_set_pos_rel
+ *
+ ***************************************************************************/
+
+static int
+gst_wavpack_reader_set_pos_rel (void *id, long pos, int mode)
+{ 
+  GstByteStream *bs = (GstByteStream *) id;
+  guint8 dummy;
+  int whence;
+
+  /* hacky hack - if we're after typefind, we'll fail because
+   * typefind is still typefinding (heh :) ). So read first. */
+  gst_wavpack_reader_peek (bs, &dummy, 1);
+
+  switch (mode) {
+    case SEEK_SET:
+      whence = GST_SEEK_METHOD_SET;
+      break;
+    case SEEK_END:
+      whence = GST_SEEK_METHOD_END;
+      break;
+    case SEEK_CUR:
+      whence = GST_SEEK_METHOD_CUR;
+      break;
+    default:
+      return -1;
+  }
+
+  /* seek */
+  if (!gst_bytestream_seek (bs, pos, whence))
+    return -1;
+
+  /* get discont */
+  if (gst_wavpack_reader_peek (bs, &dummy, 1) != 1)
+    return -1;
+
+  return 0; /* just like fseek */
+}
+
+/***************************************************************************
+ *
+ *   gst_wavpack_reader_set_pos_abs
+ *
+ ***************************************************************************/
+
+static int
+gst_wavpack_reader_set_pos_abs (void *id, ulong pos)
+{ 
+  return gst_wavpack_reader_set_pos_rel (id, (long) pos, SEEK_SET);
+}
+
+/***************************************************************************
+ *
+ *   gst_wavpack_reader_push_back_byte
+ *
+ ***************************************************************************/
+
+static int
+gst_wavpack_reader_push_back_byte (void *id, int c)
+{
+  /* only required if we want to be able to decode old v3 
+   * wavpack files as well, which we do not at the moment */
+#ifdef VER3
+# error need to implement gst_wavpack_reader_push_back_byte
+#endif
+  g_return_val_if_reached (-1);
+}
+
+/***************************************************************************
+ *
+ *   gst_wavpack_reader_get_length
+ *
+ ***************************************************************************/
+
+static ulong
+gst_wavpack_reader_get_length (void *id)
+{ 
+  return gst_bytestream_length ((GstByteStream *) id);
+}
+
+/***************************************************************************
+ *
+ *   gst_wavpack_reader_can_seek
+ *
+ ***************************************************************************/
+
+static int
+gst_wavpack_reader_can_seek (void *id)
+{ 
+  return TRUE;
+}
+
+/***************************************************************************
+ *
+ *   gst_wavpack_reader_init
+ *
+ ***************************************************************************/
+
+void
+gst_wavpack_reader_init (stream_reader *r)
+{
+  r->read_bytes = gst_wavpack_reader_read_bytes;
+  r->get_pos = gst_wavpack_reader_get_pos; 
+  r->set_pos_abs = gst_wavpack_reader_set_pos_abs;
+  r->set_pos_rel = gst_wavpack_reader_set_pos_rel;
+  r->push_back_byte = gst_wavpack_reader_push_back_byte;
+  r->get_length = gst_wavpack_reader_get_length;
+  r->can_seek = gst_wavpack_reader_can_seek;
+}
+
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/gstwavpackreader.h	2005-04-01 15:15:03.000000000 +0100
@@ -0,0 +1,30 @@
+/* GStreamer Wavpack decoder plugin
+ * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_WAVPACK_READER_H__
+#define __GST_WAVPACK_READER_H__
+
+#include "wavpack.h"
+
+#include <gst/bytestream/bytestream.h>
+
+void gst_wavpack_reader_init (stream_reader *r);
+
+#endif /* __GST_WAVPACK_READER_H__ */
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/metadata.c	2005-03-30 10:17:50.000000000 +0100
@@ -0,0 +1,310 @@
+////////////////////////////////////////////////////////////////////////////
+//                           **** WAVPACK ****                            //
+//                  Hybrid Lossless Wavefile Compressor                   //
+//              Copyright (c) 1998 - 2003 Conifer Software.               //
+//                          All Rights Reserved.                          //
+//      Distributed under the BSD Software License (see license.txt)      //
+////////////////////////////////////////////////////////////////////////////
+
+// metadata.c
+
+// This module handles the metadata structure introduced in WavPack 4.0
+
+#include "wavpack.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef DEBUG_ALLOC
+#define malloc malloc_db
+#define realloc realloc_db
+#define free free_db
+void *malloc_db (ulong size);
+void *realloc_db (void *ptr, ulong size);
+void free_db (void *ptr);
+long dump_alloc (void);
+#endif
+
+#if defined(UNPACK) || defined(INFO_ONLY)
+
+int read_metadata_buff (WavpackMetadata *wpmd, uchar *blockbuff, uchar **buffptr)
+{
+    WavpackHeader *wphdr = (WavpackHeader *) blockbuff;
+    uchar *buffend = blockbuff + wphdr->ckSize + 8;
+
+    if (buffend - *buffptr < 2)
+	return FALSE;
+
+    wpmd->id = *(*buffptr)++;
+    wpmd->byte_length = *(*buffptr)++ << 1;
+
+    if (wpmd->id & ID_LARGE) {
+	wpmd->id &= ~ID_LARGE;
+
+	if (buffend - *buffptr < 2)
+	    return FALSE;
+
+	wpmd->byte_length += *(*buffptr)++ << 9; 
+	wpmd->byte_length += *(*buffptr)++ << 17;
+    }
+
+    if (wpmd->id & ID_ODD_SIZE) {
+	wpmd->id &= ~ID_ODD_SIZE;
+	wpmd->byte_length--;
+    }
+
+    if (wpmd->byte_length) {
+	if (buffend - *buffptr < wpmd->byte_length + (wpmd->byte_length & 1)) {
+	    wpmd->data = NULL;
+	    return FALSE;
+	}
+
+	wpmd->data = *buffptr;
+	(*buffptr) += wpmd->byte_length + (wpmd->byte_length & 1);
+    }
+    else
+	wpmd->data = NULL;
+
+    return TRUE;
+}
+
+int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+
+    switch (wpmd->id) {
+	case ID_DUMMY:
+	    return TRUE;
+
+	case ID_DECORR_TERMS:
+	    return read_decorr_terms (wps, wpmd);
+
+	case ID_DECORR_WEIGHTS:
+	    return read_decorr_weights (wps, wpmd);
+
+	case ID_DECORR_SAMPLES:
+	    return read_decorr_samples (wps, wpmd);
+
+	case ID_ENTROPY_VARS:
+	    return read_entropy_vars (wps, wpmd);
+
+	case ID_HYBRID_PROFILE:
+	    return read_hybrid_profile (wps, wpmd);
+
+	case ID_SHAPING_WEIGHTS:
+	    return read_shaping_info (wps, wpmd);
+
+	case ID_FLOAT_INFO:
+	    return read_float_info (wps, wpmd);
+
+	case ID_INT32_INFO:
+	    return read_int32_info (wps, wpmd);
+
+	case ID_CHANNEL_INFO:
+	    return read_channel_info (wpc, wpmd);
+
+	case ID_CONFIG_BLOCK:
+	    return read_config_info (wpc, wpmd);
+
+	case ID_WV_BITSTREAM:
+	    return init_wv_bitstream (wps, wpmd);
+
+	case ID_WVC_BITSTREAM:
+	    return init_wvc_bitstream (wps, wpmd);
+
+	case ID_WVX_BITSTREAM:
+	    return init_wvx_bitstream (wps, wpmd);
+
+	case ID_RIFF_HEADER: case ID_RIFF_TRAILER:
+	    return read_wrapper_data (wpc, wpmd);
+
+	case ID_MD5_CHECKSUM:
+	    if (wpmd->byte_length == 16) {
+		memcpy (wpc->config.md5_checksum, wpmd->data, 16);
+		wpc->config.flags |= CONFIG_MD5_CHECKSUM;
+		wpc->config.md5_read = 1;
+	    }
+
+	    return TRUE;
+
+	default:
+	    return (wpmd->id & ID_OPTIONAL_DATA) ? TRUE : FALSE;
+    }
+}
+
+#endif
+
+#ifdef PACK
+
+int copy_metadata (WavpackMetadata *wpmd, uchar *buffer_start, uchar *buffer_end)
+{
+    ulong mdsize = wpmd->byte_length + (wpmd->byte_length & 1);
+    WavpackHeader *wphdr = (WavpackHeader *) buffer_start;
+
+    if (wpmd->byte_length & 1)
+	((char *) wpmd->data) [wpmd->byte_length] = 0;
+
+    mdsize += (wpmd->byte_length > 510) ? 4 : 2;
+    buffer_start += wphdr->ckSize + 8;
+
+    if (buffer_start + mdsize >= buffer_end)
+	return FALSE;
+
+    buffer_start [0] = wpmd->id | (wpmd->byte_length & 1 ? ID_ODD_SIZE : 0);
+    buffer_start [1] = (wpmd->byte_length + 1) >> 1;
+
+    if (wpmd->byte_length > 510) {
+	buffer_start [0] |= ID_LARGE;
+	buffer_start [2] = (wpmd->byte_length + 1) >> 9;
+	buffer_start [3] = (wpmd->byte_length + 1) >> 17;
+    }
+
+    if (wpmd->data && wpmd->byte_length) {
+	if (wpmd->byte_length > 510) {
+	    buffer_start [0] |= ID_LARGE;
+	    buffer_start [2] = (wpmd->byte_length + 1) >> 9;
+	    buffer_start [3] = (wpmd->byte_length + 1) >> 17;
+	    memcpy (buffer_start + 4, wpmd->data, mdsize - 4);
+	}
+	else
+	    memcpy (buffer_start + 2, wpmd->data, mdsize - 2);
+    }
+
+    wphdr->ckSize += mdsize;
+    return TRUE;
+}
+
+int add_to_metadata (WavpackContext *wpc, void *data, ulong bcount, uchar id)
+{
+    WavpackMetadata *mdp;
+    uchar *src = data;
+
+    while (bcount) {
+	if (wpc->metacount) {
+	    ulong bc = bcount;
+
+	    mdp = wpc->metadata + wpc->metacount - 1;
+
+	    if (mdp->id == id) {
+		if (wpc->metabytes + bcount > 1000000)
+		    bc = 1000000 - wpc->metabytes;
+
+		mdp->data = realloc (mdp->data, mdp->byte_length + bc);
+		memcpy ((char *) mdp->data + mdp->byte_length, src, bc);
+		mdp->byte_length += bc;
+		wpc->metabytes += bc;
+		bcount -= bc;
+		src += bc;
+
+		if (wpc->metabytes >= 1000000 && !write_metadata_block (wpc))
+		    return FALSE;
+	    }
+	}
+
+	if (bcount) {
+	    wpc->metadata = realloc (wpc->metadata, (wpc->metacount + 1) * sizeof (WavpackMetadata));
+	    mdp = wpc->metadata + wpc->metacount++;
+	    mdp->byte_length = 0;
+	    mdp->data = NULL;
+	    mdp->id = id;
+	}
+    }
+
+    return TRUE;
+}
+
+static char *write_metadata (WavpackMetadata *wpmd, char *outdata)
+{
+    uchar id = wpmd->id, wordlen [3];
+
+    wordlen [0] = (wpmd->byte_length + 1) >> 1;
+    wordlen [1] = (wpmd->byte_length + 1) >> 9;
+    wordlen [2] = (wpmd->byte_length + 1) >> 17;
+
+    if (wpmd->byte_length & 1) {
+//	((char *) wpmd->data) [wpmd->byte_length] = 0;
+	id |= ID_ODD_SIZE;
+    }
+
+    if (wordlen [1] || wordlen [2])
+	id |= ID_LARGE;
+
+    *outdata++ = id;
+    *outdata++ = wordlen [0];
+
+    if (id & ID_LARGE) {
+	*outdata++ = wordlen [1];
+	*outdata++ = wordlen [2];
+    }
+
+    if (wpmd->data && wpmd->byte_length) {
+	memcpy (outdata, wpmd->data, wpmd->byte_length);
+	outdata += wpmd->byte_length;
+
+	if (wpmd->byte_length & 1)
+	    *outdata++ = 0;
+    }
+
+    return outdata;
+}
+
+int write_metadata_block (WavpackContext *wpc)
+{
+    char *block_buff, *block_ptr;
+    WavpackHeader *wphdr;
+
+    if (wpc->metacount) {
+	int metacount = wpc->metacount, block_size = sizeof (WavpackHeader);
+	WavpackMetadata *wpmdp = wpc->metadata;
+
+	while (metacount--) {
+	    block_size += wpmdp->byte_length + (wpmdp->byte_length & 1);
+	    block_size += (wpmdp->byte_length > 510) ? 4 : 2;
+	    wpmdp++;
+	}
+
+	wphdr = (WavpackHeader *) (block_buff = malloc (block_size));
+
+	CLEAR (*wphdr);
+	memcpy (wphdr->ckID, "wvpk", 4);
+	wphdr->total_samples = wpc->total_samples;
+	wphdr->version = 0x403;
+	wphdr->ckSize = block_size - 8;
+	wphdr->block_samples = 0;
+
+	block_ptr = (char *)(wphdr + 1);
+
+	wpmdp = wpc->metadata;
+
+	while (wpc->metacount) {
+	    block_ptr = write_metadata (wpmdp, block_ptr);
+	    wpc->metabytes -= wpmdp->byte_length;
+	    free_metadata (wpmdp++);
+	    wpc->metacount--;
+	}
+
+	free (wpc->metadata);
+	wpc->metadata = NULL;
+	native_to_little_endian ((WavpackHeader *) block_buff, WavpackHeaderFormat);
+
+	if (!wpc->blockout (wpc->wv_out, block_buff, block_size)) {
+	    free (block_buff);
+	    strcpy (wpc->error_message, "can't write WavPack data, disk probably full!");
+	    return FALSE;
+	}
+
+	free (block_buff);
+    }
+
+    return TRUE;
+}
+
+#endif
+
+void free_metadata (WavpackMetadata *wpmd)
+{
+    if (wpmd->data) {
+	free (wpmd->data);
+	wpmd->data = NULL;
+    }
+}
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/pack.c	2005-03-30 11:05:21.000000000 +0100
@@ -0,0 +1,1055 @@
+////////////////////////////////////////////////////////////////////////////
+//			     **** WAVPACK ****				  //
+//		    Hybrid Lossless Wavefile Compressor			  //
+//		Copyright (c) 1998 - 2004 Conifer Software.		  //
+//			    All Rights Reserved.			  //
+//      Distributed under the BSD Software License (see license.txt)      //
+////////////////////////////////////////////////////////////////////////////
+
+// pack.c
+
+// This module actually handles the compression of the audio data, except for
+// the entropy coding which is handled by the words? modules. For efficiency,
+// the conversion is isolated to tight loops that handle an entire buffer.
+
+#include "wavpack.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#ifdef DEBUG_ALLOC
+#define malloc malloc_db
+#define realloc realloc_db
+#define free free_db
+void *malloc_db (ulong size);
+void *realloc_db (void *ptr, ulong size);
+void free_db (void *ptr);
+long dump_alloc (void);
+#endif
+
+//////////////////////////////// local tables ///////////////////////////////
+
+// These two tables specify the characteristics of the decorrelation filters.
+// Each term represents one layer of the sequential filter, where positive
+// values indicate the relative sample involved from the same channel (1=prev),
+// 17 & 18 are special functions using the previous 2 samples, and negative
+// values indicate cross channel decorrelation (in stereo only).
+
+const char default_terms [] = { 18,18,2,3,-2,0 };
+const char high_terms [] = { 18,18,2,3,-2,18,2,4,7,5,3,6,8,-1,18,2,0 };
+const char fast_terms [] = { 17,17,0 };
+
+///////////////////////////// executable code ////////////////////////////////
+
+// This function initializes everything required to pack WavPack bitstreams
+// and must be called BEFORE any other function in this module.
+
+void pack_init (WavpackContext *wpc)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    ulong flags = wps->wphdr.flags;
+    struct decorr_pass *dpp;
+    const char *term_string;
+    int ti;
+
+    wps->sample_index = 0;
+    wps->delta_decay = 2.0;
+    CLEAR (wps->decorr_passes);
+    CLEAR (wps->dc);
+
+    if (wpc->config.flags & CONFIG_AUTO_SHAPING)
+	wps->dc.shaping_acc [0] = wps->dc.shaping_acc [1] =
+	    (wpc->config.sample_rate < 64000 || (wps->wphdr.flags & CROSS_DECORR)) ? -512L << 16 : 1024L << 16;
+    else {
+	long weight = (long) floor (wpc->config.shaping_weight * 1024.0 + 0.5);
+
+	if (weight <= -1000)
+	    weight = -1000;
+
+	wps->dc.shaping_acc [0] = wps->dc.shaping_acc [1] = weight << 16;
+    }
+
+    if (wpc->config.flags & CONFIG_HIGH_FLAG)
+	term_string = high_terms;
+    else if (wpc->config.flags & CONFIG_FAST_FLAG)
+	term_string = fast_terms;
+    else
+	term_string = default_terms;
+
+    for (dpp = wps->decorr_passes, ti = 0; ti < strlen (term_string); ti++)
+	if (term_string [ti] >= 0 || (flags & CROSS_DECORR)) {
+	    dpp->term = term_string [ti];
+	    dpp++->delta = 2;
+	}
+	else if (!(flags & MONO_FLAG)) {
+	    dpp->term = -3;
+	    dpp++->delta = 2;
+	}
+
+    wps->num_terms = dpp - wps->decorr_passes;
+    init_words (wps);
+}
+
+// Allocate room for and copy the decorrelation terms from the decorr_passes
+// array into the specified metadata structure. Both the actual term id and
+// the delta are packed into single characters.
+
+void write_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    int tcount = wps->num_terms;
+    struct decorr_pass *dpp;
+    char *byteptr;
+
+    byteptr = wpmd->data = malloc (tcount + 1);
+    wpmd->id = ID_DECORR_TERMS;
+
+    for (dpp = wps->decorr_passes; tcount--; ++dpp)
+	*byteptr++ = ((dpp->term + 5) & 0x1f) | ((dpp->delta << 5) & 0xe0);
+
+    wpmd->byte_length = byteptr - (char *) wpmd->data;
+}
+
+// Allocate room for and copy the decorrelation term weights from the
+// decorr_passes array into the specified metadata structure. The weights
+// range +/-1024, but are rounded and truncated to fit in signed chars for
+// metadata storage. Weights are separate for the two channels
+
+void write_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    int tcount = wps->num_terms;
+    struct decorr_pass *dpp;
+    char *byteptr;
+
+    byteptr = wpmd->data = malloc ((tcount * 2) + 1);
+    wpmd->id = ID_DECORR_WEIGHTS;
+
+    for (dpp = wps->decorr_passes; tcount--; ++dpp) {
+	dpp->weight_A = restore_weight (*byteptr++ = store_weight (dpp->weight_A));
+
+	if (!(wps->wphdr.flags & MONO_FLAG))
+	    dpp->weight_B = restore_weight (*byteptr++ = store_weight (dpp->weight_B));
+    }
+
+    wpmd->byte_length = byteptr - (char *) wpmd->data;
+}
+
+// Allocate room for and copy the decorrelation samples from the decorr_passes
+// array into the specified metadata structure. The samples are signed 32-bit
+// values, but are converted to signed log2 values for storage in metadata.
+// Values are stored for both channels and are specified from the first term
+// with unspecified samples set to zero. The number of samples stored varies
+// with the actual term value, so those must obviously be specified before
+// these in the metadata list. Any number of terms can have their samples
+// specified from no terms to all the terms, however I have found that
+// sending more than the first term's samples is a waste. The "wcount"
+// variable can be set to the number of terms to have their samples stored.
+
+void write_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    int tcount = wps->num_terms, wcount = 1, temp;
+    struct decorr_pass *dpp;
+    uchar *byteptr;
+
+    byteptr = wpmd->data = malloc (256);
+    wpmd->id = ID_DECORR_SAMPLES;
+
+    for (dpp = wps->decorr_passes; tcount--; ++dpp)
+	if (wcount) {
+	    if (dpp->term > MAX_TERM) {
+		dpp->samples_A [0] = exp2s (temp = log2s (dpp->samples_A [0]));
+		*byteptr++ = temp;
+		*byteptr++ = temp >> 8;
+		dpp->samples_A [1] = exp2s (temp = log2s (dpp->samples_A [1]));
+		*byteptr++ = temp;
+		*byteptr++ = temp >> 8;
+
+		if (!(wps->wphdr.flags & MONO_FLAG)) {
+		    dpp->samples_B [0] = exp2s (temp = log2s (dpp->samples_B [0]));
+		    *byteptr++ = temp;
+		    *byteptr++ = temp >> 8;
+		    dpp->samples_B [1] = exp2s (temp = log2s (dpp->samples_B [1]));
+		    *byteptr++ = temp;
+		    *byteptr++ = temp >> 8;
+		}
+	    }
+	    else if (dpp->term < 0) {
+		dpp->samples_A [0] = exp2s (temp = log2s (dpp->samples_A [0]));
+		*byteptr++ = temp;
+		*byteptr++ = temp >> 8;
+		dpp->samples_B [0] = exp2s (temp = log2s (dpp->samples_B [0]));
+		*byteptr++ = temp;
+		*byteptr++ = temp >> 8;
+	    }
+	    else {
+		int m = 0, cnt = dpp->term;
+
+		while (cnt--) {
+		    dpp->samples_A [m] = exp2s (temp = log2s (dpp->samples_A [m]));
+		    *byteptr++ = temp;
+		    *byteptr++ = temp >> 8;
+
+		    if (!(wps->wphdr.flags & MONO_FLAG)) {
+			dpp->samples_B [m] = exp2s (temp = log2s (dpp->samples_B [m]));
+			*byteptr++ = temp;
+			*byteptr++ = temp >> 8;
+		    }
+
+		    m++;
+		}
+	    }
+
+	    wcount--;
+	}
+	else {
+	    CLEAR (dpp->samples_A);
+	    CLEAR (dpp->samples_B);
+	}
+
+    wpmd->byte_length = byteptr - (uchar *) wpmd->data;
+}
+
+// Allocate room for and copy the noise shaping info into the specified
+// metadata structure. These would normally be written to the
+// "correction" file and are used for lossless reconstruction of
+// hybrid data. The "delta" parameter is not yet used in encoding as it
+// will be part of the "quality" mode.
+
+void write_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    char *byteptr;
+    int temp;
+
+#if 0
+    if (wps->wphdr.block_samples) {
+	wps->dc.shaping_delta [0] = (-wps->dc.shaping_acc [0] - wps->dc.shaping_acc [0]) / (long) wps->wphdr.block_samples;
+	wps->dc.shaping_delta [1] = (-wps->dc.shaping_acc [1] - wps->dc.shaping_acc [1]) / (long) wps->wphdr.block_samples;
+    }
+#endif
+
+    byteptr = wpmd->data = malloc (12);
+    wpmd->id = ID_SHAPING_WEIGHTS;
+
+    wps->dc.error [0] = exp2s (temp = log2s (wps->dc.error [0]));
+    *byteptr++ = temp;
+    *byteptr++ = temp >> 8;
+    wps->dc.shaping_acc [0] = exp2s (temp = log2s (wps->dc.shaping_acc [0]));
+    *byteptr++ = temp;
+    *byteptr++ = temp >> 8;
+
+    if (!(wps->wphdr.flags & MONO_FLAG)) {
+	wps->dc.error [1] = exp2s (temp = log2s (wps->dc.error [1]));
+	*byteptr++ = temp;
+	*byteptr++ = temp >> 8;
+	wps->dc.shaping_acc [1] = exp2s (temp = log2s (wps->dc.shaping_acc [1]));
+	*byteptr++ = temp;
+	*byteptr++ = temp >> 8;
+    }
+
+    if (wps->dc.shaping_delta [0] | wps->dc.shaping_delta [1]) {
+	wps->dc.shaping_delta [0] = exp2s (temp = log2s (wps->dc.shaping_delta [0]));
+	*byteptr++ = temp;
+	*byteptr++ = temp >> 8;
+
+	if (!(wps->wphdr.flags & MONO_FLAG)) {
+	    wps->dc.shaping_delta [1] = exp2s (temp = log2s (wps->dc.shaping_delta [1]));
+	    *byteptr++ = temp;
+	    *byteptr++ = temp >> 8;
+	}
+    }
+
+    wpmd->byte_length = byteptr - (char *) wpmd->data;
+}
+
+// Allocate room for and copy the int32 data values into the specified
+// metadata structure. This data is used for integer data that has more
+// than 24 bits of magnitude or, in some cases, it's used to eliminate
+// redundant bits from any audio stream.
+
+void write_int32_info (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    char *byteptr;
+
+    byteptr = wpmd->data = malloc (4);
+    wpmd->id = ID_INT32_INFO;
+    *byteptr++ = wps->int32_sent_bits;
+    *byteptr++ = wps->int32_zeros;
+    *byteptr++ = wps->int32_ones;
+    *byteptr++ = wps->int32_dups;
+    wpmd->byte_length = byteptr - (char *) wpmd->data;
+}
+
+// Allocate room for and copy the multichannel information into the specified
+// metadata structure. The first byte is the total number of channels and the
+// following bytes represent the channel_mask as described for Microsoft
+// WAVEFORMATEX.
+
+void write_channel_info (WavpackContext *wpc, WavpackMetadata *wpmd)
+{
+    ulong mask = wpc->config.channel_mask;
+    char *byteptr;
+
+    byteptr = wpmd->data = malloc (4);
+    wpmd->id = ID_CHANNEL_INFO;
+    *byteptr++ = wpc->config.num_channels;
+
+    while (mask) {
+	*byteptr++ = mask;
+	mask >>= 8;
+    }
+
+    wpmd->byte_length = byteptr - (char *) wpmd->data;
+}
+
+// Allocate room for and copy the configuration information into the specified
+// metadata structure. Currently, we just store the upper 3 bytes of
+// config.flags and only in the first block of audio data. Note that this is
+// for informational purposes not required for playback or decoding (like
+// whether high or fast mode was specified).
+
+void write_config_info (WavpackContext *wpc, WavpackMetadata *wpmd)
+{
+    char *byteptr;
+
+    byteptr = wpmd->data = malloc (4);
+    wpmd->id = ID_CONFIG_BLOCK;
+    *byteptr++ = (char) (wpc->config.flags >> 8);
+    *byteptr++ = (char) (wpc->config.flags >> 16);
+    *byteptr++ = (char) (wpc->config.flags >> 24);
+    wpmd->byte_length = byteptr - (char *) wpmd->data;
+}
+
+// Pack an entire block of samples (either mono or stereo) into a completed
+// WavPack block. This function is actually a shell for pack_samples() and
+// performs tasks like handling any shift required by the format, preprocessing
+// of floating point data or integer data over 24 bits wide, and implementing
+// the "extra" mode (via the extra?.c modules). It is assumed that there is
+// sufficient space for the completed block at "wps->blockbuff" and that
+// "wps->blockend" points to the end of the available space. A return value of
+// FALSE indicates an error.
+
+static int scan_int32_data (WavpackStream *wps, long *values, long num_values);
+static void send_int32_data (WavpackStream *wps, long *values, long num_values);
+static int pack_samples (WavpackContext *wpc, long *buffer);
+
+int pack_block (WavpackContext *wpc, long *buffer)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    ulong flags = wps->wphdr.flags, sflags = wps->wphdr.flags;
+    ulong sample_count = wps->wphdr.block_samples;
+    long *orig_data = NULL;
+
+    if (flags & SHIFT_MASK) {
+	int shift = (flags & SHIFT_MASK) >> SHIFT_LSB;
+	int mag = (flags & MAG_MASK) >> MAG_LSB;
+	ulong cnt = sample_count;
+	long *ptr = buffer;
+
+	if (flags & MONO_FLAG)
+	    while (cnt--)
+		*ptr++ >>= shift;
+	else
+	    while (cnt--) {
+		*ptr++ >>= shift;
+		*ptr++ >>= shift;
+	    }
+
+	if ((mag -= shift) < 0)
+	    flags &= ~MAG_MASK;
+	else
+	    flags -= (1 << MAG_LSB) * shift;
+
+	wps->wphdr.flags = flags;
+    }
+
+    if ((flags & FLOAT_DATA) || (flags & MAG_MASK) >> MAG_LSB >= 24) {
+	if ((!(flags & HYBRID_FLAG) || wpc->wvc_flag) && !(wpc->config.flags & CONFIG_SKIP_WVX)) {
+	    orig_data = malloc (sizeof (f32) * ((flags & MONO_FLAG) ? sample_count : sample_count * 2));
+	    memcpy (orig_data, buffer, sizeof (f32) * ((flags & MONO_FLAG) ? sample_count : sample_count * 2));
+
+	    if (flags & FLOAT_DATA) {
+		wps->float_norm_exp = wpc->config.float_norm_exp;
+
+		if (!scan_float_data (wps, (f32 *) buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2)) {
+		    free (orig_data);
+		    orig_data = NULL;
+		}
+	    }
+	    else {
+		if (!scan_int32_data (wps, buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2)) {
+		    free (orig_data);
+		    orig_data = NULL;
+		}
+	    }
+	}
+	else {
+	    if (flags & FLOAT_DATA) {
+		wps->float_norm_exp = wpc->config.float_norm_exp;
+
+		if (scan_float_data (wps, (f32 *) buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2))
+		    wpc->lossy_blocks = TRUE;
+	    }
+	    else if (scan_int32_data (wps, buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2))
+		wpc->lossy_blocks = TRUE;
+	}
+
+	wpc->config.extra_flags |= EXTRA_SCAN_ONLY;
+    }
+    else if (wpc->config.extra_flags)
+	scan_int32_data (wps, buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2);
+
+    if (wpc->config.extra_flags) {
+	if (flags & MONO_FLAG)
+	    analyze_mono (wpc, buffer);
+	else
+	    analyze_stereo (wpc, buffer);
+    }
+    else if (!wps->sample_index || !wps->num_terms) {
+	wpc->config.extra_flags = EXTRA_SCAN_ONLY;
+
+	if (flags & MONO_FLAG)
+	    analyze_mono (wpc, buffer);
+	else
+	    analyze_stereo (wpc, buffer);
+
+	wpc->config.extra_flags = 0;
+    }
+
+    if (!pack_samples (wpc, buffer)) {
+	wps->wphdr.flags = sflags;
+
+	if (orig_data)
+	    free (orig_data);
+
+	return FALSE;
+    }
+    else
+	wps->wphdr.flags = sflags;
+
+    if (orig_data) {
+	ulong data_count;
+	uchar *cptr;
+
+	if (wpc->wvc_flag)
+	    cptr = wps->block2buff + ((WavpackHeader *) wps->block2buff)->ckSize + 8;
+	else
+	    cptr = wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 8;
+
+	bs_open_write (&wps->wvxbits, cptr + 8, wpc->wvc_flag ? wps->block2end : wps->blockend);
+
+	if (flags & FLOAT_DATA)
+	    send_float_data (wps, (f32*) orig_data, (flags & MONO_FLAG) ? sample_count : sample_count * 2);
+	else
+	    send_int32_data (wps, orig_data, (flags & MONO_FLAG) ? sample_count : sample_count * 2);
+
+	data_count = bs_close_write (&wps->wvxbits);
+	free (orig_data);
+
+	if (data_count) {
+	    if (data_count != (ulong) -1) {
+		*cptr++ = ID_WVX_BITSTREAM | ID_LARGE;
+		*cptr++ = (data_count += 4) >> 1;
+		*cptr++ = data_count >> 9;
+		*cptr++ = data_count >> 17;
+		*cptr++ = wps->crc_x;
+		*cptr++ = wps->crc_x >> 8;
+		*cptr++ = wps->crc_x >> 16;
+		*cptr = wps->crc_x >> 24;
+
+		if (wpc->wvc_flag)
+		    ((WavpackHeader *) wps->block2buff)->ckSize += data_count + 4;
+		else
+		    ((WavpackHeader *) wps->blockbuff)->ckSize += data_count + 4;
+	    }
+	    else
+		return FALSE;
+	}
+    }
+
+    return TRUE;
+}
+
+// Scan a buffer of long integer data and determine whether any redundancy in
+// the LSBs can be used to reduce the data's magnitude. If yes, then the
+// INT32_DATA flag is set and the int32 parameters are set. If bits must still
+// be transmitted literally to get down to 24 bits (which is all the integer
+// compression code can handle) then we return TRUE to indicate that a wvx
+// stream must be created in either lossless mode.
+
+static int scan_int32_data (WavpackStream *wps, long *values, long num_values)
+{
+    ulong magdata = 0, ordata = 0, xordata = 0, anddata = ~0;
+    ulong crc = 0xffffffff;
+    int total_shift = 0;
+    long *dp, count;
+
+    wps->int32_sent_bits = wps->int32_zeros = wps->int32_ones = wps->int32_dups = 0;
+
+    for (dp = values, count = num_values; count--; dp++) {
+	crc = crc * 9 + (*dp & 0xffff) * 3 + ((*dp >> 16) & 0xffff);
+	magdata |= (*dp < 0) ? ~*dp : *dp;
+	xordata |= *dp ^ -(*dp & 1);
+	anddata &= *dp;
+	ordata |= *dp;
+    }
+
+    wps->crc_x = crc;
+    wps->wphdr.flags &= ~MAG_MASK;
+
+    while (magdata) {
+	wps->wphdr.flags += 1 << MAG_LSB;
+	magdata >>= 1;
+    }
+
+    if (!((wps->wphdr.flags & MAG_MASK) >> MAG_LSB)) {
+	wps->wphdr.flags &= ~INT32_DATA;
+	return FALSE;
+    }
+
+    if (!(ordata & 1))
+	while (!(ordata & 1)) {
+	    wps->wphdr.flags -= 1 << MAG_LSB;
+	    wps->int32_zeros++;
+	    total_shift++;
+	    ordata >>= 1;
+	}
+    else if (anddata & 1)
+	while (anddata & 1) {
+	    wps->wphdr.flags -= 1 << MAG_LSB;
+	    wps->int32_ones++;
+	    total_shift++;
+	    anddata >>= 1;
+	}
+    else if (!(xordata & 2))
+	while (!(xordata & 2)) {
+	    wps->wphdr.flags -= 1 << MAG_LSB;
+	    wps->int32_dups++;
+	    total_shift++;
+	    xordata >>= 1;
+	}
+
+    if (((wps->wphdr.flags & MAG_MASK) >> MAG_LSB) > 23) {
+	wps->int32_sent_bits = ((wps->wphdr.flags & MAG_MASK) >> MAG_LSB) - 23;
+	total_shift += wps->int32_sent_bits;
+	wps->wphdr.flags &= ~MAG_MASK;
+	wps->wphdr.flags += 23 << MAG_LSB;
+    }
+
+    if (total_shift) {
+	wps->wphdr.flags |= INT32_DATA;
+
+	for (dp = values, count = num_values; count--; dp++)
+	    *dp >>= total_shift;
+    }
+
+#if 0
+    if (wps->int32_sent_bits + wps->int32_zeros + wps->int32_ones + wps->int32_dups)
+	error_line ("sent bits = %d, zeros/ones/dups = %d/%d/%d", wps->int32_sent_bits,
+	    wps->int32_zeros, wps->int32_ones, wps->int32_dups);
+#endif
+
+    return wps->int32_sent_bits;
+}
+
+// For the specified buffer values and the int32 parameters stored in "wps",
+// send the literal bits required to the "wvxbits" bitstream.
+
+static void send_int32_data (WavpackStream *wps, long *values, long num_values)
+{
+    int sent_bits = wps->int32_sent_bits, pre_shift;
+    long mask = (1 << sent_bits) - 1;
+    long count, value, *dp;
+
+    pre_shift = wps->int32_zeros + wps->int32_ones + wps->int32_dups;
+
+    if (sent_bits)
+	for (dp = values, count = num_values; count--; dp++) {
+	    value = (*dp >> pre_shift) & mask;
+	    putbits (value, sent_bits, &wps->wvxbits);
+	}
+}
+
+// Pack an entire block of samples (either mono or stereo) into a completed
+// WavPack block. It is assumed that there is sufficient space for the
+// completed block at "wps->blockbuff" and that "wps->blockend" points to the
+// end of the available space. A return value of FALSE indicates an error.
+// Any unsent metadata is transmitted first, then required metadata for this
+// block is sent, and finally the compressed integer data is sent. If a "wpx"
+// stream is required for floating point data or large integer data, then this
+// must be handled outside this function. To find out how much data was written
+// the caller must look at the ckSize field of the written WavpackHeader, NOT
+// the one in the WavpackStream.
+
+static int pack_samples (WavpackContext *wpc, long *buffer)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    ulong sample_count = wps->wphdr.block_samples;
+    ulong flags = wps->wphdr.flags, data_count;
+    /* int mag16 = ((flags & MAG_MASK) >> MAG_LSB) >= 16; */
+    int tcount, lossy = FALSE, m = 0;
+    double noise_acc = 0.0, noise;
+    struct decorr_pass *dpp;
+    WavpackMetadata wpmd;
+    ulong crc, crc2, i;
+    long *bptr;
+
+    crc = crc2 = 0xffffffff;
+
+    wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
+    memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader));
+
+    if (wpc->metacount) {
+	WavpackMetadata *wpmdp = wpc->metadata;
+
+	while (wpc->metacount) {
+	    copy_metadata (wpmdp, wps->blockbuff, wps->blockend);
+	    wpc->metabytes -= wpmdp->byte_length;
+	    free_metadata (wpmdp++);
+	    wpc->metacount--;
+	}
+
+	free (wpc->metadata);
+	wpc->metadata = NULL;
+    }
+
+    if (!sample_count)
+	return TRUE;
+
+    write_decorr_terms (wps, &wpmd);
+    copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+    free_metadata (&wpmd);
+
+    write_decorr_weights (wps, &wpmd);
+    copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+    free_metadata (&wpmd);
+
+    write_decorr_samples (wps, &wpmd);
+    copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+    free_metadata (&wpmd);
+
+    write_entropy_vars (wps, &wpmd);
+    copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+    free_metadata (&wpmd);
+
+    if (flags & HYBRID_FLAG) {
+	write_hybrid_profile (wps, &wpmd);
+	copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+	free_metadata (&wpmd);
+    }
+
+    if (flags & FLOAT_DATA) {
+	write_float_info (wps, &wpmd);
+	copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+	free_metadata (&wpmd);
+    }
+
+    if (flags & INT32_DATA) {
+	write_int32_info (wps, &wpmd);
+	copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+	free_metadata (&wpmd);
+    }
+
+    if ((flags & INITIAL_BLOCK) &&
+	(wpc->config.num_channels > 2 ||
+	wpc->config.channel_mask != 0x5 - wpc->config.num_channels)) {
+	    write_channel_info (wpc, &wpmd);
+	    copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+	    free_metadata (&wpmd);
+    }
+
+    if ((flags & INITIAL_BLOCK) && !wps->sample_index) {
+	write_config_info (wpc, &wpmd);
+	copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+	free_metadata (&wpmd);
+    }
+
+    bs_open_write (&wps->wvbits, wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 12, wps->blockend);
+
+    if (wpc->wvc_flag) {
+	wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
+	memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader));
+
+	if (flags & HYBRID_SHAPE) {
+	    write_shaping_info (wps, &wpmd);
+	    copy_metadata (&wpmd, wps->block2buff, wps->block2end);
+	    free_metadata (&wpmd);
+	}
+
+	bs_open_write (&wps->wvcbits, wps->block2buff + ((WavpackHeader *) wps->block2buff)->ckSize + 12, wps->block2end);
+    }
+
+    /////////////////////// handle lossless mono mode /////////////////////////
+
+    if (!(flags & HYBRID_FLAG) && (flags & MONO_FLAG))
+	for (bptr = buffer, i = 0; i < sample_count; ++i) {
+	    long code;
+
+	    crc = crc * 3 + (code = *bptr++);
+
+	    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
+		long sam;
+
+		if (dpp->term > MAX_TERM) {
+		    if (dpp->term & 1)
+			sam = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+		    else
+			sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+
+		    dpp->samples_A [1] = dpp->samples_A [0];
+		    dpp->samples_A [0] = code;
+		}
+		else {
+		    sam = dpp->samples_A [m];
+		    dpp->samples_A [(m + dpp->term) & (MAX_TERM - 1)] = code;
+		}
+
+		code -= apply_weight (dpp->weight_A, sam);
+		update_weight (dpp->weight_A, dpp->delta, sam, code);
+	    }
+
+	    m = (m + 1) & (MAX_TERM - 1);
+	    send_word_lossless (wps, code, 0);
+	}
+
+    //////////////////// handle the lossless stereo mode //////////////////////
+
+    else if (!(flags & HYBRID_FLAG) && !(flags & MONO_FLAG))
+	for (bptr = buffer, i = 0; i < sample_count; ++i, bptr += 2) {
+	    long left, right, sam_A, sam_B;
+
+	    crc = crc * 3 + (left = bptr [0]);
+	    crc = crc * 3 + (right = bptr [1]);
+
+	    if (flags & JOINT_STEREO)
+		right += ((left -= right) >> 1);
+
+	    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount-- ; dpp++) {
+		if (dpp->term > 0) {
+		    if (dpp->term > MAX_TERM) {
+			if (dpp->term & 1) {
+			    sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+			    sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
+			}
+			else {
+			    sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+			    sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
+			}
+
+			dpp->samples_A [1] = dpp->samples_A [0];
+			dpp->samples_B [1] = dpp->samples_B [0];
+			dpp->samples_A [0] = left;
+			dpp->samples_B [0] = right;
+		    }
+		    else {
+			int k = (m + dpp->term) & (MAX_TERM - 1);
+
+			sam_A = dpp->samples_A [m];
+			sam_B = dpp->samples_B [m];
+			dpp->samples_A [k] = left;
+			dpp->samples_B [k] = right;
+		    }
+
+		    left -= apply_weight (dpp->weight_A, sam_A);
+		    right -= apply_weight (dpp->weight_B, sam_B);
+		    update_weight (dpp->weight_A, dpp->delta, sam_A, left);
+		    update_weight (dpp->weight_B, dpp->delta, sam_B, right);
+		}
+		else {
+		    sam_A = (dpp->term == -2) ? right : dpp->samples_A [0];
+		    sam_B = (dpp->term == -1) ? left : dpp->samples_B [0];
+		    dpp->samples_A [0] = right;
+		    dpp->samples_B [0] = left;
+		    left -= apply_weight (dpp->weight_A, sam_A);
+		    right -= apply_weight (dpp->weight_B, sam_B);
+		    update_weight_clip (dpp->weight_A, dpp->delta, sam_A, left);
+		    update_weight_clip (dpp->weight_B, dpp->delta, sam_B, right);
+		}
+	    }
+
+	    m = (m + 1) & (MAX_TERM - 1);
+	    send_word_lossless (wps, left, 0);
+	    send_word_lossless (wps, right, 1);
+	}
+
+    /////////////////// handle the lossy/hybrid mono mode /////////////////////
+
+    else if ((flags & HYBRID_FLAG) && (flags & MONO_FLAG))
+	for (bptr = buffer, i = 0; i < sample_count; ++i) {
+	    long code, temp;
+
+	    crc2 = crc2 * 3 + (code = *bptr++);
+
+	    if (flags & HYBRID_SHAPE) {
+		int shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
+		temp = -apply_weight (shaping_weight, wps->dc.error [0]);
+
+		if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) {
+		    if (temp == wps->dc.error [0])
+			temp = (temp < 0) ? temp + 1 : temp - 1;
+
+		    wps->dc.error [0] = -code;
+		    code += temp;
+		}
+		else
+		    wps->dc.error [0] = -(code += temp);
+	    }
+
+	    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount-- ; dpp++)
+		if (dpp->term > MAX_TERM) {
+		    if (dpp->term & 1)
+			dpp->samples_A [2] = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+		    else
+			dpp->samples_A [2] = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+
+		    code -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [2]));
+		}
+		else
+		    code -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [m]));
+
+	    code = send_word (wps, code, 0);
+
+	    while (--dpp >= wps->decorr_passes) {
+		if (dpp->term > MAX_TERM) {
+		    update_weight (dpp->weight_A, dpp->delta, dpp->samples_A [2], code);
+		    dpp->samples_A [1] = dpp->samples_A [0];
+		    dpp->samples_A [0] = (code += dpp->aweight_A);
+		}
+		else {
+		    long sam = dpp->samples_A [m];
+
+		    update_weight (dpp->weight_A, dpp->delta, sam, code);
+		    dpp->samples_A [(m + dpp->term) & (MAX_TERM - 1)] = (code += dpp->aweight_A);
+		}
+	    }
+
+	    wps->dc.error [0] += code;
+	    m = (m + 1) & (MAX_TERM - 1);
+
+	    if ((crc = crc * 3 + code) != crc2)
+		lossy = TRUE;
+
+	    if (wpc->config.flags & CONFIG_CALC_NOISE) {
+		noise = code - bptr [-1];
+
+		noise_acc += noise *= noise;
+		wps->dc.noise_ave = (wps->dc.noise_ave * 0.99) + (noise * 0.01);
+
+		if (wps->dc.noise_ave > wps->dc.noise_max)
+		    wps->dc.noise_max = wps->dc.noise_ave;
+	    }
+	}
+
+    /////////////////// handle the lossy/hybrid stereo mode ///////////////////
+
+    else if ((flags & HYBRID_FLAG) && !(flags & MONO_FLAG))
+	for (bptr = buffer, i = 0; i < sample_count; ++i) {
+	    long left, right, temp;
+	    int shaping_weight;
+
+	    left = *bptr++;
+	    crc2 = (crc2 * 3 + left) * 3 + (right = *bptr++);
+
+	    if (flags & HYBRID_SHAPE) {
+		shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
+		temp = -apply_weight (shaping_weight, wps->dc.error [0]);
+
+		if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) {
+		    if (temp == wps->dc.error [0])
+			temp = (temp < 0) ? temp + 1 : temp - 1;
+
+		    wps->dc.error [0] = -left;
+		    left += temp;
+		}
+		else
+		    wps->dc.error [0] = -(left += temp);
+
+		shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16;
+		temp = -apply_weight (shaping_weight, wps->dc.error [1]);
+
+		if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) {
+		    if (temp == wps->dc.error [1])
+			temp = (temp < 0) ? temp + 1 : temp - 1;
+
+		    wps->dc.error [1] = -right;
+		    right += temp;
+		}
+		else
+		    wps->dc.error [1] = -(right += temp);
+	    }
+
+	    if (flags & JOINT_STEREO)
+		right += ((left -= right) >> 1);
+
+	    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount-- ; dpp++)
+		if (dpp->term > MAX_TERM) {
+		    if (dpp->term & 1) {
+			dpp->samples_A [2] = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+			dpp->samples_B [2] = 2 * dpp->samples_B [0] - dpp->samples_B [1];
+		    }
+		    else {
+			dpp->samples_A [2] = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+			dpp->samples_B [2] = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
+		    }
+
+		    left -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [2]));
+		    right -= (dpp->aweight_B = apply_weight (dpp->weight_B, dpp->samples_B [2]));
+		}
+		else if (dpp->term > 0) {
+		    left -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [m]));
+		    right -= (dpp->aweight_B = apply_weight (dpp->weight_B, dpp->samples_B [m]));
+		}
+		else {
+		    if (dpp->term == -1)
+			dpp->samples_B [0] = left;
+		    else if (dpp->term == -2)
+			dpp->samples_A [0] = right;
+
+		    left -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [0]));
+		    right -= (dpp->aweight_B = apply_weight (dpp->weight_B, dpp->samples_B [0]));
+		}
+#if 0
+if (labs (left) > 60000000 || labs (right) > 60000000)
+    error_line ("sending %d, %d; samples = %d, %d", left, right, bptr [-2], bptr [-1]);
+#endif
+	    left = send_word (wps, left, 0);
+	    right = send_word (wps, right, 1);
+
+	    while (--dpp >= wps->decorr_passes)
+		if (dpp->term > MAX_TERM) {
+		    update_weight (dpp->weight_A, dpp->delta, dpp->samples_A [2], left);
+		    update_weight (dpp->weight_B, dpp->delta, dpp->samples_B [2], right);
+
+		    dpp->samples_A [1] = dpp->samples_A [0];
+		    dpp->samples_B [1] = dpp->samples_B [0];
+
+		    dpp->samples_A [0] = (left += dpp->aweight_A);
+		    dpp->samples_B [0] = (right += dpp->aweight_B);
+		}
+		else if (dpp->term > 0) {
+		    int k = (m + dpp->term) & (MAX_TERM - 1);
+
+		    update_weight (dpp->weight_A, dpp->delta, dpp->samples_A [m], left);
+		    dpp->samples_A [k] = (left += dpp->aweight_A);
+
+		    update_weight (dpp->weight_B, dpp->delta, dpp->samples_B [m], right);
+		    dpp->samples_B [k] = (right += dpp->aweight_B);
+		}
+		else {
+		    if (dpp->term == -1) {
+			dpp->samples_B [0] = left + dpp->aweight_A;
+			dpp->aweight_B = apply_weight (dpp->weight_B, dpp->samples_B [0]);
+		    }
+		    else if (dpp->term == -2) {
+			dpp->samples_A [0] = right + dpp->aweight_B;
+			dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [0]);
+		    }
+
+		    update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], left);
+		    update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], right);
+		    dpp->samples_B [0] = (left += dpp->aweight_A);
+		    dpp->samples_A [0] = (right += dpp->aweight_B);
+		}
+
+	    if (flags & JOINT_STEREO)
+		left += (right -= (left >> 1));
+
+	    wps->dc.error [0] += left;
+	    wps->dc.error [1] += right;
+	    m = (m + 1) & (MAX_TERM - 1);
+
+	    if ((crc = (crc * 3 + left) * 3 + right) != crc2)
+		lossy = TRUE;
+
+	    if (wpc->config.flags & CONFIG_CALC_NOISE) {
+		noise = (double)(left - bptr [-2]) * (left - bptr [-2]);
+		noise += (double)(right - bptr [-1]) * (right - bptr [-1]);
+
+		noise_acc += noise /= 2.0;
+		wps->dc.noise_ave = (wps->dc.noise_ave * 0.99) + (noise * 0.01);
+
+		if (wps->dc.noise_ave > wps->dc.noise_max)
+		    wps->dc.noise_max = wps->dc.noise_ave;
+	    }
+	}
+
+    if (m)
+	for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
+	    if (dpp->term > 0 && dpp->term <= MAX_TERM) {
+		long temp_A [MAX_TERM], temp_B [MAX_TERM];
+		int k;
+
+		memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A));
+		memcpy (temp_B, dpp->samples_B, sizeof (dpp->samples_B));
+
+		for (k = 0; k < MAX_TERM; k++) {
+		    dpp->samples_A [k] = temp_A [m];
+		    dpp->samples_B [k] = temp_B [m];
+		    m = (m + 1) & (MAX_TERM - 1);
+		}
+	    }
+
+    if (wpc->config.flags & CONFIG_CALC_NOISE)
+	wps->dc.noise_sum += noise_acc;
+
+    flush_word (wps);
+    data_count = bs_close_write (&wps->wvbits);
+
+    if (data_count) {
+	if (data_count != (ulong) -1) {
+	    uchar *cptr = wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 8;
+
+	    *cptr++ = ID_WV_BITSTREAM | ID_LARGE;
+	    *cptr++ = data_count >> 1;
+	    *cptr++ = data_count >> 9;
+	    *cptr++ = data_count >> 17;
+	    ((WavpackHeader *) wps->blockbuff)->ckSize += data_count + 4;
+	}
+	else
+	    return FALSE;
+    }
+
+    ((WavpackHeader *) wps->blockbuff)->crc = crc;
+
+    if (wpc->wvc_flag) {
+	data_count = bs_close_write (&wps->wvcbits);
+
+	if (data_count && lossy) {
+	    if (data_count != (ulong) -1) {
+		uchar *cptr = wps->block2buff + ((WavpackHeader *) wps->block2buff)->ckSize + 8;
+
+		*cptr++ = ID_WVC_BITSTREAM | ID_LARGE;
+		*cptr++ = data_count >> 1;
+		*cptr++ = data_count >> 9;
+		*cptr++ = data_count >> 17;
+		((WavpackHeader *) wps->block2buff)->ckSize += data_count + 4;
+	    }
+	    else
+		return FALSE;
+	}
+
+	((WavpackHeader *) wps->block2buff)->crc = crc2;
+    }
+    else if (lossy)
+	wpc->lossy_blocks = TRUE;
+
+    wps->sample_index += sample_count;
+    return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// This function returns the accumulated RMS noise as a double if the       //
+// CALC_NOISE bit was set in the WavPack header. The peak noise can also be //
+// returned if desired. See wavpack.c for the calculations required to      //
+// convert this into decibels of noise below full scale.                    //
+//////////////////////////////////////////////////////////////////////////////
+
+double pack_noise (WavpackContext *wpc, double *peak)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+
+    if (peak)
+	*peak = wps->dc.noise_max;
+
+    return wps->dc.noise_sum;
+}
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/unpack.c	2005-03-30 11:33:44.000000000 +0100
@@ -0,0 +1,966 @@
+////////////////////////////////////////////////////////////////////////////
+//			     **** WAVPACK ****				  //
+//		    Hybrid Lossless Wavefile Compressor			  //
+//		Copyright (c) 1998 - 2004 Conifer Software.		  //
+//			    All Rights Reserved.			  //
+//      Distributed under the BSD Software License (see license.txt)      //
+////////////////////////////////////////////////////////////////////////////
+
+// unpack.c
+
+// This module actually handles the decompression of the audio data, except
+// for the entropy decoding which is handled by the words? modules. For
+// maximum efficiency, the conversion is isolated to tight loops that handle
+// an entire buffer.
+
+#include "wavpack.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#define LOSSY_MUTE
+
+#ifdef DEBUG_ALLOC
+#define malloc malloc_db
+#define realloc realloc_db
+#define free free_db
+void *malloc_db (ulong size);
+void *realloc_db (void *ptr, ulong size);
+void free_db (void *ptr);
+long dump_alloc (void);
+#endif
+
+///////////////////////////// executable code ////////////////////////////////
+
+// This function initializes everything required to unpack a WavPack block
+// and must be called before unpack_samples() is called to obtain audio data.
+// It is assumed that the WavpackHeader has been read into the wps->wphdr
+// (in the current WavpackStream) and that the entire block has been read at
+// wps->blockbuff. If a correction file is available (wpc->wvc_flag = TRUE)
+// then the corresponding correction block must be read into wps->block2buff
+// and its WavpackHeader has overwritten the header at wps->wphdr. This is
+// where all the metadata blocks are scanned including those that contain
+// bitstream data.
+
+int unpack_init (WavpackContext *wpc)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    uchar *blockptr, *block2ptr;
+    WavpackMetadata wpmd;
+
+    if (wps->wphdr.block_samples && wps->wphdr.block_index != (ulong) -1)
+	wps->sample_index = wps->wphdr.block_index;
+
+    wps->mute_error = FALSE;
+    wps->crc = wps->crc_x = 0xffffffff;
+    CLEAR (wps->wvbits);
+    CLEAR (wps->wvcbits);
+    CLEAR (wps->wvxbits);
+    CLEAR (wps->decorr_passes);
+    CLEAR (wps->dc);
+    CLEAR (wps->w);
+
+    blockptr = wps->blockbuff + sizeof (WavpackHeader);
+
+    while (read_metadata_buff (&wpmd, wps->blockbuff, &blockptr))
+	if (!process_metadata (wpc, &wpmd)) {
+	    sprintf (wpc->error_message, "invalid metadata %2x!", wpmd.id);
+	    return FALSE;
+	}
+
+    block2ptr = wps->block2buff + sizeof (WavpackHeader);
+
+    while (wpc->wvc_flag && wps->wphdr.block_samples && read_metadata_buff (&wpmd, wps->block2buff, &block2ptr))
+	if (!process_metadata (wpc, &wpmd)) {
+	    sprintf (wpc->error_message, "invalid metadata %2x in wvc file!", wpmd.id);
+	    return FALSE;
+	}
+
+    if (wps->wphdr.block_samples && !bs_is_open (&wps->wvbits)) {
+	if (bs_is_open (&wps->wvcbits))
+	    strcpy (wpc->error_message, "can't unpack correction files alone!");
+
+	return FALSE;
+    }
+
+    if (wps->wphdr.block_samples && !bs_is_open (&wps->wvxbits)) {
+	if ((wps->wphdr.flags & INT32_DATA) && wps->int32_sent_bits)
+	    wpc->lossy_blocks = TRUE;
+
+	if ((wps->wphdr.flags & FLOAT_DATA) &&
+	    wps->float_flags & (FLOAT_EXCEPTIONS | FLOAT_ZEROS_SENT | FLOAT_SHIFT_SENT | FLOAT_SHIFT_SAME))
+		wpc->lossy_blocks = TRUE;
+    }
+
+    return TRUE;
+}
+
+// This function initialzes the main bitstream for audio samples, which must
+// be in the "wv" file.
+
+int init_wv_bitstream (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    bs_open_read (&wps->wvbits, wpmd->data, (char *) wpmd->data + wpmd->byte_length);
+    return TRUE;
+}
+
+// This function initialzes the "correction" bitstream for audio samples,
+// which currently must be in the "wvc" file.
+
+int init_wvc_bitstream (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    bs_open_read (&wps->wvcbits, wpmd->data, (char *) wpmd->data + wpmd->byte_length);
+    return TRUE;
+}
+
+// This function initialzes the "extra" bitstream for audio samples which
+// contains the information required to losslessly decompress 32-bit float data
+// or integer data that exceeds 24 bits. This bitstream is in the "wv" file
+// for pure lossless data or the "wvc" file for hybrid lossless. This data
+// would not be used for hybrid lossy mode. There is also a 32-bit CRC stored
+// in the first 4 bytes of these blocks.
+
+int init_wvx_bitstream (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    uchar *cp = wpmd->data;
+
+    wps->crc_wvx = *cp++;
+    wps->crc_wvx |= (long) *cp++ << 8;
+    wps->crc_wvx |= (long) *cp++ << 16;
+    wps->crc_wvx |= (long) *cp++ << 24;
+
+    bs_open_read (&wps->wvxbits, cp, (char *) wpmd->data + wpmd->byte_length);
+    return TRUE;
+}
+
+// Read decorrelation terms from specified metadata block into the
+// decorr_passes array. The terms range from -3 to 8, plus 17 & 18;
+// other values are reserved and generate errors for now. The delta
+// ranges from 0 to 7 with all values valid. Note that the terms are
+// stored in the opposite order in the decorr_passes array compared
+// to packing.
+
+int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    int termcnt = wpmd->byte_length;
+    uchar *byteptr = wpmd->data;
+    struct decorr_pass *dpp;
+
+    if (termcnt > MAX_NTERMS)
+	return FALSE;
+
+    wps->num_terms = termcnt;
+
+    for (dpp = wps->decorr_passes + termcnt - 1; termcnt--; dpp--) {
+	dpp->term = (int)(*byteptr & 0x1f) - 5;
+	dpp->delta = (*byteptr++ >> 5) & 0x7;
+
+	if (!dpp->term || dpp->term < -3 || (dpp->term > MAX_TERM && dpp->term < 17) || dpp->term > 18)
+	    return FALSE;
+    }
+
+    return TRUE;
+}
+
+// Read decorrelation weights from specified metadata block into the
+// decorr_passes array. The weights range +/-1024, but are rounded and
+// truncated to fit in signed chars for metadata storage. Weights are
+// separate for the two channels and are specified from the "last" term
+// (first during encode). Unspecified weights are set to zero.
+
+int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    int termcnt = wpmd->byte_length, tcount;
+    char *byteptr = wpmd->data;
+    struct decorr_pass *dpp;
+
+    if (!(wps->wphdr.flags & MONO_FLAG))
+	termcnt /= 2;
+
+    if (termcnt > wps->num_terms)
+	return FALSE;
+
+    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
+	dpp->weight_A = dpp->weight_B = 0;
+
+    while (--dpp >= wps->decorr_passes && termcnt--) {
+	dpp->weight_A = restore_weight (*byteptr++);
+
+	if (!(wps->wphdr.flags & MONO_FLAG))
+	    dpp->weight_B = restore_weight (*byteptr++);
+    }
+
+    return TRUE;
+}
+
+// Read decorrelation samples from specified metadata block into the
+// decorr_passes array. The samples are signed 32-bit values, but are
+// converted to signed log2 values for storage in metadata. Values are
+// stored for both channels and are specified from the "last" term
+// (first during encode) with unspecified samples set to zero. The
+// number of samples stored varies with the actual term value, so
+// those must obviously come first in the metadata.
+
+int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    uchar *byteptr = wpmd->data;
+    uchar *endptr = byteptr + wpmd->byte_length;
+    struct decorr_pass *dpp;
+    int tcount;
+
+    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
+	CLEAR (dpp->samples_A);
+	CLEAR (dpp->samples_B);
+    }
+
+    if (wps->wphdr.version == 0x402 && (wps->wphdr.flags & HYBRID_FLAG)) {
+	wps->dc.error [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+	byteptr += 2;
+
+	if (!(wps->wphdr.flags & MONO_FLAG)) {
+	    wps->dc.error [1] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+	    byteptr += 2;
+	}
+    }
+
+    while (dpp-- > wps->decorr_passes && byteptr < endptr)
+	if (dpp->term > MAX_TERM) {
+	    dpp->samples_A [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+	    dpp->samples_A [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8)));
+	    byteptr += 4;
+
+	    if (!(wps->wphdr.flags & MONO_FLAG)) {
+		dpp->samples_B [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+		dpp->samples_B [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8)));
+		byteptr += 4;
+	    }
+	}
+	else if (dpp->term < 0) {
+	    dpp->samples_A [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+	    dpp->samples_B [0] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8)));
+	    byteptr += 4;
+	}
+	else {
+	    int m = 0, cnt = dpp->term;
+
+	    while (cnt--) {
+		dpp->samples_A [m] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+		byteptr += 2;
+
+		if (!(wps->wphdr.flags & MONO_FLAG)) {
+		    dpp->samples_B [m] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+		    byteptr += 2;
+		}
+
+		m++;
+	    }
+	}
+
+    return byteptr == endptr;
+}
+
+// Read the shaping weights from specified metadata block into the
+// WavpackStream structure. Note that there must be two values (even
+// for mono streams) and that the values are stored in the same
+// manner as decorrelation weights. These would normally be read from
+// the "correction" file and are used for lossless reconstruction of
+// hybrid data.
+
+int read_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    if (wpmd->byte_length == 2) {
+	char *byteptr = wpmd->data;
+
+	wps->dc.shaping_acc [0] = (long) restore_weight (*byteptr++) << 16;
+	wps->dc.shaping_acc [1] = (long) restore_weight (*byteptr++) << 16;
+	return TRUE;
+    }
+    else if (wpmd->byte_length >= (wps->wphdr.flags & MONO_FLAG ? 4 : 8)) {
+	uchar *byteptr = wpmd->data;
+
+	wps->dc.error [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+	wps->dc.shaping_acc [0] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8)));
+	byteptr += 4;
+
+	if (!(wps->wphdr.flags & MONO_FLAG)) {
+	    wps->dc.error [1] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+	    wps->dc.shaping_acc [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8)));
+	    byteptr += 4;
+	}
+
+	if (wpmd->byte_length == (wps->wphdr.flags & MONO_FLAG ? 6 : 12)) {
+	    wps->dc.shaping_delta [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+
+	    if (!(wps->wphdr.flags & MONO_FLAG))
+		wps->dc.shaping_delta [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8)));
+	}
+
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
+// Read the int32 data from the specified metadata into the specified stream.
+// This data is used for integer data that has more than 24 bits of magnitude
+// or, in some cases, used to eliminate redundant bits from any audio stream.
+
+int read_int32_info (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    int bytecnt = wpmd->byte_length;
+    char *byteptr = wpmd->data;
+
+    if (bytecnt != 4)
+	return FALSE;
+
+    wps->int32_sent_bits = *byteptr++;
+    wps->int32_zeros = *byteptr++;
+    wps->int32_ones = *byteptr++;
+    wps->int32_dups = *byteptr;
+
+    return TRUE;
+}
+
+// Read multichannel information from metadata. The first byte is the total
+// number of channels and the following bytes represent the channel_mask
+// as described for Microsoft WAVEFORMATEX.
+
+int read_channel_info (WavpackContext *wpc, WavpackMetadata *wpmd)
+{
+    int bytecnt = wpmd->byte_length, shift = 0;
+    char *byteptr = wpmd->data;
+    ulong mask = 0;
+
+    if (!bytecnt || bytecnt > 5)
+	return FALSE;
+
+    wpc->config.num_channels = *byteptr++;
+
+    while (--bytecnt) {
+	mask |= (ulong) *byteptr++ << shift;
+	shift += 8;
+    }
+
+    wpc->config.channel_mask = mask;
+    return TRUE;
+}
+
+// Read configuration information from metadata.
+
+int read_config_info (WavpackContext *wpc, WavpackMetadata *wpmd)
+{
+    int bytecnt = wpmd->byte_length;
+    uchar *byteptr = wpmd->data;
+
+    if (bytecnt >= 3) {
+	wpc->config.flags &= 0xff;
+	wpc->config.flags |= (long) *byteptr++ << 8;
+	wpc->config.flags |= (long) *byteptr++ << 16;
+	wpc->config.flags |= (long) *byteptr << 24;
+    }
+
+    return TRUE;
+}
+
+// Read wrapper data from metadata. Currently, this consists of the RIFF
+// header and trailer that wav files contain around the audio data but could
+// be used for other formats as well. Because WavPack files contain all the
+// information required for decoding and playback, this data can probably
+// be ignored except when an exact wavefile restoration is needed.
+
+int read_wrapper_data (WavpackContext *wpc, WavpackMetadata *wpmd)
+{
+    if (wpc->open_flags & OPEN_WRAPPER) {
+	wpc->wrapper_data = realloc (wpc->wrapper_data, wpc->wrapper_bytes + wpmd->byte_length);
+	memcpy (wpc->wrapper_data + wpc->wrapper_bytes, wpmd->data, wpmd->byte_length);
+	wpc->wrapper_bytes += wpmd->byte_length;
+    }
+
+    return TRUE;
+}
+
+#ifdef UNPACK
+
+// This monster actually unpacks the WavPack bitstream(s) into the specified
+// buffer as 32-bit integers or floats (depending on orignal data). Lossy
+// samples will be clipped to their original limits (i.e. 8-bit samples are
+// clipped to -128/+127) but are still returned in longs. It is up to the
+// caller to potentially reformat this for the final output including any
+// multichannel distribution, block alignment or endian compensation. The
+// function unpack_init() must have been called and the entire WavPack block
+// must still be visible (although wps->blockbuff will not be accessed again).
+// For maximum clarity, the function is broken up into segments that handle
+// various modes. This makes for a few extra infrequent flag checks, but
+// makes the code easier to follow because the nesting does not become so
+// deep. For maximum efficiency, the conversion is isolated to tight loops
+// that handle an entire buffer. The function returns the total number of
+// samples unpacked, which can be less than the number requested if an error
+// occurs or the end of the block is reached.
+
+static void fixup_samples (WavpackContext *wpc, long *buffer, ulong sample_count);
+
+long unpack_samples (WavpackContext *wpc, long *buffer, ulong sample_count)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    ulong flags = wps->wphdr.flags, crc = wps->crc, i = 0;
+    long mute_limit = (1L << ((flags & MAG_MASK) >> MAG_LSB)) + 2;
+    long correction [2], read_word, *bptr;
+    struct decorr_pass *dpp;
+    int tcount, m = 0;
+
+    if (wps->sample_index + sample_count > wps->wphdr.block_index + wps->wphdr.block_samples)
+	sample_count = wps->wphdr.block_index + wps->wphdr.block_samples - wps->sample_index;
+
+    if (wps->mute_error) {
+	memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8));
+	wps->sample_index += sample_count;
+	return sample_count;
+    }
+
+    if ((flags & HYBRID_FLAG) && !wpc->wvc_flag)
+	mute_limit *= 2;
+
+    ///////////////// handle version 4 lossless mono data /////////////////////
+
+    if (!(flags & HYBRID_FLAG) && (flags & MONO_FLAG))
+	for (bptr = buffer, i = 0; i < sample_count; ++i) {
+	    if ((read_word = get_word_lossless (wps, 0)) == WORD_EOF)
+		break;
+
+	    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
+		long sam, temp;
+		int k;
+
+		if (dpp->term > MAX_TERM) {
+		    if (dpp->term & 1)
+			sam = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+		    else
+			sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+
+		    dpp->samples_A [1] = dpp->samples_A [0];
+		    k = 0;
+		}
+		else {
+		    sam = dpp->samples_A [m];
+		    k = (m + dpp->term) & (MAX_TERM - 1);
+		}
+
+		temp = apply_weight (dpp->weight_A, sam) + read_word;
+		update_weight (dpp->weight_A, dpp->delta, sam, read_word);
+		dpp->samples_A [k] = read_word = temp;
+	    }
+
+	    if (labs (read_word) > mute_limit)
+		break;
+
+	    m = (m + 1) & (MAX_TERM - 1);
+	    crc = crc * 3 + read_word;
+	    *bptr++ = read_word;
+	}
+
+    //////////////// handle version 4 lossless stereo data ////////////////////
+
+    else if (!(flags & HYBRID_FLAG) && !(flags & MONO_FLAG))
+	for (bptr = buffer, i = 0; i < sample_count; ++i) {
+	    long left, right, left2, right2;
+
+	    if ((left = get_word_lossless (wps, 0)) == WORD_EOF ||
+		(right = get_word_lossless (wps, 1)) == WORD_EOF)
+		    break;
+
+	    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
+		if (dpp->term > 0) {
+		    long sam_A, sam_B;
+		    int k;
+
+		    if (dpp->term > MAX_TERM) {
+			if (dpp->term & 1) {
+			    sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+			    sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
+			}
+			else {
+			    sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+			    sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
+			}
+
+			dpp->samples_A [1] = dpp->samples_A [0];
+			dpp->samples_B [1] = dpp->samples_B [0];
+			k = 0;
+		    }
+		    else {
+			sam_A = dpp->samples_A [m];
+			sam_B = dpp->samples_B [m];
+			k = (m + dpp->term) & (MAX_TERM - 1);
+		    }
+
+		    left2 = apply_weight (dpp->weight_A, sam_A) + left;
+		    right2 = apply_weight (dpp->weight_B, sam_B) + right;
+
+		    update_weight (dpp->weight_A, dpp->delta, sam_A, left);
+		    update_weight (dpp->weight_B, dpp->delta, sam_B, right);
+
+		    dpp->samples_A [k] = left = left2;
+		    dpp->samples_B [k] = right = right2;
+		}
+		else if (dpp->term == -1) {
+		    left2 = left + apply_weight (dpp->weight_A, dpp->samples_A [0]);
+		    update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], left);
+		    left = left2;
+		    right2 = right + apply_weight (dpp->weight_B, left2);
+		    update_weight_clip (dpp->weight_B, dpp->delta, left2, right);
+		    dpp->samples_A [0] = right = right2;
+		}
+		else {
+		    right2 = right + apply_weight (dpp->weight_B, dpp->samples_B [0]);
+		    update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], right);
+		    right = right2;
+
+		    if (dpp->term == -3) {
+			right2 = dpp->samples_A [0];
+			dpp->samples_A [0] = right;
+		    }
+
+		    left2 = left + apply_weight (dpp->weight_A, right2);
+		    update_weight_clip (dpp->weight_A, dpp->delta, right2, left);
+		    dpp->samples_B [0] = left = left2;
+		}
+
+	    m = (m + 1) & (MAX_TERM - 1);
+
+	    if (flags & JOINT_STEREO)
+		left += (right -= (left >> 1));
+
+	    if (labs (left) > mute_limit || labs (right) > mute_limit)
+		break;
+
+	    crc = (crc * 3 + left) * 3 + right;
+	    *bptr++ = left;
+	    *bptr++ = right;
+	}
+
+    //////////////// handle version 4 lossy/hybrid mono data //////////////////
+
+    else if ((flags & HYBRID_FLAG) && (flags & MONO_FLAG))
+	for (bptr = buffer, i = 0; i < sample_count; ++i) {
+
+	    if ((read_word = get_word (wps, 0, correction)) == WORD_EOF)
+		break;
+
+	    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
+		long sam, temp;
+		int k;
+
+		if (dpp->term > MAX_TERM) {
+		    if (dpp->term & 1)
+			sam = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+		    else
+			sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+
+		    dpp->samples_A [1] = dpp->samples_A [0];
+		    k = 0;
+		}
+		else {
+		    sam = dpp->samples_A [m];
+		    k = (m + dpp->term) & (MAX_TERM - 1);
+		}
+
+		temp = apply_weight (dpp->weight_A, sam) + read_word;
+		update_weight (dpp->weight_A, dpp->delta, sam, read_word);
+		dpp->samples_A [k] = read_word = temp;
+	    }
+
+	    m = (m + 1) & (MAX_TERM - 1);
+
+	    if (wpc->wvc_flag) {
+		if (flags & HYBRID_SHAPE) {
+		    int shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
+		    long temp = -apply_weight (shaping_weight, wps->dc.error [0]);
+
+		    if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) {
+			if (temp == wps->dc.error [0])
+			    temp = (temp < 0) ? temp + 1 : temp - 1;
+
+			wps->dc.error [0] = temp - correction [0];
+		    }
+		    else
+			wps->dc.error [0] = -correction [0];
+
+		    read_word += correction [0] - temp;
+		}
+		else
+		    read_word += correction [0];
+	    }
+
+	    crc = crc * 3 + read_word;
+
+#ifdef LOSSY_MUTE
+	    if (labs (read_word) > mute_limit)
+		break;
+#endif
+	    *bptr++ = read_word;
+	}
+
+    //////////////// handle version 4 lossy/hybrid stereo data ////////////////
+
+    else if ((flags & HYBRID_FLAG) && !(flags & MONO_FLAG))
+	for (bptr = buffer, i = 0; i < sample_count; ++i) {
+	    long left, right, left2, right2;
+	    long left_c = 0, right_c = 0;
+
+	    if ((left = get_word (wps, 0, correction)) == WORD_EOF ||
+		(right = get_word (wps, 1, correction + 1)) == WORD_EOF)
+		    break;
+
+	    if (wpc->wvc_flag && (flags & CROSS_DECORR)) {
+		left_c = left + correction [0];
+		right_c = right + correction [1];
+
+		for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
+		    long sam_A, sam_B;
+
+		    if (dpp->term > 0) {
+			if (dpp->term > MAX_TERM) {
+			    if (dpp->term & 1) {
+				sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+				sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
+			    }
+			    else {
+				sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+				sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
+			    }
+			}
+			else {
+			    sam_A = dpp->samples_A [m];
+			    sam_B = dpp->samples_B [m];
+			}
+
+			left_c += apply_weight (dpp->weight_A, sam_A);
+			right_c += apply_weight (dpp->weight_B, sam_B);
+		    }
+		    else if (dpp->term == -1) {
+			left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]);
+			right_c += apply_weight (dpp->weight_B, left_c);
+		    }
+		    else {
+			right_c += apply_weight (dpp->weight_B, dpp->samples_B [0]);
+
+			if (dpp->term == -3)
+			    left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]);
+			else
+			    left_c += apply_weight (dpp->weight_A, right_c);
+		    }
+		}
+
+		if (flags & JOINT_STEREO)
+		    left_c += (right_c -= (left_c >> 1));
+	    }
+
+	    for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
+		long sam_A, sam_B;
+
+		if (dpp->term > 0) {
+		    int k;
+
+		    if (dpp->term > MAX_TERM) {
+			if (dpp->term & 1) {
+			    sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
+			    sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
+			}
+			else {
+			    sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
+			    sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
+			}
+
+			dpp->samples_A [1] = dpp->samples_A [0];
+			dpp->samples_B [1] = dpp->samples_B [0];
+			k = 0;
+		    }
+		    else {
+			sam_A = dpp->samples_A [m];
+			sam_B = dpp->samples_B [m];
+			k = (m + dpp->term) & (MAX_TERM - 1);
+		    }
+
+		    left2 = apply_weight (dpp->weight_A, sam_A) + left;
+		    right2 = apply_weight (dpp->weight_B, sam_B) + right;
+
+		    update_weight (dpp->weight_A, dpp->delta, sam_A, left);
+		    update_weight (dpp->weight_B, dpp->delta, sam_B, right);
+
+		    dpp->samples_A [k] = left = left2;
+		    dpp->samples_B [k] = right = right2;
+		}
+		else if (dpp->term == -1) {
+		    left2 = left + apply_weight (dpp->weight_A, dpp->samples_A [0]);
+		    update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], left);
+		    left = left2;
+		    right2 = right + apply_weight (dpp->weight_B, left2);
+		    update_weight_clip (dpp->weight_B, dpp->delta, left2, right);
+		    dpp->samples_A [0] = right = right2;
+		}
+		else {
+		    right2 = right + apply_weight (dpp->weight_B, dpp->samples_B [0]);
+		    update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], right);
+		    right = right2;
+
+		    if (dpp->term == -3) {
+			right2 = dpp->samples_A [0];
+			dpp->samples_A [0] = right;
+		    }
+
+		    left2 = left + apply_weight (dpp->weight_A, right2);
+		    update_weight_clip (dpp->weight_A, dpp->delta, right2, left);
+		    dpp->samples_B [0] = left = left2;
+		}
+	    }
+
+	    m = (m + 1) & (MAX_TERM - 1);
+
+	    if (wpc->wvc_flag && !(flags & CROSS_DECORR)) {
+		left_c = left + correction [0];
+		right_c = right + correction [1];
+
+		if (flags & JOINT_STEREO)
+		    left_c += (right_c -= (left_c >> 1));
+	    }
+
+	    if (flags & JOINT_STEREO)
+		left += (right -= (left >> 1));
+
+	    if (wpc->wvc_flag) {
+		if (flags & HYBRID_SHAPE) {
+		    int shaping_weight;
+		    long temp;
+	
+		    correction [0] = left_c - left;
+		    shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
+		    temp = -apply_weight (shaping_weight, wps->dc.error [0]);
+
+		    if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) {
+			if (temp == wps->dc.error [0])
+			    temp = (temp < 0) ? temp + 1 : temp - 1;
+
+			wps->dc.error [0] = temp - correction [0];
+		    }
+		    else
+			wps->dc.error [0] = -correction [0];
+
+		    left = left_c - temp;
+		    correction [1] = right_c - right;
+		    shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16;
+		    temp = -apply_weight (shaping_weight, wps->dc.error [1]);
+
+		    if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) {
+			if (temp == wps->dc.error [1])
+			    temp = (temp < 0) ? temp + 1 : temp - 1;
+
+			wps->dc.error [1] = temp - correction [1];
+		    }
+		    else
+			wps->dc.error [1] = -correction [1];
+
+		    right = right_c - temp;
+		}
+		else {
+		    left = left_c;
+		    right = right_c;
+		}
+	    }
+#ifdef LOSSY_MUTE
+	    if (labs (left) > mute_limit || labs (right) > mute_limit)
+		break;
+#endif
+	    crc = (crc * 3 + left) * 3 + right;
+	    *bptr++ = left;
+	    *bptr++ = right;
+	}
+
+    if (i != sample_count) {
+	memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8));
+	wps->mute_error = TRUE;
+	i = sample_count;
+
+	if (bs_is_open (&wps->wvxbits))
+	    bs_close_read (&wps->wvxbits);
+    }
+
+    if (m)
+	for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
+	    if (dpp->term > 0 && dpp->term <= MAX_TERM) {
+		long temp_A [MAX_TERM], temp_B [MAX_TERM];
+		int k;
+
+		memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A));
+		memcpy (temp_B, dpp->samples_B, sizeof (dpp->samples_B));
+
+		for (k = 0; k < MAX_TERM; k++) {
+		    dpp->samples_A [k] = temp_A [m];
+		    dpp->samples_B [k] = temp_B [m];
+		    m = (m + 1) & (MAX_TERM - 1);
+		}
+	    }
+
+    fixup_samples (wpc, buffer, i);
+
+    if ((flags & FLOAT_DATA) && (wpc->open_flags & OPEN_NORMALIZE))
+	float_normalize (buffer, (flags & MONO_FLAG) ? i : i * 2,
+	    127 - wps->float_norm_exp + wpc->norm_offset);
+
+    wps->sample_index += i;
+    wps->crc = crc;
+
+    return i;
+}
+
+// This is a helper function for unpack_samples() that applies several final
+// operations. First, if the data is 32-bit float data, then that conversion
+// is done in the float.c module (whether lossy or lossless) and we return.
+// Otherwise, if the extended integer data applies, then that operation is
+// executed first. If the unpacked data is lossy (and not corrected) then
+// it is clipped and shifted in a single operation. Otherwise, if it's
+// lossless then the last step is to apply the final shift (if any).
+
+static void fixup_samples (WavpackContext *wpc, long *buffer, ulong sample_count)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    ulong flags = wps->wphdr.flags;
+    int lossy_flag = (flags & HYBRID_FLAG) && !wpc->wvc_flag;
+    int shift = (flags & SHIFT_MASK) >> SHIFT_LSB;
+
+    if (flags & FLOAT_DATA) {
+	float_values (wps, buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2);
+	return;
+    }
+
+    if (flags & INT32_DATA) {
+	ulong count = (flags & MONO_FLAG) ? sample_count : sample_count * 2;
+	int sent_bits = wps->int32_sent_bits, zeros = wps->int32_zeros;
+	int ones = wps->int32_ones, dups = wps->int32_dups;
+	ulong data, mask = (1 << sent_bits) - 1;
+	long *dptr = buffer;
+
+	if (bs_is_open (&wps->wvxbits)) {
+	    ulong crc = wps->crc_x;
+
+	    while (count--) {
+//		if (sent_bits) {
+		    getbits (&data, sent_bits, &wps->wvxbits);
+		    *dptr = (*dptr << sent_bits) | (data & mask);
+//		}
+
+		if (zeros)
+		    *dptr <<= zeros;
+		else if (ones)
+		    *dptr = ((*dptr + 1) << ones) - 1;
+		else if (dups)
+		    *dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1);
+
+		crc = crc * 9 + (*dptr & 0xffff) * 3 + ((*dptr >> 16) & 0xffff);
+		dptr++;
+	    }
+
+	    wps->crc_x = crc;
+	}
+	else if (!sent_bits && (zeros + ones + dups)) {
+	    while (lossy_flag && (flags & BYTES_STORED) == 3 && shift < 8) {
+		if (zeros)
+		    zeros--;
+		else if (ones)
+		    ones--;
+		else if (dups)
+		    dups--;
+		else
+		    break;
+
+		shift++;
+	    }
+
+	    while (count--) {
+		if (zeros)
+		    *dptr <<= zeros;
+		else if (ones)
+		    *dptr = ((*dptr + 1) << ones) - 1;
+		else if (dups)
+		    *dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1);
+
+		dptr++;
+	    }
+	}
+	else
+	    shift += zeros + sent_bits + ones + dups;
+    }
+
+    if (lossy_flag) {
+	long min_value = 0, max_value = 0, min_shifted = 0, max_shifted = 0;
+
+	switch (flags & BYTES_STORED) {
+	    case 0:
+		min_shifted = (min_value = -128 >> shift) << shift;
+		max_shifted = (max_value = 127 >> shift) << shift;
+		break;
+
+	    case 1:
+		min_shifted = (min_value = -32768 >> shift) << shift;
+		max_shifted = (max_value = 32767 >> shift) << shift;
+		break;
+
+	    case 2:
+		min_shifted = (min_value = -8388608 >> shift) << shift;
+		max_shifted = (max_value = 8388607 >> shift) << shift;
+		break;
+
+	    case 3:
+		min_value = (-2147483647-1) >> shift;
+		max_value = (long) 2147483647 >> shift;
+		min_shifted = min_value << shift;
+		max_shifted = max_value << shift;
+		break;
+	}
+
+	if (!(flags & MONO_FLAG))
+	    sample_count *= 2;
+
+	while (sample_count--) {
+	    if (*buffer < min_value)
+		*buffer++ = min_shifted;
+	    else if (*buffer > max_value)
+		*buffer++ = max_shifted;
+	    else
+		*buffer++ <<= shift;
+	}
+    }
+    else if (shift) {
+	if (!(flags & MONO_FLAG))
+	    sample_count *= 2;
+
+	while (sample_count--)
+	    *buffer++ <<= shift;
+    }
+}
+
+// This function checks the crc value(s) for an unpacked block, returning the
+// number of actual crc errors detected for the block. The block must be
+// completely unpacked before this test is valid. For losslessly unpacked
+// blocks of float or extended integer data the extended crc is also checked.
+// Note that WavPack's crc is not a CCITT approved polynomial algorithm, but
+// is a much simpler method that is virtually as robust for real world data.
+
+int check_crc_error (WavpackContext *wpc)
+{
+    int result = 0, stream;
+
+    for (stream = 0; stream < wpc->num_streams; stream++) {
+	WavpackStream *wps = wpc->streams [stream];
+
+	if (wps->crc != wps->wphdr.crc)
+	    ++result;
+	else if (bs_is_open (&wps->wvxbits) && wps->crc_x != wps->crc_wvx)
+	    ++result;
+    }
+
+    return result;
+}
+
+#endif
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/wavpack.h	2005-03-30 11:42:50.000000000 +0100
@@ -0,0 +1,604 @@
+////////////////////////////////////////////////////////////////////////////
+//                           **** WAVPACK ****                            //
+//                  Hybrid Lossless Wavefile Compressor                   //
+//              Copyright (c) 1998 - 2004 Conifer Software.               //
+//                          All Rights Reserved.                          //
+//      Distributed under the BSD Software License (see license.txt)      //
+////////////////////////////////////////////////////////////////////////////
+
+// wavpack.h
+
+#ifndef WAVPACK_H
+#define WAVPACK_H
+
+#if defined(WIN32)
+#define FASTCALL __fastcall
+#else
+#define FASTCALL
+#define SetConsoleTitle(x)
+#endif
+
+#include <sys/types.h>
+
+// This header file contains all the definitions required by WavPack.
+
+typedef unsigned char	uchar;
+#if !defined(__GNUC__) || defined(WIN32)
+typedef unsigned short	ushort;
+typedef unsigned long	ulong;
+typedef unsigned int	uint;
+#elif defined(__APPLE__)
+typedef unsigned long	ulong;
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+
+// This structure is used to access the individual fields of 32-bit ieee
+// floating point numbers. This will not be compatible with compilers that
+// allocate bit fields from the most significant bits, although I'm not sure
+// how common that is.
+
+typedef struct {
+    unsigned mantissa : 23;
+    unsigned exponent : 8;
+    unsigned sign : 1;
+} f32;
+
+#include <stdio.h>
+
+#define FALSE 0
+#define TRUE 1
+
+#if defined(WIN32)
+#undef VERSION_OS
+#define VERSION_OS "Win32"
+#endif
+#define VERSION_STR "4.1 "
+#define DATE_STR "14-9-04"
+
+// ID3v1 and APEv2 TAG formats (may occur at the end of WavPack files)
+
+typedef struct {
+    uchar tag_id [3], title [30], artist [30], album [30];
+    uchar year [4], comment [30], genre;
+} ID3_Tag;
+
+typedef struct {
+    char ID [8];
+    long version, length, item_count, flags;
+    char res [8];
+} APE_Tag_Hdr;
+
+#define APE_Tag_Hdr_Format "8LLLL"
+
+typedef struct {
+    ID3_Tag id3_tag;
+    APE_Tag_Hdr ape_tag_hdr;
+    char *ape_tag_data;
+} M_Tag;
+
+// RIFF / wav header formats (these occur at the beginning of both wav files
+// and pre-4.0 WavPack files that are not in the "raw" mode)
+
+typedef struct {
+    char ckID [4];
+    long ckSize;
+    char formType [4];
+} RiffChunkHeader;
+
+typedef struct {
+    char ckID [4];
+    long ckSize;
+} ChunkHeader;
+
+#define ChunkHeaderFormat "4L"
+
+typedef struct {
+    ushort FormatTag, NumChannels;
+    ulong SampleRate, BytesPerSecond;
+    ushort BlockAlign, BitsPerSample;
+    ushort cbSize, ValidBitsPerSample;
+    long ChannelMask;
+    ushort SubFormat;
+    char GUID [14];
+} WaveHeader;
+
+#define WaveHeaderFormat "SSLLSSSSLS"
+
+////////////////////////////// WavPack Header /////////////////////////////////
+
+// 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
+
+//////////////////////////// WavPack Metadata /////////////////////////////////
+
+// This is an internal representation of metadata.
+
+typedef struct {
+    long byte_length;
+    void *data;
+    uchar id;
+} WavpackMetadata;
+
+#define ID_OPTIONAL_DATA	0x20
+#define ID_ODD_SIZE		0x40
+#define ID_LARGE		0x80
+
+#define ID_DUMMY		0x0
+#define ID_ENCODER_INFO		0x1
+#define ID_DECORR_TERMS		0x2
+#define ID_DECORR_WEIGHTS	0x3
+#define ID_DECORR_SAMPLES	0x4
+#define ID_ENTROPY_VARS		0x5
+#define ID_HYBRID_PROFILE	0x6
+#define ID_SHAPING_WEIGHTS	0x7
+#define ID_FLOAT_INFO		0x8
+#define ID_INT32_INFO		0x9
+#define ID_WV_BITSTREAM		0xa
+#define ID_WVC_BITSTREAM	0xb
+#define ID_WVX_BITSTREAM	0xc
+#define ID_CHANNEL_INFO		0xd
+
+#define ID_RIFF_HEADER		(ID_OPTIONAL_DATA | 0x1)
+#define ID_RIFF_TRAILER		(ID_OPTIONAL_DATA | 0x2)
+#define ID_REPLAY_GAIN		(ID_OPTIONAL_DATA | 0x3)
+#define ID_CUESHEET		(ID_OPTIONAL_DATA | 0x4)
+#define ID_CONFIG_BLOCK		(ID_OPTIONAL_DATA | 0x5)
+#define ID_MD5_CHECKSUM		(ID_OPTIONAL_DATA | 0x6)
+
+///////////////////////// WavPack Configuration ///////////////////////////////
+
+// This internal structure is used during encode to provide configuration to
+// the encoding engine and during decoding to provide fle information back to
+// the higher level functions. Not all fields are used in both modes.
+
+typedef struct {
+    float bitrate, shaping_weight;
+    int bits_per_sample, bytes_per_sample;
+    int qmode, flags, xmode, num_channels, float_norm_exp;
+    long block_samples, extra_flags, sample_rate, channel_mask;
+    uchar md5_checksum [16], md5_read;
+} WavpackConfig;
+
+#define CONFIG_BYTES_STORED	3	// 1-4 bytes/sample
+#define CONFIG_MONO_FLAG	4	// not stereo
+#define CONFIG_HYBRID_FLAG	8	// hybrid mode
+#define CONFIG_JOINT_STEREO	0x10	// joint stereo
+#define CONFIG_CROSS_DECORR	0x20	// no-delay cross decorrelation
+#define CONFIG_HYBRID_SHAPE	0x40	// noise shape (hybrid mode only)
+#define CONFIG_FLOAT_DATA	0x80	// ieee 32-bit floating point data
+
+#define CONFIG_ADOBE_MODE	0x100	// "adobe" mode for 32-bit floats
+#define CONFIG_FAST_FLAG	0x200	// fast mode
+#define CONFIG_VERY_FAST_FLAG	0x400	// double fast
+#define CONFIG_HIGH_FLAG	0x800	// high quality mode
+#define CONFIG_VERY_HIGH_FLAG	0x1000	// double high (not used yet)
+#define CONFIG_BITRATE_KBPS	0x2000	// bitrate is kbps, not bits / sample
+#define CONFIG_AUTO_SHAPING	0x4000	// automatic noise shaping
+#define CONFIG_SHAPE_OVERRIDE	0x8000	// shaping mode specified
+#define CONFIG_JOINT_OVERRIDE	0x10000	// joint-stereo mode specified
+#define CONFIG_COPY_TIME	0x20000	// copy file-time from source
+#define CONFIG_CREATE_EXE	0x40000	// create executable
+#define CONFIG_CREATE_WVC	0x80000	// create correction file
+#define CONFIG_OPTIMIZE_WVC	0x100000 // maximize bybrid compression
+#define CONFIG_QUALITY_MODE	0x200000 // psychoacoustic quality mode
+#define CONFIG_RAW_FLAG		0x400000 // raw mode (not implemented yet)
+#define CONFIG_CALC_NOISE	0x800000 // calc noise in hybrid mode
+#define CONFIG_LOSSY_MODE	0x1000000 // obsolete (for information)
+#define CONFIG_EXTRA_MODE	0x2000000 // extra processing mode
+#define CONFIG_SKIP_WVX		0x4000000 // no wvx stream w/ floats & big ints
+#define CONFIG_MD5_CHECKSUM	0x8000000 // compute & store MD5 signature
+#define CONFIG_QUIET_MODE	0x10000000 // don't report progress %
+
+#define EXTRA_SCAN_ONLY		1
+#define EXTRA_STEREO_MODES	2
+//#define EXTRA_CHECK_TERMS	4
+#define EXTRA_TRY_DELTAS	8
+#define EXTRA_ADJUST_DELTAS	16
+#define EXTRA_SORT_FIRST	32
+#define EXTRA_BRANCHES		0x1c0
+#define EXTRA_SKIP_8TO16	512
+#define EXTRA_TERMS		0x3c00
+#define EXTRA_DUMP_TERMS	16384
+#define EXTRA_SORT_LAST		32768
+
+//////////////////////////////// WavPack Stream ///////////////////////////////
+
+// This internal structure contains everything required to handle a WavPack
+// "stream", which is defined as a stereo or mono stream of audio samples. For
+// multichannel audio several of these would be required. Each stream contains
+// pointers to hold a complete allocated block of WavPack data, although it's
+// possible to decode WavPack blocks without buffering an entire block.
+
+typedef struct bs {
+    uchar *buf, *end, *ptr;
+    void (*wrap)(struct bs *bs);
+    int error, bc;
+    ulong sr;
+} Bitstream;
+
+#define MAX_STREAMS 8
+#define MAX_NTERMS 16
+#define MAX_TERM 8
+
+struct decorr_pass {
+    int term, delta, weight_A, weight_B;
+    long samples_A [MAX_TERM], samples_B [MAX_TERM];
+    long aweight_A, aweight_B;
+#ifdef PACK
+    long sum_A, sum_B, min, max;
+#endif
+};
+
+typedef struct {
+    WavpackHeader wphdr;
+
+    uchar *blockbuff, *blockend;
+    uchar *block2buff, *block2end;
+    long *sample_buffer;
+
+    ulong sample_index, crc, crc_x, crc_wvx;
+    Bitstream wvbits, wvcbits, wvxbits;
+    int bits, num_terms, mute_error;
+    float delta_decay;
+
+    uchar int32_sent_bits, int32_zeros, int32_ones, int32_dups;
+    uchar float_flags, float_shift, float_max_exp, float_norm_exp;
+ 
+    struct {
+	long shaping_acc [2], shaping_delta [2], error [2];
+	double noise_sum, noise_ave, noise_max;
+    } dc;
+
+    struct decorr_pass decorr_passes [MAX_NTERMS];
+
+    struct {
+	ulong bitrate_delta [2], bitrate_acc [2];
+	ulong median [3] [2], slow_level [2], error_limit [2];
+	ulong pend_data, holding_one, zeros_acc;
+	int holding_zero, pend_count;
+    } w;
+} WavpackStream;
+
+// flags for float_flags:
+
+#define FLOAT_SHIFT_ONES 1	// bits left-shifted into float = '1'
+#define FLOAT_SHIFT_SAME 2	// bits left-shifted into float are the same
+#define FLOAT_SHIFT_SENT 4	// bits shifted into float are sent literally
+#define FLOAT_ZEROS_SENT 8	// "zeros" are not all real zeros
+#define FLOAT_NEG_ZEROS  0x10	// contains negative zeros
+#define FLOAT_EXCEPTIONS 0x20	// contains exceptions (inf, nan, etc.)
+
+/////////////////////////////// WavPack Context ///////////////////////////////
+
+// This internal structure holds everything required to encode or decode WavPack
+// files. It is recommended that direct access to this structure be minimized
+// and the provided utilities used instead.
+
+typedef struct {
+    long (*read_bytes)(void *id, void *data, long bcount);
+    ulong (*get_pos)(void *id);
+    int (*set_pos_abs)(void *id, ulong pos);
+    int (*set_pos_rel)(void *id, long delta, int mode);
+    int (*push_back_byte)(void *id, int c);
+    ulong (*get_length)(void *id);
+    int (*can_seek)(void *id);
+} stream_reader;
+
+typedef int (*blockout)(void *id, void *data, long bcount);
+
+typedef struct {
+    WavpackConfig config;
+
+    WavpackMetadata *metadata;
+    ulong metabytes;
+    int metacount;
+
+    uchar *wrapper_data;
+    ulong wrapper_bytes;
+
+    blockout blockout;
+    void *wv_out, *wvc_out;
+
+    stream_reader *reader;
+    void *wv_in, *wvc_in;
+
+    ulong filelen, file2len, filepos, file2pos, total_samples, crc_errors, first_flags;
+    int wvc_flag, open_flags, norm_offset, reduced_channels, lossy_blocks, close_files;
+    int block_samples, max_samples, acc_samples;
+    M_Tag m_tag;
+
+    int current_stream, num_streams;
+    WavpackStream *streams [8];
+    void *stream3;
+
+    char error_message [80];
+} WavpackContext;
+
+//////////////////////// function prototypes and macros //////////////////////
+
+#define CLEAR(destin) memset (&destin, 0, sizeof (destin));
+
+// these macros implement the weight application and update operations
+// that are at the heart of the decorrelation loops
+
+#define apply_weight_i(weight, sample) ((weight * sample + 512) >> 10)
+
+#define apply_weight_f(weight, sample) (((((sample & 0xffff) * weight) >> 9) + \
+    (((sample & ~0xffff) >> 9) * weight) + 1) >> 1)
+
+#define apply_weight(weight, sample) (sample != (short) sample ? \
+    apply_weight_f (weight, sample) : apply_weight_i (weight, sample))
+
+#define update_weight(weight, delta, source, result) \
+    if (source && result) weight -= ((((source ^ result) >> 30) & 2) - 1) * delta;
+
+#define update_weight_clip(weight, delta, source, result) \
+    if (source && result && ((source ^ result) < 0 ? (weight -= delta) < -1024 : (weight += delta) > 1024)) \
+	weight = weight < 0 ? -1024 : 1024;
+
+// bits.c
+
+void bs_open_read (Bitstream *bs, uchar *buffer_start, uchar *buffer_end);
+void bs_open_write (Bitstream *bs, uchar *buffer_start, uchar *buffer_end);
+ulong bs_close_read (Bitstream *bs);
+ulong bs_close_write (Bitstream *bs);
+
+int DoReadFile (FILE *hFile, void *lpBuffer, ulong nNumberOfBytesToRead, ulong *lpNumberOfBytesRead);
+int DoWriteFile (FILE *hFile, void *lpBuffer, ulong nNumberOfBytesToWrite, ulong *lpNumberOfBytesWritten);
+ulong DoGetFileSize (FILE *hFile), DoGetFilePosition (FILE *hFile);
+int DoSetFilePositionRelative (FILE *hFile, long pos, int mode);
+int DoSetFilePositionAbsolute (FILE *hFile, ulong pos);
+int DoUngetc (int c, FILE *hFile), DoDeleteFile (char *filename);
+int DoCloseHandle (FILE *hFile), DoTruncateFile (FILE *hFile);
+
+#define bs_is_open(bs) ((bs)->ptr != NULL)
+
+#define getbit(bs) ( \
+    (((bs)->bc) ? \
+	((bs)->bc--, (bs)->sr & 1) : \
+	    (((++((bs)->ptr) != (bs)->end) ? (void) 0 : (bs)->wrap (bs)), (bs)->bc = 7, ((bs)->sr = *((bs)->ptr)) & 1) \
+    ) ? \
+	((bs)->sr >>= 1, 1) : \
+	((bs)->sr >>= 1, 0) \
+)
+
+#define getbits(value, nbits, bs) { \
+    while ((nbits) > (bs)->bc) { \
+	if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
+	(bs)->sr |= (long)*((bs)->ptr) << (bs)->bc; \
+	(bs)->bc += 8; \
+    } \
+    *(value) = (bs)->sr; \
+    (bs)->sr >>= (nbits); \
+    (bs)->bc -= (nbits); \
+}
+
+#define putbit(bit, bs) { if (bit) (bs)->sr |= (1 << (bs)->bc); \
+    if (++((bs)->bc) == 8) { \
+	*((bs)->ptr) = (bs)->sr; \
+	(bs)->sr = (bs)->bc = 0; \
+	if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
+    }}
+
+#define putbit_0(bs) { \
+    if (++((bs)->bc) == 8) { \
+	*((bs)->ptr) = (bs)->sr; \
+	(bs)->sr = (bs)->bc = 0; \
+	if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
+    }}
+
+#define putbit_1(bs) { (bs)->sr |= (1 << (bs)->bc); \
+    if (++((bs)->bc) == 8) { \
+	*((bs)->ptr) = (bs)->sr; \
+	(bs)->sr = (bs)->bc = 0; \
+	if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
+    }}
+
+#define putbits(value, nbits, bs) { \
+    (bs)->sr |= (long)(value) << (bs)->bc; \
+    if (((bs)->bc += (nbits)) >= 8) \
+	do { \
+	    *((bs)->ptr) = (bs)->sr; \
+	    (bs)->sr >>= 8; \
+	    if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
+	} while (((bs)->bc -= 8) >= 8); \
+}
+
+void little_endian_to_native (void *data, char *format);
+void native_to_little_endian (void *data, char *format);
+
+// pack.c
+
+void pack_init (WavpackContext *wpc);
+int pack_block (WavpackContext *wpc, long *buffer);
+double pack_noise (WavpackContext *wpc, double *peak);
+
+// unpack.c
+
+int unpack_init (WavpackContext *wpc);
+int init_wv_bitstream (WavpackStream *wps, WavpackMetadata *wpmd);
+int init_wvc_bitstream (WavpackStream *wps, WavpackMetadata *wpmd);
+int init_wvx_bitstream (WavpackStream *wps, WavpackMetadata *wpmd);
+int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd);
+int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd);
+int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd);
+int read_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd);
+int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd);
+int read_int32_info (WavpackStream *wps, WavpackMetadata *wpmd);
+int read_channel_info (WavpackContext *wpc, WavpackMetadata *wpmd);
+int read_config_info (WavpackContext *wpc, WavpackMetadata *wpmd);
+int read_wrapper_data (WavpackContext *wpc, WavpackMetadata *wpmd);
+long unpack_samples (WavpackContext *wpc, long *buffer, ulong sample_count);
+int check_crc_error (WavpackContext *wpc);
+
+// unpack3.c
+
+WavpackContext *open_file3 (WavpackContext *wpc, char *error);
+long unpack_samples3 (WavpackContext *wpc, long *buffer, ulong sample_count);
+int seek_sample3 (WavpackContext *wpc, ulong desired_index);
+ulong get_sample_index3 (WavpackContext *wpc);
+void free_stream3 (WavpackContext *wpc);
+int get_version3 (WavpackContext *wpc);
+
+// utils.c
+
+int copy_timestamp (const char *src_filename, const char *dst_filename);
+char *filespec_ext (char *filespec), *filespec_path (char *filespec);
+char *filespec_name (char *filespec), *filespec_wild (char *filespec);
+void error_line (char *error, ...), finish_line (void);
+void setup_break (void);
+int check_break (void);
+char yna (void);
+
+#define FN_FIT(fn) ((strlen (fn) > 30) ? filespec_name (fn) : fn)
+
+// metadata.c stuff
+
+int read_metadata_buff (WavpackMetadata *wpmd, uchar *blockbuff, uchar **buffptr);
+int write_metadata_block (WavpackContext *wpc);
+int copy_metadata (WavpackMetadata *wpmd, uchar *buffer_start, uchar *buffer_end);
+int add_to_metadata (WavpackContext *wpc, void *data, ulong bcount, uchar id);
+int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd);
+void free_metadata (WavpackMetadata *wpmd);
+
+// words.c stuff
+
+void init_words (WavpackStream *wps);
+void word_set_bitrate (WavpackStream *wps);
+void write_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd);
+void write_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd);
+int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd);
+int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd);
+long FASTCALL send_word (WavpackStream *wps, long value, int chan);
+void FASTCALL send_word_lossless (WavpackStream *wps, long value, int chan);
+long FASTCALL get_word (WavpackStream *wps, int chan, long *correction);
+long FASTCALL get_word_lossless (WavpackStream *wps, int chan);
+void flush_word (WavpackStream *wps);
+long nosend_word (WavpackStream *wps, long value, int chan);
+void scan_word (WavpackStream *wps, long *samples, ulong num_samples, int dir);
+
+int log2s (long value);
+long exp2s (int log);
+ulong log2buffer (long *samples, ulong num_samples);
+
+char store_weight (int weight);
+int restore_weight (char weight);
+
+#define WORD_EOF (1L << 31)
+
+// float.c
+
+void write_float_info (WavpackStream *wps, WavpackMetadata *wpmd);
+int scan_float_data (WavpackStream *wps, f32 *values, long num_values);
+void send_float_data (WavpackStream *wps, f32 *values, long num_values);
+int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd);
+void float_values (WavpackStream *wps, long *values, long num_values);
+void float_normalize (long *values, long num_values, int delta_exp);
+
+// analyze?.c
+
+void analyze_stereo (WavpackContext *wpc, long *samples);
+void analyze_mono (WavpackContext *wpc, long *samples);
+
+// wputils.c
+
+WavpackContext *WavpackOpenFileInputEx (stream_reader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset);
+WavpackContext *WavpackOpenFileInput (const char *infilename, char *error, int flags, int norm_offset);
+
+#define OPEN_WVC	0x1	// open/read "correction" file
+#define OPEN_TAGS	0x2	// read ID3v1 / APEv2 tags (seekable file)
+#define OPEN_WRAPPER	0x4	// make audio wrapper available (i.e. RIFF)
+#define OPEN_2CH_MAX	0x8	// open multichannel as stereo (no downmix)
+#define OPEN_NORMALIZE	0x10	// normalize floating point data to +/- 1.0
+
+int WavpackGetMode (WavpackContext *wpc);
+
+#define MODE_WVC	0x1
+#define MODE_LOSSLESS	0x2
+#define MODE_HYBRID	0x4
+#define MODE_FLOAT	0x8
+#define MODE_VALID_TAG	0x10
+#define MODE_HIGH	0x20
+#define MODE_FAST	0x40
+#define MODE_EXTRA	0x80
+#define MODE_APETAG	0x100
+#define MODE_SFX	0x200
+
+int WavpackGetVersion (WavpackContext *wpc);
+ulong WavpackUnpackSamples (WavpackContext *wpc, long *buffer, ulong samples);
+ulong WavpackGetNumSamples (WavpackContext *wpc);
+ulong WavpackGetSampleIndex (WavpackContext *wpc);
+int WavpackGetNumErrors (WavpackContext *wpc);
+int WavpackLossyBlocks (WavpackContext *wpc);
+int WavpackSeekSample (WavpackContext *wpc, ulong sample);
+WavpackContext *WavpackCloseFile (WavpackContext *wpc);
+ulong WavpackGetSampleRate (WavpackContext *wpc);
+int WavpackGetBitsPerSample (WavpackContext *wpc);
+int WavpackGetBytesPerSample (WavpackContext *wpc);
+int WavpackGetNumChannels (WavpackContext *wpc);
+int WavpackGetReducedChannels (WavpackContext *wpc);
+int WavpackGetMD5Sum (WavpackContext *wpc, uchar data [16]);
+ulong WavpackGetWrapperBytes (WavpackContext *wpc);
+uchar *WavpackGetWrapperData (WavpackContext *wpc);
+void WavpackFreeWrapper (WavpackContext *wpc);
+double WavpackGetProgress (WavpackContext *wpc);
+ulong WavpackGetFileSize (WavpackContext *wpc);
+double WavpackGetRatio (WavpackContext *wpc);
+double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc);
+double WavpackGetInstantBitrate (WavpackContext *wpc);
+int WavpackGetTagItem (WavpackContext *wpc, const char *item, char *value, int size);
+
+WavpackContext *WavpackOpenFileOutput (blockout blockout, void *wv_id, void *wvc_id);
+int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, ulong total_samples);
+int WavpackAddWrapper (WavpackContext *wpc, void *data, ulong bcount);
+int WavpackStoreMD5Sum (WavpackContext *wpc, uchar data [16]);
+int WavpackPackInit (WavpackContext *wpc);
+int WavpackPackSamples (WavpackContext *wpc, long *sample_buffer, ulong sample_count);
+int WavpackFlushSamples (WavpackContext *wpc);
+
+#endif
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/words.c	2005-03-30 11:34:11.000000000 +0100
@@ -0,0 +1,1422 @@
+////////////////////////////////////////////////////////////////////////////
+//                           **** WAVPACK ****                            //
+//                  Hybrid Lossless Wavefile Compressor                   //
+//              Copyright (c) 1998 - 2004 Conifer Software.               //
+//                          All Rights Reserved.                          //
+////////////////////////////////////////////////////////////////////////////
+
+// words.c
+
+// This module provides entropy word encoding and decoding functions using
+// a variation on the Rice method.  This was introduced in version 3.93
+// because it allows splitting the data into a "lossy" stream and a
+// "correction" stream in a very efficient manner and is therefore ideal
+// for the "hybrid" mode.  For 4.0, the efficiency of this method was
+// significantly improved by moving away from the normal Rice restriction of
+// using powers of two for the modulus divisions and now the method can be
+// used for both hybrid and pure lossless encoding.
+
+// Samples are divided by median probabilities at 5/7 (71.43%), 10/49 (20.41%),
+// and 20/343 (5.83%). Each zone has 3.5 times fewer samples than the
+// previous. Using standard Rice coding on this data would result in 1.4
+// bits per sample average (not counting sign bit). However, there is a
+// very simple encoding that is over 99% efficient with this data and
+// results in about 1.22 bits per sample.
+
+#include "wavpack.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef DEBUG_ALLOC
+#define malloc malloc_db
+#define realloc realloc_db
+#define free free_db
+void *malloc_db (ulong size);
+void *realloc_db (void *ptr, ulong size);
+void free_db (void *ptr);
+long dump_alloc (void);
+#endif
+
+// #define DEBUG_WORDS	// debug module by sending all 32 bits literally
+
+//////////////////////////////// local macros /////////////////////////////////
+
+#define LIMIT_ONES 16	// maximum consecutive 1s sent for "div" data
+
+// these control the time constant "slow_level" which is used for hybrid mode
+// that controls bitrate as a function of residual level (HYBRID_BITRATE).
+#define SLS 8
+#define SLO ((1 << (SLS - 1)))
+
+// these control the time constant of the 3 median level breakpoints
+#define DIV0 128	// 5/7 of samples
+#define DIV1 64		// 10/49 of samples
+#define DIV2 32		// 20/343 of samples
+
+// this macro retrieves the specified median breakpoint (without frac; min = 1)
+#define GET_MED(med) (((wps->w.median [med] [chan]) >> 4) + 1)
+
+// These macros update the specified median breakpoints. Note that the median
+// is incremented when the sample is higher than the median, else decremented.
+// They are designed so that the median will never drop below 1 and the value
+// is essentially stationary if there are 2 increments for every 5 decrements.
+
+#define INC_MED0() (wps->w.median [0] [chan] += ((wps->w.median [0] [chan] + DIV0) / DIV0) * 5)
+#define DEC_MED0() (wps->w.median [0] [chan] -= ((wps->w.median [0] [chan] + (DIV0-2)) / DIV0) * 2)
+#define INC_MED1() (wps->w.median [1] [chan] += ((wps->w.median [1] [chan] + DIV1) / DIV1) * 5)
+#define DEC_MED1() (wps->w.median [1] [chan] -= ((wps->w.median [1] [chan] + (DIV1-2)) / DIV1) * 2)
+#define INC_MED2() (wps->w.median [2] [chan] += ((wps->w.median [2] [chan] + DIV2) / DIV2) * 5)
+#define DEC_MED2() (wps->w.median [2] [chan] -= ((wps->w.median [2] [chan] + (DIV2-2)) / DIV2) * 2)
+
+#define count_bits(av) ( \
+ (av) < (1 << 8) ? nbits_table [av] : \
+  ( \
+   (av) < (1L << 16) ? nbits_table [(av) >> 8] + 8 : \
+   ((av) < (1L << 24) ? nbits_table [(av) >> 16] + 16 : nbits_table [(av) >> 24] + 24) \
+  ) \
+)
+
+///////////////////////////// local table storage ////////////////////////////
+
+const ulong bitset [] = {
+    1L << 0, 1L << 1, 1L << 2, 1L << 3,
+    1L << 4, 1L << 5, 1L << 6, 1L << 7,
+    1L << 8, 1L << 9, 1L << 10, 1L << 11,
+    1L << 12, 1L << 13, 1L << 14, 1L << 15,
+    1L << 16, 1L << 17, 1L << 18, 1L << 19,
+    1L << 20, 1L << 21, 1L << 22, 1L << 23,
+    1L << 24, 1L << 25, 1L << 26, 1L << 27,
+    1L << 28, 1L << 29, 1L << 30, 1L << 31
+};
+
+const ulong bitmask [] = {
+    (1L << 0) - 1, (1L << 1) - 1, (1L << 2) - 1, (1L << 3) - 1,
+    (1L << 4) - 1, (1L << 5) - 1, (1L << 6) - 1, (1L << 7) - 1,
+    (1L << 8) - 1, (1L << 9) - 1, (1L << 10) - 1, (1L << 11) - 1,
+    (1L << 12) - 1, (1L << 13) - 1, (1L << 14) - 1, (1L << 15) - 1,
+    (1L << 16) - 1, (1L << 17) - 1, (1L << 18) - 1, (1L << 19) - 1,
+    (1L << 20) - 1, (1L << 21) - 1, (1L << 22) - 1, (1L << 23) - 1,
+    (1L << 24) - 1, (1L << 25) - 1, (1L << 26) - 1, (1L << 27) - 1,
+    (1L << 28) - 1, (1L << 29) - 1, (1L << 30) - 1, 0x7fffffff
+};
+
+const char nbits_table [] = {
+    0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,	// 0 - 15
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,	// 16 - 31
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,	// 32 - 47
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,	// 48 - 63
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,	// 64 - 79
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,	// 80 - 95
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,	// 96 - 111
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,	// 112 - 127
+    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,	// 128 - 143
+    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,	// 144 - 159
+    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,	// 160 - 175
+    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,	// 176 - 191
+    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,	// 192 - 207
+    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,	// 208 - 223
+    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,	// 224 - 239
+    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8	// 240 - 255
+};
+
+static const uchar log2_table [] = {
+    0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
+    0x16, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a,
+    0x2c, 0x2d, 0x2e, 0x2f, 0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e,
+    0x3f, 0x41, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
+    0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
+    0x64, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, 0x75,
+    0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
+    0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+    0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
+    0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb2,
+    0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc0,
+    0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xce,
+    0xcf, 0xd0, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd8, 0xd9, 0xda, 0xdb,
+    0xdc, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe7, 0xe7,
+    0xe8, 0xe9, 0xea, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xee, 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf4,
+    0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff
+};
+
+static const uchar exp2_table [] = {
+    0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b,
+    0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x16,
+    0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23,
+    0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3d,
+    0x3e, 0x3f, 0x40, 0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
+    0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
+    0x5b, 0x5c, 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+    0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+    0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a,
+    0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+    0x9c, 0x9d, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad,
+    0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0,
+    0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4,
+    0xd6, 0xd7, 0xd8, 0xd9, 0xdb, 0xdc, 0xdd, 0xde, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9,
+    0xea, 0xec, 0xed, 0xee, 0xf0, 0xf1, 0xf2, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff
+};
+
+///////////////////////////// executable code ////////////////////////////////
+
+static int FASTCALL mylog2 (unsigned long avalue);
+
+// Initialize entropy encoder for the specified stream. In lossless mode there
+// are no parameters to select; in hybrid mode the bitrate mode and value need
+// be initialized.
+
+#ifdef PACK
+
+void init_words (WavpackStream *wps)
+{
+    CLEAR (wps->w);
+
+    if (wps->wphdr.flags & HYBRID_FLAG)
+	word_set_bitrate (wps);
+}
+
+// Set up parameters for hybrid mode based on header flags and "bits" field.
+// This is currently only set up for the HYBRID_BITRATE mode in which the
+// allowed error varies with the residual level (from "slow_level"). The
+// simpler mode (which is not used yet) has the error level directly
+// controlled from the metadata.
+
+void word_set_bitrate (WavpackStream *wps)
+{
+    int bitrate_0, bitrate_1 = 0;
+
+    if (wps->wphdr.flags & HYBRID_BITRATE) {
+	bitrate_0 = wps->bits < 568 ? 0 : wps->bits - 568;
+
+	if (!(wps->wphdr.flags & MONO_FLAG)) {
+
+	    if (wps->wphdr.flags & HYBRID_BALANCE)
+		bitrate_1 = (wps->wphdr.flags & JOINT_STEREO) ? 256 : 0;
+	    else {
+		bitrate_1 = bitrate_0;
+
+		if (wps->wphdr.flags & JOINT_STEREO) {
+		    if (bitrate_0 < 128) {
+			bitrate_1 += bitrate_0;
+			bitrate_0 = 0;
+		    }
+		    else {
+			bitrate_0 -= 128;
+			bitrate_1 += 128;
+		    }
+		}
+	    }
+	}
+    }
+    else
+	bitrate_0 = bitrate_1 = 0;
+
+    wps->w.bitrate_acc [0] = (long) bitrate_0 << 16;
+    wps->w.bitrate_acc [1] = (long) bitrate_1 << 16;
+}
+
+// Allocates the correct space in the metadata structure and writes the
+// current median values to it. Values are converted from 32-bit unsigned
+// to our internal 16-bit mylog2 values, and read_entropy_vars () is called
+// to read the values back because we must compensate for the loss through
+// the log function.
+
+void write_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    uchar *byteptr;
+    int temp;
+
+    byteptr = wpmd->data = malloc (12);
+    wpmd->id = ID_ENTROPY_VARS;
+
+    *byteptr++ = temp = mylog2 (wps->w.median [0] [0]);
+    *byteptr++ = temp >> 8;
+    *byteptr++ = temp = mylog2 (wps->w.median [1] [0]);
+    *byteptr++ = temp >> 8;
+    *byteptr++ = temp = mylog2 (wps->w.median [2] [0]);
+    *byteptr++ = temp >> 8;
+
+    if (!(wps->wphdr.flags & MONO_FLAG)) {
+	*byteptr++ = temp = mylog2 (wps->w.median [0] [1]);
+	*byteptr++ = temp >> 8;
+	*byteptr++ = temp = mylog2 (wps->w.median [1] [1]);
+	*byteptr++ = temp >> 8;
+	*byteptr++ = temp = mylog2 (wps->w.median [2] [1]);
+	*byteptr++ = temp >> 8;
+    }
+
+    wpmd->byte_length = byteptr - (uchar *) wpmd->data;
+    read_entropy_vars (wps, wpmd);
+}
+
+// Allocates enough space in the metadata structure and writes the current
+// high word of the bitrate accumulator and the slow_level values to it. The
+// slow_level values are converted from 32-bit unsigned to our internal 16-bit
+// mylog2 values. Afterward, read_entropy_vars () is called to read the values
+// back because we must compensate for the loss through the log function and
+// the truncation of the bitrate.
+
+void write_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    uchar *byteptr;
+    int temp;
+
+    word_set_bitrate (wps);
+    byteptr = wpmd->data = malloc (512);
+    wpmd->id = ID_HYBRID_PROFILE;
+
+    if (wps->wphdr.flags & HYBRID_BITRATE) {
+	*byteptr++ = temp = log2s (wps->w.slow_level [0]);
+	*byteptr++ = temp >> 8;
+
+	if (!(wps->wphdr.flags & MONO_FLAG)) {
+	    *byteptr++ = temp = log2s (wps->w.slow_level [1]);
+	    *byteptr++ = temp >> 8;
+	}
+    }
+
+    *byteptr++ = temp = wps->w.bitrate_acc [0] >> 16;
+    *byteptr++ = temp >> 8;
+
+    if (!(wps->wphdr.flags & MONO_FLAG)) {
+	*byteptr++ = temp = wps->w.bitrate_acc [1] >> 16;
+	*byteptr++ = temp >> 8;
+    }
+
+    if (wps->w.bitrate_delta [0] | wps->w.bitrate_delta [1]) {
+	*byteptr++ = temp = log2s (wps->w.bitrate_delta [0]);
+	*byteptr++ = temp >> 8;
+
+	if (!(wps->wphdr.flags & MONO_FLAG)) {
+	    *byteptr++ = temp = log2s (wps->w.bitrate_delta [1]);
+	    *byteptr++ = temp >> 8;
+	}
+    }
+
+    wpmd->byte_length = byteptr - (uchar *) wpmd->data;
+    read_hybrid_profile (wps, wpmd);
+}
+
+#endif
+
+// Read the median log2 values from the specifed metadata structure, convert
+// them back to 32-bit unsigned values and store them. If length is not
+// exactly correct then we flag and return an error.
+
+int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    uchar *byteptr = wpmd->data;
+
+    if (wpmd->byte_length != ((wps->wphdr.flags & MONO_FLAG) ? 6 : 12))
+	return FALSE;
+
+    wps->w.median [0] [0] = exp2s (byteptr [0] + (byteptr [1] << 8));
+    wps->w.median [1] [0] = exp2s (byteptr [2] + (byteptr [3] << 8));
+    wps->w.median [2] [0] = exp2s (byteptr [4] + (byteptr [5] << 8));
+
+    if (!(wps->wphdr.flags & MONO_FLAG)) {
+	wps->w.median [0] [1] = exp2s (byteptr [6] + (byteptr [7] << 8));
+	wps->w.median [1] [1] = exp2s (byteptr [8] + (byteptr [9] << 8));
+	wps->w.median [2] [1] = exp2s (byteptr [10] + (byteptr [11] << 8));
+    }
+
+    return TRUE;
+}
+
+// Read the hybrid related values from the specifed metadata structure, convert
+// them back to their internal formats and store them. The extended profile
+// stuff is not implemented yet, so return an error if we get more data than
+// we know what to do with.
+
+int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+    uchar *byteptr = wpmd->data;
+    uchar *endptr = byteptr + wpmd->byte_length;
+
+    if (wps->wphdr.flags & HYBRID_BITRATE) {
+	wps->w.slow_level [0] = exp2s (byteptr [0] + (byteptr [1] << 8));
+	byteptr += 2;
+
+	if (!(wps->wphdr.flags & MONO_FLAG)) {
+	    wps->w.slow_level [1] = exp2s (byteptr [0] + (byteptr [1] << 8));
+	    byteptr += 2;
+	}
+    }
+
+    wps->w.bitrate_acc [0] = (long)(byteptr [0] + (byteptr [1] << 8)) << 16;
+    byteptr += 2;
+
+    if (!(wps->wphdr.flags & MONO_FLAG)) {
+	wps->w.bitrate_acc [1] = (long)(byteptr [0] + (byteptr [1] << 8)) << 16;
+	byteptr += 2;
+    }
+
+    if (byteptr < endptr) {
+	wps->w.bitrate_delta [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+	byteptr += 2;
+
+	if (!(wps->wphdr.flags & MONO_FLAG)) {
+	    wps->w.bitrate_delta [1] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
+	    byteptr += 2;
+	}
+
+	if (byteptr < endptr)
+	    return FALSE;
+    }
+    else
+	wps->w.bitrate_delta [0] = wps->w.bitrate_delta [1] = 0;
+
+    return TRUE;
+}
+
+// This function is called during both encoding and decoding of hybrid data to
+// update the "error_limit" variable which determines the maximum sample error
+// allowed in the main bitstream. In the HYBRID_BITRATE mode (which is the only
+// currently implemented) this is calculated from the slow_level values and the
+// bitrate accumulators. Note that the bitrate accumulators can be changing.
+
+static void update_error_limit (WavpackStream *wps)
+{
+    int bitrate_0 = (wps->w.bitrate_acc [0] += wps->w.bitrate_delta [0]) >> 16;
+
+    if (wps->wphdr.flags & MONO_FLAG) {
+	if (wps->wphdr.flags & HYBRID_BITRATE) {
+	    int slow_log_0 = (wps->w.slow_level [0] + SLO) >> SLS;
+
+	    if (slow_log_0 - bitrate_0 > -0x100)
+		wps->w.error_limit [0] = exp2s (slow_log_0 - bitrate_0 + 0x100);
+	    else
+		wps->w.error_limit [0] = 0;
+	}
+	else
+	    wps->w.error_limit [0] = exp2s (bitrate_0);
+    }
+    else {
+	int bitrate_1 = (wps->w.bitrate_acc [1] += wps->w.bitrate_delta [1]) >> 16;
+
+	if (wps->wphdr.flags & HYBRID_BITRATE) {
+	    int slow_log_0 = (wps->w.slow_level [0] + SLO) >> SLS;
+	    int slow_log_1 = (wps->w.slow_level [1] + SLO) >> SLS;
+
+	    if (wps->wphdr.flags & HYBRID_BALANCE) {
+		int balance = (slow_log_1 - slow_log_0 + bitrate_1 + 1) >> 1;
+
+		if (balance > bitrate_0) {
+		    bitrate_1 = bitrate_0 * 2;
+		    bitrate_0 = 0;
+		}
+		else if (-balance > bitrate_0) {
+		    bitrate_0 = bitrate_0 * 2;
+		    bitrate_1 = 0;
+		}
+		else {
+		    bitrate_1 = bitrate_0 + balance;
+		    bitrate_0 = bitrate_0 - balance;
+		}
+	    }
+
+	    if (slow_log_0 - bitrate_0 > -0x100)
+		wps->w.error_limit [0] = exp2s (slow_log_0 - bitrate_0 + 0x100);
+	    else
+		wps->w.error_limit [0] = 0;
+
+	    if (slow_log_1 - bitrate_1 > -0x100)
+		wps->w.error_limit [1] = exp2s (slow_log_1 - bitrate_1 + 0x100);
+	    else
+		wps->w.error_limit [1] = 0;
+	}
+	else {
+	    wps->w.error_limit [0] = exp2s (bitrate_0);
+	    wps->w.error_limit [1] = exp2s (bitrate_1);
+	}
+    }
+}
+
+#ifdef PACK
+
+// This function writes the specified word to the open bitstream "wvbits" and,
+// if the bitstream "wvcbits" is open, writes any correction data there. This
+// function will work for either lossless or hybrid but because a version
+// optimized for lossless exits below, it would normally be used for the hybrid
+// mode only. The return value is the actual value stored to the stream (even
+// if a correction file is being created) and is used as feedback to the
+// predictor.
+
+long FASTCALL send_word (WavpackStream *wps, long value, int chan)
+{
+    ulong ones_count, low, mid, high;
+    int sign = (value < 0) ? 1 : 0;
+
+#ifdef DEBUG_WORDS
+    mid = value;
+    ones_count = 32;
+    while (ones_count--) {
+	putbit (value & 1, &wps->wvbits);
+	value >>= 1;
+    }
+    return mid;
+#endif
+
+    if (wps->w.zeros_acc) {
+	if (value)
+	    flush_word (wps);
+	else {
+	    wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS;
+	    wps->w.zeros_acc++;
+	    return 0;
+	}
+    }
+    else if (!wps->w.holding_zero && !(wps->w.median [0] [0] & ~1) && !(wps->w.median [0] [1] & ~1)) {
+	if (value) {
+	    putbit_0 (&wps->wvbits);
+	}
+	else {
+	    wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS;
+	    CLEAR (wps->w.median);
+	    wps->w.zeros_acc = 1;
+	    return 0;
+	}
+    }
+
+    if (sign)
+	value = ~value;
+
+    if ((wps->wphdr.flags & HYBRID_FLAG) && !chan)
+	update_error_limit (wps);
+
+    if (value < GET_MED (0)) {
+	ones_count = low = 0;
+	high = GET_MED (0) - 1;
+	DEC_MED0 ();
+    }
+    else {
+	low = GET_MED (0);
+	INC_MED0 ();
+
+	if (value - low < GET_MED (1)) {
+	    ones_count = 1;
+	    high = low + GET_MED (1) - 1;
+	    DEC_MED1 ();
+	}
+	else {
+	    low += GET_MED (1);
+	    INC_MED1 ();
+
+	    if (value - low < GET_MED (2)) {
+		ones_count = 2;
+		high = low + GET_MED (2) - 1;
+		DEC_MED2 ();
+	    }
+	    else {
+		ones_count = 2 + (value - low) / GET_MED (2);
+		low += (ones_count - 2) * GET_MED (2);
+		high = low + GET_MED (2) - 1;
+		INC_MED2 ();
+	    }
+	}
+    }
+
+    mid = (high + low + 1) >> 1;
+
+    if (wps->w.holding_zero) {
+	if (ones_count)
+	    wps->w.holding_one++;
+
+	flush_word (wps);
+
+	if (ones_count) {
+	    wps->w.holding_zero = 1;
+	    ones_count--;
+	}
+	else
+	    wps->w.holding_zero = 0;
+    }
+    else
+	wps->w.holding_zero = 1;
+
+    wps->w.holding_one = ones_count * 2;
+
+    if (!wps->w.error_limit [chan]) {
+	if (high != low) {	
+	    ulong maxcode = high - low, code = value - low;
+	    int bitcount = count_bits (maxcode);
+	    ulong extras = bitset [bitcount] - maxcode - 1;
+
+	    if (code < extras) {
+		wps->w.pend_data |= code << wps->w.pend_count;
+		wps->w.pend_count += bitcount - 1;
+	    }
+	    else {
+		wps->w.pend_data |= ((code + extras) >> 1) << wps->w.pend_count;
+		wps->w.pend_count += bitcount - 1;
+		wps->w.pend_data |= ((code + extras) & 1) << wps->w.pend_count++;
+	    }
+	}
+
+	mid = value;
+    }
+    else
+	while (high - low > wps->w.error_limit [chan])
+	    if (value < mid) {
+		mid = ((high = mid - 1) + low + 1) >> 1;
+		wps->w.pend_count++;
+	    }
+	    else {
+		mid = (high + (low = mid) + 1) >> 1;
+		wps->w.pend_data |= bitset [wps->w.pend_count++];
+	    }
+
+    wps->w.pend_data |= ((long) sign << wps->w.pend_count++);
+
+    if (!wps->w.holding_zero)
+	flush_word (wps);
+
+    if (bs_is_open (&wps->wvcbits) && wps->w.error_limit [chan]) {
+	ulong code = value - low, maxcode = high - low;
+	int bitcount = count_bits (maxcode);
+	ulong extras = bitset [bitcount] - maxcode - 1;
+
+	if (bitcount) {
+	    if (code < extras) {
+		putbits (code, bitcount - 1, &wps->wvcbits);
+	    }
+	    else {
+		putbits ((code + extras) >> 1, bitcount - 1, &wps->wvcbits);
+		putbit ((code + extras) & 1, &wps->wvcbits);
+	    }
+	}
+    }
+
+    if (wps->wphdr.flags & HYBRID_BITRATE) {
+	wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS;
+	wps->w.slow_level [chan] += mylog2 (mid);
+    }
+
+    return sign ? ~mid : mid;
+}
+
+// This function is an optimized version of send_word() that only handles
+// lossless (error_limit == 0). It does not return a value because it always
+// encodes the exact value passed.
+
+void FASTCALL send_word_lossless (WavpackStream *wps, long value, int chan)
+{
+    int sign = (value < 0) ? 1 : 0;
+    ulong ones_count, low, high;
+
+#ifdef DEBUG_WORDS
+    ones_count = 32;
+    while (ones_count--) {
+	putbit (value & 1, &wps->wvbits);
+	value >>= 1;
+    }
+    return;
+#endif
+
+    if (wps->w.zeros_acc) {
+	if (value)
+	    flush_word (wps);
+	else {
+	    wps->w.zeros_acc++;
+	    return;
+	}
+    }
+    else if (!wps->w.holding_zero && !(wps->w.median [0] [0] & ~1) && !(wps->w.median [0] [1] & ~1)) {
+	if (value) {
+	    putbit_0 (&wps->wvbits);
+	}
+	else {
+	    CLEAR (wps->w.median);
+	    wps->w.zeros_acc = 1;
+	    return;
+	}
+    }
+
+    if (sign)
+	value = ~value;
+
+    if (value < GET_MED (0)) {
+	ones_count = low = 0;
+	high = GET_MED (0) - 1;
+	DEC_MED0 ();
+    }
+    else {
+	low = GET_MED (0);
+	INC_MED0 ();
+
+	if (value - low < GET_MED (1)) {
+	    ones_count = 1;
+	    high = low + GET_MED (1) - 1;
+	    DEC_MED1 ();
+	}
+	else {
+	    low += GET_MED (1);
+	    INC_MED1 ();
+
+	    if (value - low < GET_MED (2)) {
+		ones_count = 2;
+		high = low + GET_MED (2) - 1;
+		DEC_MED2 ();
+	    }
+	    else {
+		ones_count = 2 + (value - low) / GET_MED (2);
+		low += (ones_count - 2) * GET_MED (2);
+		high = low + GET_MED (2) - 1;
+		INC_MED2 ();
+	    }
+	}
+    }
+
+    if (wps->w.holding_zero) {
+	if (ones_count)
+	    wps->w.holding_one++;
+
+	flush_word (wps);
+
+	if (ones_count) {
+	    wps->w.holding_zero = 1;
+	    ones_count--;
+	}
+	else
+	    wps->w.holding_zero = 0;
+    }
+    else
+	wps->w.holding_zero = 1;
+
+    wps->w.holding_one = ones_count * 2;
+
+    if (high != low) {	
+	ulong maxcode = high - low, code = value - low;
+	int bitcount = count_bits (maxcode);
+	ulong extras = bitset [bitcount] - maxcode - 1;
+
+	if (code < extras) {
+	    wps->w.pend_data |= code << wps->w.pend_count;
+	    wps->w.pend_count += bitcount - 1;
+	}
+	else {
+	    wps->w.pend_data |= ((code + extras) >> 1) << wps->w.pend_count;
+	    wps->w.pend_count += bitcount - 1;
+	    wps->w.pend_data |= ((code + extras) & 1) << wps->w.pend_count++;
+	}
+    }
+
+    wps->w.pend_data |= ((long) sign << wps->w.pend_count++);
+
+    if (!wps->w.holding_zero)
+	flush_word (wps);
+}
+
+// Used by send_word() and send_word_lossless() to actually send most the
+// accumulated data onto the bitstream. This is also called directly from
+// clients when all words have been sent.
+
+void flush_word (WavpackStream *wps)
+{
+    if (wps->w.zeros_acc) {
+	int cbits = count_bits (wps->w.zeros_acc);
+
+	while (cbits--) {
+	    putbit_1 (&wps->wvbits);
+	}
+
+	putbit_0 (&wps->wvbits);
+
+	while (wps->w.zeros_acc > 1) {
+	    putbit (wps->w.zeros_acc & 1, &wps->wvbits);
+	    wps->w.zeros_acc >>= 1;
+	}
+
+	wps->w.zeros_acc = 0;
+    }
+
+    if (wps->w.holding_one) {
+#ifdef LIMIT_ONES
+	if (wps->w.holding_one >= LIMIT_ONES) {
+	    int cbits;
+
+	    putbits ((1L << LIMIT_ONES) - 1, LIMIT_ONES + 1, &wps->wvbits);
+	    wps->w.holding_one -= LIMIT_ONES;
+	    cbits = count_bits (wps->w.holding_one);
+
+	    while (cbits--) {
+		putbit_1 (&wps->wvbits);
+	    }
+
+	    putbit_0 (&wps->wvbits);
+
+	    while (wps->w.holding_one > 1) {
+		putbit (wps->w.holding_one & 1, &wps->wvbits);
+		wps->w.holding_one >>= 1;
+	    }
+
+	    wps->w.holding_zero = 0;
+	}
+	else
+	    putbits (bitmask [wps->w.holding_one], wps->w.holding_one, &wps->wvbits);
+
+	wps->w.holding_one = 0;
+#else
+	do {
+	    putbit_1 (&wps->wvbits);
+	} while (--wps->w.holding_one);
+#endif
+    }
+
+    if (wps->w.holding_zero) {
+	putbit_0 (&wps->wvbits);
+	wps->w.holding_zero = 0;
+    }
+
+    if (wps->w.pend_count) {
+
+	while (wps->w.pend_count > 24) {
+	    putbit (wps->w.pend_data & 1, &wps->wvbits);
+	    wps->w.pend_data >>= 1;
+	    wps->w.pend_count--;
+	}
+
+	putbits (wps->w.pend_data, wps->w.pend_count, &wps->wvbits);
+	wps->w.pend_data = wps->w.pend_count = 0;
+    }
+}
+
+// This function is similar to send_word() except that no data is actually
+// written to any stream, but it does return the value that would have been
+// sent to a hybrid stream. It is used to determine beforehand how much noise
+// will be added to samples.
+
+long nosend_word (WavpackStream *wps, long value, int chan)
+{
+    ulong ones_count, low, mid, high;
+    int sign = (value < 0) ? 1 : 0;
+
+    if (sign)
+	value = ~value;
+
+    if ((wps->wphdr.flags & HYBRID_FLAG) && !chan)
+	update_error_limit (wps);
+
+    if (value < GET_MED (0)) {
+	low = 0;
+	high = GET_MED (0) - 1;
+	DEC_MED0 ();
+    }
+    else {
+	low = GET_MED (0);
+	INC_MED0 ();
+
+	if (value - low < GET_MED (1)) {
+	    high = low + GET_MED (1) - 1;
+	    DEC_MED1 ();
+	}
+	else {
+	    low += GET_MED (1);
+	    INC_MED1 ();
+
+	    if (value - low < GET_MED (2)) {
+		high = low + GET_MED (2) - 1;
+		DEC_MED2 ();
+	    }
+	    else {
+		ones_count = 2 + (value - low) / GET_MED (2);
+		low += (ones_count - 2) * GET_MED (2);
+		high = low + GET_MED (2) - 1;
+		INC_MED2 ();
+	    }
+	}
+    }
+
+    mid = (high + low + 1) >> 1;
+
+    if (!wps->w.error_limit [chan])
+	mid = value;
+    else
+	while (high - low > wps->w.error_limit [chan])
+	    if (value < mid)
+		mid = ((high = mid - 1) + low + 1) >> 1;
+	    else
+		mid = (high + (low = mid) + 1) >> 1;
+
+    wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS;
+    wps->w.slow_level [chan] += mylog2 (mid);
+
+    return sign ? ~mid : mid;
+}
+
+// This function is used to scan some number of samples to set the variables
+// "slow_level" and the "median" array. In pure symetrical encoding mode this
+// would not be needed because these values would simply be continued from the
+// previous block. However, in the -X modes and the 32-bit modes we cannot do
+// this because parameters may change between blocks and the variables might
+// not apply. This function can work in mono or stereo and can scan a block
+// in either direction.
+
+void scan_word (WavpackStream *wps, long *samples, ulong num_samples, int dir)
+{
+    ulong flags = wps->wphdr.flags, value, low;
+    int chan;
+
+    CLEAR (wps->w.slow_level);
+    CLEAR (wps->w.median);
+
+    if (flags & MONO_FLAG) {
+	if (dir < 0) {
+	    samples += (num_samples - 1);
+	    dir = -1;
+	}
+	else
+	    dir = 1;
+    }
+    else {
+	if (dir < 0) {
+	    samples += (num_samples - 1) * 2;
+	    dir = -2;
+	}
+	else
+	    dir = 2;
+    }
+
+    while (num_samples--) {
+
+	value = labs (samples [chan = 0]);
+
+	if (flags & HYBRID_BITRATE) {
+	    wps->w.slow_level [0] -= (wps->w.slow_level [0] + SLO) >> SLS;
+	    wps->w.slow_level [0] += mylog2 (value);
+	}
+
+	if (value < GET_MED (0)) {
+	    DEC_MED0 ();
+	}
+	else {
+	    low = GET_MED (0);
+	    INC_MED0 ();
+
+	    if (value - low < GET_MED (1)) {
+		DEC_MED1 ();
+	    }
+	    else {
+		low += GET_MED (1);
+		INC_MED1 ();
+
+		if (value - low < GET_MED (2)) {
+		    DEC_MED2 ();
+		}
+		else {
+		    INC_MED2 ();
+		}
+	    }
+	}
+
+	if (!(flags & MONO_FLAG)) {
+	    value = labs (samples [chan = 1]);
+
+	    if (wps->wphdr.flags & HYBRID_BITRATE) {
+		wps->w.slow_level [1] -= (wps->w.slow_level [1] + SLO) >> SLS;
+		wps->w.slow_level [1] += mylog2 (value);
+	    }
+
+	    if (value < GET_MED (0)) {
+		DEC_MED0 ();
+	    }
+	    else {
+		low = GET_MED (0);
+		INC_MED0 ();
+
+		if (value - low < GET_MED (1)) {
+		    DEC_MED1 ();
+		}
+		else {
+		    low += GET_MED (1);
+		    INC_MED1 ();
+
+		    if (value - low < GET_MED (2)) {
+			DEC_MED2 ();
+		    }
+		    else {
+			INC_MED2 ();
+		    }
+		}
+	    }
+	}
+
+	samples += dir;
+    }
+}
+
+#endif
+
+#ifdef UNPACK
+
+static ulong FASTCALL read_code (Bitstream *bs, ulong maxcode);
+
+// Read the next word from the bitstream "wvbits" and return the value. This
+// function can be used for hybrid or lossless streams, but since an
+// optimized version is available for lossless this function would normally
+// be used for hybrid only. If a hybrid lossless stream is being read then
+// the "correction" offset is written at the specified pointer. A return value
+// of WORD_EOF indicates that the end of the bitstream was reached (all 1s) or
+// some other error occurred.
+
+long FASTCALL get_word (WavpackStream *wps, int chan, long *correction)
+{
+    ulong ones_count, low, mid, high;
+    long value;
+    int sign;
+
+    if (correction)
+	*correction = 0;
+
+#ifdef DEBUG_WORDS
+    ones_count = 32;
+    while (ones_count--)
+	value = getbit (&wps->wvbits) ? (value >> 1) | 0x80000000 : (value >> 1) & 0x7fffffff;
+    return value;
+#endif
+
+    if (wps->w.zeros_acc) {
+	if (--wps->w.zeros_acc) {
+	    wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS;
+	    return 0;
+	}
+    }
+    else if (!wps->w.holding_zero && !wps->w.holding_one && !(wps->w.median [0] [0] & ~1) && !(wps->w.median [0] [1] & ~1)) {
+	ulong mask;
+	int cbits;
+
+	for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits);
+
+	if (cbits == 33)
+	    return WORD_EOF;
+
+	if (cbits < 2)
+	    wps->w.zeros_acc = cbits;
+	else {
+	    for (mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1)
+		if (getbit (&wps->wvbits))
+		    wps->w.zeros_acc |= mask;
+
+	    wps->w.zeros_acc |= mask;
+	}
+
+	if (wps->w.zeros_acc) {
+	    wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS;
+	    CLEAR (wps->w.median);
+	    return 0;
+	}
+    }
+
+    if (wps->w.holding_zero)
+	ones_count = wps->w.holding_zero = 0;
+    else {
+#ifdef LIMIT_ONES
+	for (ones_count = 0; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count);
+
+	if (ones_count == (LIMIT_ONES + 1))
+	    return WORD_EOF;
+
+	if (ones_count == LIMIT_ONES) {
+	    ulong mask;
+	    int cbits;
+
+	    for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits);
+
+	    if (cbits == 33)
+		return WORD_EOF;
+
+	    if (cbits < 2)
+		ones_count = cbits;
+	    else {
+		for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
+		    if (getbit (&wps->wvbits))
+			ones_count |= mask;
+
+		ones_count |= mask;
+	    }
+
+	    ones_count += LIMIT_ONES;
+	}
+#else
+	for (ones_count = 0; getbit (&wps->wvbits); ++ones_count);
+#endif
+
+	if (wps->w.holding_one) {
+	    wps->w.holding_one = ones_count & 1;
+	    ones_count = (ones_count >> 1) + 1;
+	}
+	else {
+	    wps->w.holding_one = ones_count & 1;
+	    ones_count >>= 1;
+	}
+
+	wps->w.holding_zero = ~wps->w.holding_one & 1;
+    }
+
+    if ((wps->wphdr.flags & HYBRID_FLAG) && !chan)
+	update_error_limit (wps);
+
+    if (ones_count == 0) {
+	low = 0;
+	high = GET_MED (0) - 1;
+	DEC_MED0 ();
+    }
+    else {
+	low = GET_MED (0);
+	INC_MED0 ();
+
+	if (ones_count == 1) {
+	    high = low + GET_MED (1) - 1;
+	    DEC_MED1 ();
+	}
+	else {
+	    low += GET_MED (1);
+	    INC_MED1 ();
+
+	    if (ones_count == 2) {
+		high = low + GET_MED (2) - 1;
+		DEC_MED2 ();
+	    }
+	    else {
+		low += (ones_count - 2) * GET_MED (2);
+		high = low + GET_MED (2) - 1;
+		INC_MED2 ();
+	    }
+	}
+    }
+
+    mid = (high + low + 1) >> 1;
+
+    if (!wps->w.error_limit [chan])
+	mid = read_code (&wps->wvbits, high - low) + low;
+    else while (high - low > wps->w.error_limit [chan]) {
+	if (getbit (&wps->wvbits))
+	    mid = (high + (low = mid) + 1) >> 1;
+	else
+	    mid = ((high = mid - 1) + low + 1) >> 1;
+    }
+
+    sign = getbit (&wps->wvbits);
+
+    if (bs_is_open (&wps->wvcbits) && wps->w.error_limit [chan]) {
+	value = read_code (&wps->wvcbits, high - low) + low;
+
+	if (correction)
+	    *correction = sign ? (mid - value) : (value - mid);
+    }
+
+    if (wps->wphdr.flags & HYBRID_BITRATE) {
+	wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS;
+	wps->w.slow_level [chan] += mylog2 (mid);
+    }
+
+    return sign ? ~mid : mid;
+}
+
+// This is an optimized version of get_word() that is used for lossless only
+// (error_limit == 0). There are two versions of an internal section; they
+// are identical from a functional standpoint, but one may be faster than the
+// other under different compilers / processors.
+
+long FASTCALL get_word_lossless (WavpackStream *wps, int chan)
+{
+    ulong ones_count, low, high;
+
+#ifdef DEBUG_WORDS
+    ones_count = 32;
+    while (ones_count--)
+	low = getbit (&wps->wvbits) ? (low >> 1) | 0x80000000 : (low >> 1) & 0x7fffffff;
+    return low;
+#endif
+
+    if (wps->w.zeros_acc) {
+	if (--wps->w.zeros_acc)
+	    return 0;
+    }
+    else if (!wps->w.holding_zero && !wps->w.holding_one && !(wps->w.median [0] [0] & ~1) && !(wps->w.median [0] [1] & ~1)) {
+	ulong mask;
+	int cbits;
+
+	for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits);
+
+	if (cbits == 33)
+	    return WORD_EOF;
+
+	if (cbits < 2)
+	    wps->w.zeros_acc = cbits;
+	else {
+	    for (mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1)
+		if (getbit (&wps->wvbits))
+		    wps->w.zeros_acc |= mask;
+
+	    wps->w.zeros_acc |= mask;
+	}
+
+	if (wps->w.zeros_acc) {
+	    CLEAR (wps->w.median);
+	    return 0;
+	}
+    }
+
+    if (wps->w.holding_zero)
+	ones_count = wps->w.holding_zero = 0;
+    else {
+#ifdef LIMIT_ONES
+	for (ones_count = 0; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count);
+
+	if (ones_count == (LIMIT_ONES + 1))
+	    return WORD_EOF;
+
+	if (ones_count == LIMIT_ONES) {
+	    ulong mask;
+	    int cbits;
+
+	    for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits);
+
+	    if (cbits == 33)
+		return WORD_EOF;
+
+	    if (cbits < 2)
+		ones_count = cbits;
+	    else {
+		for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
+		    if (getbit (&wps->wvbits))
+			ones_count |= mask;
+
+		ones_count |= mask;
+	    }
+
+	    ones_count += LIMIT_ONES;
+	}
+#else
+	for (ones_count = 0; getbit (&wps->wvbits); ++ones_count);
+#endif
+
+	if (wps->w.holding_one) {
+	    wps->w.holding_one = ones_count & 1;
+	    ones_count = (ones_count >> 1) + 1;
+	}
+	else {
+	    wps->w.holding_one = ones_count & 1;
+	    ones_count >>= 1;
+	}
+
+	wps->w.holding_zero = ~wps->w.holding_one & 1;
+    }
+
+#if 1
+    if (ones_count == 0) {
+	low = 0;
+	high = GET_MED (0) - 1;
+	DEC_MED0 ();
+    }
+    else {
+	low = GET_MED (0);
+	INC_MED0 ();
+
+	if (ones_count == 1) {
+	    high = low + GET_MED (1) - 1;
+	    DEC_MED1 ();
+	}
+	else {
+	    low += GET_MED (1);
+	    INC_MED1 ();
+
+	    if (ones_count == 2) {
+		high = low + GET_MED (2) - 1;
+		DEC_MED2 ();
+	    }
+	    else {
+		low += (ones_count - 2) * GET_MED (2);
+		high = low + GET_MED (2) - 1;
+		INC_MED2 ();
+	    }
+	}
+    }
+#else
+    switch (ones_count) {
+	case 0:
+	    low = 0;
+	    high = GET_MED (0) - 1;
+	    DEC_MED0 ();
+	    break;
+
+	case 1:
+	    high = (low = GET_MED (0)) + GET_MED (1) - 1;
+	    INC_MED0 ();
+	    DEC_MED1 ();
+	    break;
+
+	case 2:
+	    high = (low = GET_MED (0) + GET_MED (1)) + GET_MED (2) - 1;
+	    INC_MED0 ();
+	    INC_MED1 ();
+	    DEC_MED2 ();
+	    break;
+
+	default:
+	    low = GET_MED (0) + GET_MED (1) + (ones_count - 2) * GET_MED (2);
+	    high = low + GET_MED (2) - 1;
+	    INC_MED0 ();
+	    INC_MED1 ();
+	    INC_MED2 ();
+	    break;
+    }
+#endif
+
+    low += read_code (&wps->wvbits, high - low);
+    return (getbit (&wps->wvbits)) ? ~low : low;
+}
+
+// Read a single unsigned value from the specified bitstream with a value
+// from 0 to maxcode. If there are exactly a power of two number of possible
+// codes then this will read a fixed number of bits; otherwise it reads the
+// minimum number of bits and then determines whether another bit is needed
+// to define the code.
+
+static ulong FASTCALL read_code (Bitstream *bs, ulong maxcode)
+{
+    int bitcount = count_bits (maxcode);
+    ulong extras = bitset [bitcount] - maxcode - 1, code;
+
+    if (!bitcount)
+	return 0;
+
+    getbits (&code, bitcount - 1, bs);
+    code &= bitmask [bitcount - 1];
+
+    if (code >= extras) {
+	code = (code << 1) - extras;
+
+	if (getbit (bs))
+	    ++code;
+    }
+
+    return code;
+}
+
+#endif
+
+// The concept of a base 2 logarithm is used in many parts of WavPack. It is
+// a way of sufficiently accurately representing 32-bit signed and unsigned
+// values storing only 16 bits (actually fewer). It is also used in the hybrid
+// mode for quickly comparing the relative magnitude of large values (i.e.
+// division) and providing smooth exponentials using only addition.
+
+// These are not strict logarithms in that they become linear around zero and
+// can therefore represent both zero and negative values. They have 8 bits
+// of precision and in "roundtrip" conversions the total error never exceeds 1
+// part in 225 except for the cases of +/-115 and +/-195 (which error by 1).
+
+
+// This function returns the log2 for the specified 32-bit unsigned value.
+// The maximum value allowed is about 0xff800000 and returns 8447.
+
+static int FASTCALL mylog2 (unsigned long avalue)
+{
+    int dbits;
+
+    if ((avalue += avalue >> 9) < (1 << 8)) {
+	dbits = nbits_table [avalue];
+	return (dbits << 8) + log2_table [(avalue << (9 - dbits)) & 0xff];
+    }
+    else {
+	if (avalue < (1L << 16))
+	    dbits = nbits_table [avalue >> 8] + 8;
+	else if (avalue < (1L << 24))
+	    dbits = nbits_table [avalue >> 16] + 16;
+	else
+	    dbits = nbits_table [avalue >> 24] + 24;
+
+	return (dbits << 8) + log2_table [(avalue >> (dbits - 9)) & 0xff];
+    }
+}
+
+// This function scans a buffer of longs and accumulates the total log2 value
+// of all the samples. This is useful for determining maximum compression
+// because the bitstream storage required for entropy coding is proportional
+// to the base 2 log of the samples.
+
+ulong log2buffer (long *samples, ulong num_samples)
+{
+    ulong result = 0, avalue;
+    int dbits;
+
+    while (num_samples--) {
+	avalue = abs (*samples++);
+
+	if ((avalue += avalue >> 9) < (1 << 8)) {
+	    dbits = nbits_table [avalue];
+	    result += (dbits << 8) + log2_table [(avalue << (9 - dbits)) & 0xff];
+	}
+	else {
+	    if (avalue < (1L << 16))
+		dbits = nbits_table [avalue >> 8] + 8;
+	    else if (avalue < (1L << 24))
+		dbits = nbits_table [avalue >> 16] + 16;
+	    else
+		dbits = nbits_table [avalue >> 24] + 24;
+
+	    result += (dbits << 8) + log2_table [(avalue >> (dbits - 9)) & 0xff];
+	}
+    }
+
+    return result;
+}
+
+// This function returns the log2 for the specified 32-bit signed value.
+// All input values are valid and the return values are in the range of
+// +/- 8192.
+
+int log2s (long value)
+{
+    return (value < 0) ? -mylog2 (-value) : mylog2 (value);
+}
+
+// This function returns the original integer represented by the supplied
+// logarithm (at least within the provided accuracy). The log is signed,
+// but since a full 32-bit value is returned this can be used for unsigned
+// conversions as well (i.e. the input range is -8192 to +8447).
+
+long exp2s (int log)
+{
+    ulong value;
+
+    if (log < 0)
+	return -exp2s (-log);
+
+    value = exp2_table [log & 0xff] | 0x100;
+
+    if ((log >>= 8) <= 9)
+	return value >> (9 - log);
+    else
+	return value << (log - 9);
+}
+
+// These two functions convert internal weights (which are normally +/-1024)
+// to and from an 8-bit signed character version for storage in metadata. The
+// weights are clipped here in the case that they are outside that range.
+
+char store_weight (int weight)
+{
+    if (weight > 1024)
+	weight = 1024;
+    else if (weight < -1024)
+	weight = -1024;
+
+    if (weight > 0)
+	weight -= (weight + 64) >> 7;
+
+    return (weight + 4) >> 3;
+}
+
+int restore_weight (char weight)
+{
+    int result;
+
+    if ((result = (int) weight << 3) > 0)
+	result += (result + 64) >> 7;
+
+    return result;
+}
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/wputils.c	2005-04-01 16:24:06.000000000 +0100
@@ -0,0 +1,1970 @@
+////////////////////////////////////////////////////////////////////////////
+//			     **** WAVPACK ****				  //
+//		    Hybrid Lossless Wavefile Compressor			  //
+//		Copyright (c) 1998 - 2004 Conifer Software.		  //
+//			    All Rights Reserved.			  //
+//      Distributed under the BSD Software License (see license.txt)      //
+////////////////////////////////////////////////////////////////////////////
+
+// wputils.c
+
+// This module provides a high-level interface to reading and writing WavPack
+// files. WavPack input files can be opened as standard "C" streams using a
+// provided filename. However, an alternate entry uses stream-reading
+// callbacks to make using another file I/O method easy. Note that in this
+// case the user application is responsible for finding and opening the .wvc
+// file if the use of them is desired.
+
+// For writing WavPack files there are no I/O routines used; a callback for
+// writing completed blocks is provided.
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <io.h>
+#endif
+
+#include "wavpack.h"
+
+#if !defined(WIN32)
+#define stricmp(x,y) strcasecmp(x,y)
+#endif
+
+#ifdef DEBUG_ALLOC
+#define malloc malloc_db
+#define realloc realloc_db
+#define free free_db
+void *malloc_db (ulong size);
+void *realloc_db (void *ptr, ulong size);
+void free_db (void *ptr);
+long dump_alloc (void);
+#endif
+
+static void free_streams (WavpackContext *wpc);
+
+///////////////////////////// local table storage ////////////////////////////
+
+const ulong sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050,
+    24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 };
+
+///////////////////////////// executable code ////////////////////////////////
+
+#if defined(UNPACK) || defined(INFO_ONLY)
+
+static ulong read_next_header (stream_reader *reader, void *id, WavpackHeader *wphdr);
+static ulong seek_final_index (stream_reader *reader, void *id);
+
+#ifdef TAGS
+static int load_tag (WavpackContext *wpc);
+static int valid_tag (M_Tag *m_tag);
+static void free_tag (M_Tag *m_tag);
+#endif
+
+// This code provides an interface between the reader callback mechanism that
+// WavPack uses internally and the standard fstream C library.
+
+#ifdef USE_FSTREAMS
+
+static long read_bytes (void *id, void *data, long bcount)
+{
+    return fread (data, 1, bcount, (FILE*) id);
+}
+
+static ulong get_pos (void *id)
+{
+    return ftell ((FILE*) id);
+}
+
+static int set_pos_abs (void *id, ulong pos)
+{
+    return fseek (id, pos, SEEK_SET);
+}
+
+static int set_pos_rel (void *id, long delta, int mode)
+{
+    return fseek (id, delta, mode);
+}
+
+static int push_back_byte (void *id, int c)
+{
+    return ungetc (c, id);
+}
+
+static ulong get_length (void *id)
+{
+    FILE *file = id;
+    struct stat statbuf;
+
+    if (!file || fstat (fileno (file), &statbuf) || !(statbuf.st_mode & S_IFREG))
+	return 0;
+
+    return statbuf.st_size;
+}
+
+static int can_seek (void *id)
+{
+    FILE *file = id;
+    struct stat statbuf;
+
+    return file && !fstat (fileno (file), &statbuf) && (statbuf.st_mode & S_IFREG);
+}
+
+static stream_reader freader = {
+    read_bytes, get_pos, set_pos_abs, set_pos_rel, push_back_byte, get_length, can_seek
+};
+	
+// This function attempts to open the specified WavPack file for reading. If
+// this fails for any reason then an appropriate message is copied to "error"
+// and NULL is returned, otherwise a pointer to a WavpackContext structure is
+// returned (which is used to call all other functions in this module). A
+// filename beginning with "-" is assumed to be stdin. The "flags" argument
+// has the following bit mask values to specify details of the open operation:
+
+// OPEN_WVC:  attempt to open/read "correction" file
+// OPEN_TAGS:  attempt to read ID3v1 / APEv2 tags (requires seekable file)
+// OPEN_WRAPPER:  make audio wrapper available (i.e. RIFF) to caller
+// OPEN_2CH_MAX:  open only first stream of multichannel file (usually L/R)
+// OPEN_NORMALIZE:  normalize floating point data to +/- 1.0 (w/ offset exp)
+
+WavpackContext *WavpackOpenFileInput (const char *infilename, char *error, int flags, int norm_offset)
+{
+    FILE *wv_id, *wvc_id;
+    WavpackContext *wpc;
+
+    if (*infilename == '-') {
+	wv_id = stdin;
+#if defined(WIN32)
+	setmode (fileno (stdin), O_BINARY);
+#endif
+    }
+    else if ((wv_id = fopen (infilename, "rb")) == NULL) {
+	strcpy (error, "can't open file");
+	return NULL;
+    }
+
+    if (wv_id != stdin && (flags & OPEN_WVC)) {
+	char *in2filename = malloc (strlen (infilename) + 10);
+
+	strcpy (in2filename, infilename);
+	strcat (in2filename, "c");
+	wvc_id = fopen (in2filename, "rb");
+    }
+    else
+	wvc_id = NULL;
+
+    wpc = WavpackOpenFileInputEx (&freader, wv_id, wvc_id, error, flags, norm_offset);
+
+    if (!wpc) {
+	if (wv_id)
+	    fclose (wv_id);
+
+	if (wvc_id)
+	    fclose (wvc_id);
+    }
+    else
+	wpc->close_files = TRUE;
+
+    return wpc;
+}
+
+#endif
+
+// This function is identical to WavpackOpenFileInput() except that instead
+// of providing a filename to open, the caller provides a pointer to a set of
+// reader callbacks and instances of up to two streams. The first of these
+// streams is required and contains the regular WavPack data stream; the second
+// contains the "correction" file if desired. Unlike the standard open
+// function which handles the correction file transparently, in this case it
+// is the responsibility of the caller to be aware of correction files.
+
+WavpackContext *WavpackOpenFileInputEx (stream_reader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset)
+{
+    WavpackContext *wpc = malloc (sizeof (WavpackContext));
+    WavpackStream *wps;
+    /* uchar first_byte; */
+    ulong bcount;
+
+    if (!wpc)
+	return NULL;
+
+    CLEAR (*wpc);
+    wpc->wv_in = wv_id;
+    wpc->wvc_in = wvc_id;
+    wpc->reader = reader;
+    wpc->total_samples = (ulong) -1;
+    wpc->norm_offset = norm_offset;
+    wpc->open_flags = flags;
+
+    wpc->filelen = wpc->reader->get_length (wpc->wv_in);
+
+#ifdef TAGS
+    if ((flags & OPEN_TAGS) && wpc->reader->can_seek (wpc->wv_in)) {
+	load_tag (wpc);
+	wpc->reader->set_pos_abs (wpc->wv_in, 0);
+    }
+#endif
+
+#ifdef VER3
+    if (wpc->reader->read_bytes (wpc->wv_in, &first_byte, 1) != 1) {
+	strcpy (error, "can't read all of WavPack file!");
+	return WavpackCloseFile (wpc);
+    }
+
+    wpc->reader->push_back_byte (wpc->wv_in, first_byte);
+
+    if (first_byte == 'R')
+	return open_file3 (wpc, error);
+#endif
+
+    wpc->streams [0] = wps = malloc (sizeof (WavpackStream));
+    wpc->num_streams = 1;
+    CLEAR (*wps);
+
+    while (!wps->wphdr.block_samples) {
+
+	wpc->filepos = wpc->reader->get_pos (wpc->wv_in);
+	bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr);
+
+	if (bcount == (ulong) -1) {
+	    strcpy (error, "not compatible with this version of WavPack file!");
+	    return WavpackCloseFile (wpc);
+	}
+
+	wpc->filepos += bcount;
+	wps->blockbuff = malloc (wps->wphdr.ckSize + 8);
+	memcpy (wps->blockbuff, &wps->wphdr, 32);
+
+	if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != wps->wphdr.ckSize - 24) {
+	    strcpy (error, "can't read all of WavPack file!");
+	    return WavpackCloseFile (wpc);
+	}
+
+	if ((wps->wphdr.flags & UNKNOWN_FLAGS) || wps->wphdr.version < 0x402 || wps->wphdr.version > 0x40f) {
+	    strcpy (error, "not compatible with this version of WavPack file!");
+	    return WavpackCloseFile (wpc);
+	}
+
+	if (wps->wphdr.block_samples) {
+	    if (wps->wphdr.total_samples == (ulong) -1 && wpc->reader->can_seek (wpc->wv_in)) {
+		ulong pos_save = wpc->reader->get_pos (wpc->wv_in);
+		ulong final_index = seek_final_index (wpc->reader, wpc->wv_in);
+
+		if (final_index != (ulong) -1)
+		    wpc->total_samples = final_index - wps->wphdr.block_index;
+
+		wpc->reader->set_pos_abs (wpc->wv_in, pos_save);
+	    }
+	    else
+		wpc->total_samples = wps->wphdr.total_samples;
+	}
+
+	if (wpc->wvc_in && wps->wphdr.block_samples && (wps->wphdr.flags & HYBRID_FLAG)) {
+	    wpc->file2len = wpc->reader->get_length (wpc->wvc_in);
+	    wpc->wvc_flag = TRUE;
+	}
+
+	if (wpc->wvc_flag) {
+	    wpc->file2pos = wpc->reader->get_pos (wpc->wvc_in);
+	    bcount = read_next_header (wpc->reader, wpc->wvc_in, &wps->wphdr);
+
+	    if (bcount == (ulong) -1) {
+		strcpy (error, "problem with correction file");
+		return WavpackCloseFile (wpc);
+	    }
+
+	    wpc->file2pos += bcount;
+	    wps->block2buff = malloc (wps->wphdr.ckSize + 8);
+	    memcpy (wps->block2buff, &wps->wphdr, 32);
+
+	    if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + 32, wps->wphdr.ckSize - 24) !=
+		wps->wphdr.ckSize - 24) {
+		    strcpy (error, "can't read all of WavPack file!");
+		    return WavpackCloseFile (wpc);
+	    }
+	}
+
+	if (!unpack_init (wpc)) {
+	    strcpy (error, wpc->error_message [0] ? wpc->error_message :
+		"not compatible with this version of WavPack file!");
+
+	    return WavpackCloseFile (wpc);
+	}
+    }
+
+    wpc->config.flags &= ~0xff;
+    wpc->config.flags |= wps->wphdr.flags & 0xff;
+    wpc->config.bytes_per_sample = (wps->wphdr.flags & BYTES_STORED) + 1;
+    wpc->config.float_norm_exp = wps->float_norm_exp;
+
+    wpc->config.bits_per_sample = (wpc->config.bytes_per_sample * 8) - 
+	((wps->wphdr.flags & SHIFT_MASK) >> SHIFT_LSB);
+
+    if (!wpc->config.sample_rate) {
+	if (!wps || !wps->wphdr.block_samples || (wps->wphdr.flags & SRATE_MASK) == SRATE_MASK)
+	    wpc->config.sample_rate = 44100;
+	else
+	    wpc->config.sample_rate = sample_rates [(wps->wphdr.flags & SRATE_MASK) >> SRATE_LSB];
+    }
+
+    if (!wpc->config.num_channels) {
+	wpc->config.num_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2;
+	wpc->config.channel_mask = 0x5 - wpc->config.num_channels;
+    }
+
+    if ((flags & OPEN_2CH_MAX) && !(wps->wphdr.flags & FINAL_BLOCK))
+	wpc->reduced_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2;
+
+    return wpc;
+}
+
+// This function obtains general information about an open file and returns
+// a mask with the following bit values:
+
+// MODE_WVC:  a .wvc file has been found and will be used for lossless
+// MODE_LOSSLESS:  file is lossless (either pure or hybrid)
+// MODE_HYBRID:  file is hybrid mode (either lossy or lossless)
+// MODE_FLOAT:  audio data is 32-bit ieee floating point
+// MODE_VALID_TAG:  file conatins a valid ID3v1 or APEv2 tag
+// MODE_HIGH:  file was created in "high" mode (information only)
+// MODE_FAST:  file was created in "fast" mode (information only)
+// MODE_EXTRA:  file was created using "extra" mode (information only)
+// MODE_APETAG:  file contains a valid APEv2 tag
+// MODE_SFX:  file was created as a "self-extracting" executable
+
+int WavpackGetMode (WavpackContext *wpc)
+{
+    int mode = 0;
+
+    if (wpc) {
+	if (wpc->config.flags & CONFIG_HYBRID_FLAG)
+	    mode |= MODE_HYBRID;
+	else if (!(wpc->config.flags & CONFIG_LOSSY_MODE))
+	    mode |= MODE_LOSSLESS;
+
+	if (wpc->wvc_flag)
+	    mode |= (MODE_LOSSLESS | MODE_WVC);
+
+	if (wpc->lossy_blocks)
+	    mode &= ~MODE_LOSSLESS;
+
+	if (wpc->config.flags & CONFIG_FLOAT_DATA)
+	    mode |= MODE_FLOAT;
+
+	if (wpc->config.flags & CONFIG_HIGH_FLAG)
+	    mode |= MODE_HIGH;
+
+	if (wpc->config.flags & CONFIG_FAST_FLAG)
+	    mode |= MODE_FAST;
+
+	if (wpc->config.flags & CONFIG_EXTRA_MODE)
+	    mode |= MODE_EXTRA;
+
+	if (wpc->config.flags & CONFIG_CREATE_EXE)
+	    mode |= MODE_SFX;
+
+#ifdef TAGS
+	if (valid_tag (&wpc->m_tag)) {
+	    mode |= MODE_VALID_TAG;
+
+	    if (valid_tag (&wpc->m_tag) == 'A')
+		mode |= MODE_APETAG;
+	}
+#endif
+    }
+
+    return mode;
+}
+
+int WavpackGetVersion (WavpackContext *wpc)
+{
+    if (wpc) {
+#ifdef VER3
+	if (wpc->stream3)
+	    return get_version3 (wpc);
+#endif
+	return 4;
+    }
+
+    return 0;
+}
+
+#endif
+
+#ifdef UNPACK
+
+// Unpack the specified number of samples from the current file position.
+// Note that "samples" here refers to "complete" samples, which would be
+// 2 longs for stereo files or even more for multichannel files, so the
+// required memory at "buffer" is 4 * samples * num_channels bytes. The
+// audio data is returned right-justified in 32-bit longs in the endian
+// mode native to the executing processor. So, if the original data was
+// 16-bit, then the values returned would be +/-32k. Floating point data
+// can also be returned if the source was floating point data (and this
+// can be optionally normalized to +/-1.0 by using the appropriate flag
+// in the call to WavpackOpenFileInput ()). The actual number of samples
+// unpacked is returned, which should be equal to the number requested unless
+// the end of fle is encountered or an error occurs.
+
+ulong WavpackUnpackSamples (WavpackContext *wpc, long *buffer, ulong samples)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream = 0];
+    ulong bcount, samples_unpacked = 0, samples_to_unpack;
+    int num_channels = wpc->config.num_channels;
+
+#ifdef VER3
+    if (wpc->stream3)
+	return unpack_samples3 (wpc, buffer, samples);
+#endif
+
+    while (samples) {
+	if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) ||
+	    wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples) {
+		free_streams (wpc);
+		wpc->filepos = wpc->reader->get_pos (wpc->wv_in);
+		bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr);
+
+		if (bcount == (ulong) -1)
+		    break;
+
+		wpc->filepos += bcount;
+		wps->blockbuff = malloc (wps->wphdr.ckSize + 8);
+		memcpy (wps->blockbuff, &wps->wphdr, 32);
+
+		if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) !=
+		    wps->wphdr.ckSize - 24) {
+			strcpy (wpc->error_message, "can't read all of WavPack file!");
+			break;
+		}
+
+		if (wps->wphdr.version < 0x402 || wps->wphdr.version > 0x40f) {
+		    strcpy (wpc->error_message, "not compatible with this version of WavPack file!");
+		    break;
+		}
+
+		if (wps->wphdr.block_samples && wpc->wvc_flag) {
+		    wpc->file2pos = wpc->reader->get_pos (wpc->wvc_in);
+		    bcount = read_next_header (wpc->reader, wpc->wvc_in, &wps->wphdr);
+
+		    if (bcount == (ulong) -1)
+			break;
+
+		    wpc->file2pos += bcount;
+		    wps->block2buff = malloc (wps->wphdr.ckSize + 8);
+		    memcpy (wps->block2buff, &wps->wphdr, 32);
+
+		    if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + 32, wps->wphdr.ckSize - 24) !=
+			wps->wphdr.ckSize - 24)
+			    break;
+		}
+
+		if (!wps->wphdr.block_samples || wps->sample_index == wps->wphdr.block_index)
+		    if (!unpack_init (wpc))
+			break;
+	}
+
+	if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) ||
+	    wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples)
+		continue;
+
+	if (wps->sample_index < wps->wphdr.block_index) {
+	    samples_to_unpack = wps->wphdr.block_index - wps->sample_index;
+
+	    if (samples_to_unpack > samples)
+		samples_to_unpack = samples;
+
+	    wps->sample_index += samples_to_unpack;
+	    samples_unpacked += samples_to_unpack;
+	    samples -= samples_to_unpack;
+
+	    if (wpc->reduced_channels)
+		samples_to_unpack *= wpc->reduced_channels;
+	    else
+		samples_to_unpack *= num_channels;
+
+	    while (samples_to_unpack--)
+		*buffer++ = 0;
+
+	    continue;
+	}
+
+	samples_to_unpack = wps->wphdr.block_index + wps->wphdr.block_samples - wps->sample_index;
+
+	if (samples_to_unpack > samples)
+	    samples_to_unpack = samples;
+
+	if (!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) {
+	    long *temp_buffer = malloc (samples_to_unpack * 8), *src, *dst;
+	    int offset = 0;
+	    ulong samcnt;
+
+	    while (1) {
+		if (wpc->current_stream == wpc->num_streams) {
+		    wps = wpc->streams [wpc->num_streams++] = malloc (sizeof (WavpackStream));
+		    CLEAR (*wps);
+		    bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr);
+
+		    if (bcount == (ulong) -1)
+			break;
+
+		    wps->blockbuff = malloc (wps->wphdr.ckSize + 8);
+		    memcpy (wps->blockbuff, &wps->wphdr, 32);
+
+		    if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) !=
+			wps->wphdr.ckSize - 24) {
+			    strcpy (wpc->error_message, "can't read all of WavPack file!");
+			    break;
+		    }
+
+		    if (wpc->wvc_flag) {
+			bcount = read_next_header (wpc->reader, wpc->wvc_in, &wps->wphdr);
+
+			if (bcount == (ulong) -1)
+			    break;
+
+			wps->block2buff = malloc (wps->wphdr.ckSize + 8);
+			memcpy (wps->block2buff, &wps->wphdr, 32);
+
+			if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + 32, wps->wphdr.ckSize - 24) !=
+			    wps->wphdr.ckSize - 24)
+				break;
+		    }
+
+		    if (!unpack_init (wpc))
+			break;
+		}
+		else
+		    wps = wpc->streams [wpc->current_stream];
+
+		unpack_samples (wpc, src = temp_buffer, samples_to_unpack);
+		samcnt = samples_to_unpack;
+		dst = buffer + offset;
+
+		if (wps->wphdr.flags & MONO_FLAG) {
+		    while (samcnt--) {
+			dst [0] = *src++;
+			dst += num_channels;
+		    }
+
+		    offset++;
+		}
+		else {
+		    while (samcnt--) {
+			dst [0] = *src++;
+			dst [1] = *src++;
+			dst += num_channels;
+		    }
+
+		    offset += 2;
+		}
+	
+		if (wps->wphdr.flags & FINAL_BLOCK)
+		    break;
+		else
+		    wpc->current_stream++;
+	    }
+
+	    wps = wpc->streams [wpc->current_stream = 0];
+	    free (temp_buffer);
+	}
+	else
+	    unpack_samples (wpc, buffer, samples_to_unpack);
+
+	if (wpc->reduced_channels)
+	    buffer += samples_to_unpack * wpc->reduced_channels;
+	else
+	    buffer += samples_to_unpack * num_channels;
+
+	samples_unpacked += samples_to_unpack;
+	samples -= samples_to_unpack;
+
+	if (wps->sample_index == wps->wphdr.block_index + wps->wphdr.block_samples) {
+	    if (check_crc_error (wpc) && wps->blockbuff) {
+
+		if (wpc->reader->can_seek (wpc->wv_in)) {
+		    long rseek = ((WavpackHeader *) wps->blockbuff)->ckSize / 3;
+		    wpc->reader->set_pos_rel (wpc->wv_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR);
+		}
+
+		if (wpc->wvc_flag && wps->block2buff && wpc->reader->can_seek (wpc->wvc_in)) {
+		    long rseek = ((WavpackHeader *) wps->block2buff)->ckSize / 3;
+		    wpc->reader->set_pos_rel (wpc->wvc_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR);
+		}
+
+		wpc->crc_errors++;
+	    }
+	}
+
+	if (wps->sample_index == wpc->total_samples)
+	    break;
+    }
+
+    return samples_unpacked;
+}
+
+#ifdef SEEKING
+
+static ulong find_sample (WavpackContext *wpc, void *infile, ulong header_pos, ulong sample);
+
+// Seek to the specifed sample index, returning TRUE on success. Note that
+// files generated with version 4.0 or newer will seek almost immediately.
+// Older files can take quite long if required to seek through unplayed
+// portions of the file, but will create a seek map so that reverse seeks
+// (or forward seeks to already scanned areas) will be very fast.
+
+int WavpackSeekSample (WavpackContext *wpc, ulong sample)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream = 0];
+    ulong bcount, samples_to_skip;
+    long *buffer;
+
+    if (wpc->total_samples == (ulong) -1 || sample >= wpc->total_samples ||
+	!wpc->reader->can_seek (wpc->wv_in) ||
+	(wpc->wvc_flag && !wpc->reader->can_seek (wpc->wvc_in)))
+	    return FALSE;
+
+#ifdef VER3
+    if (wpc->stream3)
+	return seek_sample3 (wpc, sample);
+#endif
+
+    if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || sample < wps->wphdr.block_index ||
+	sample >= wps->wphdr.block_index + wps->wphdr.block_samples) {
+
+	    free_streams (wpc);
+	    wpc->filepos = find_sample (wpc, wpc->wv_in, wpc->filepos, sample);
+
+	    if (wpc->filepos == (ulong) -1)
+		return FALSE;
+
+	    if (wpc->wvc_flag) {
+		wpc->file2pos = find_sample (wpc, wpc->wvc_in, 0, sample);
+
+		if (wpc->file2pos == (ulong) -1)
+		    return FALSE;
+	    }
+    }
+
+    if (!wps->blockbuff) {
+	wpc->reader->set_pos_abs (wpc->wv_in, wpc->filepos);
+	wpc->reader->read_bytes (wpc->wv_in, &wps->wphdr, sizeof (WavpackHeader));
+	little_endian_to_native (&wps->wphdr, WavpackHeaderFormat);
+	wps->blockbuff = malloc (wps->wphdr.ckSize + 8);
+	memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader));
+
+	if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) !=
+	    wps->wphdr.ckSize - 24) {
+		free_streams (wpc);
+		return FALSE;
+	}
+
+	if (wpc->wvc_flag) {
+	    wpc->reader->set_pos_abs (wpc->wvc_in, wpc->file2pos);
+	    wpc->reader->read_bytes (wpc->wvc_in, &wps->wphdr, sizeof (WavpackHeader));
+	    little_endian_to_native (&wps->wphdr, WavpackHeaderFormat);
+	    wps->block2buff = malloc (wps->wphdr.ckSize + 8);
+	    memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader));
+
+	    if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) !=
+		wps->wphdr.ckSize - 24) {
+		    free_streams (wpc);
+		    return FALSE;
+	    }
+	}
+
+	if (!unpack_init (wpc)) {
+	    free_streams (wpc);
+	    return FALSE;
+	}
+    }
+
+    while (!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) {
+	if (++wpc->current_stream == wpc->num_streams) {
+	    wps = wpc->streams [wpc->num_streams++] = malloc (sizeof (WavpackStream));
+	    CLEAR (*wps);
+	    bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr);
+
+	    if (bcount == (ulong) -1)
+		break;
+
+	    wps->blockbuff = malloc (wps->wphdr.ckSize + 8);
+	    memcpy (wps->blockbuff, &wps->wphdr, 32);
+
+	    if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) !=
+		wps->wphdr.ckSize - 24) {
+		    strcpy (wpc->error_message, "can't read all of WavPack file!");
+		    break;
+	    }
+
+	    if (wpc->wvc_flag) {
+		bcount = read_next_header (wpc->reader, wpc->wvc_in, &wps->wphdr);
+
+		if (bcount == (ulong) -1)
+		    break;
+
+		wps->block2buff = malloc (wps->wphdr.ckSize + 8);
+		memcpy (wps->block2buff, &wps->wphdr, 32);
+
+		if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + 32, wps->wphdr.ckSize - 24) !=
+		    wps->wphdr.ckSize - 24)
+			break;
+	    }
+
+	    if (!unpack_init (wpc))
+		break;
+	}
+	else
+	    wps = wpc->streams [wpc->current_stream];
+    }
+
+    if (sample < wps->sample_index)
+	for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++)
+	    if (!unpack_init (wpc))
+		return FALSE;
+
+    samples_to_skip = sample - wps->sample_index;
+
+    if (samples_to_skip) {
+	buffer = malloc (samples_to_skip * 8);
+
+	for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++)
+	    unpack_samples (wpc, buffer, samples_to_skip);
+
+	free (buffer);
+    }
+
+    wpc->current_stream = 0;
+    return TRUE;
+}
+
+#endif
+
+#ifdef TAGS
+
+// Attempt to get the specified item from the specified ID3v1 or APEv2 tag.
+// The "size" parameter specifies the amount of space available at "value",
+// if the desired item will not fit in this space then ellipses (...) will
+// be appended and the string terminated. Only text data are supported.
+
+static void tagcpy (char *dest, char *src, int tag_size);
+
+int WavpackGetTagItem (WavpackContext *wpc, const char *item, char *value, int size)
+{
+    M_Tag *m_tag = &wpc->m_tag;
+    char lvalue [1024];
+
+    lvalue [0] = 0;
+
+    if (value)
+	*value = 0;
+
+    if (m_tag->ape_tag_hdr.ID [0] == 'A') {
+	char *p = m_tag->ape_tag_data;
+	char *q = p + m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr);
+	int i;
+
+	for (i = 0; i < m_tag->ape_tag_hdr.item_count; ++i) {
+	    int vsize = * ((long *) p)++;
+	    int flags = * ((long *) p)++;
+	    int isize = strlen (p);
+
+	    little_endian_to_native (&vsize, "L");
+	    little_endian_to_native (&flags, "L");
+
+	    if (p + isize + vsize + 1 > q)
+		break;
+
+	    if (isize && vsize && vsize < sizeof (lvalue) && !stricmp (item, p) && !(flags & 6)) {
+		strncpy (lvalue, p + isize + 1, vsize);
+		lvalue [vsize] = 0;
+		break;
+	    }
+	    else
+		p += isize + vsize + 1;
+	}
+    }
+    else if (m_tag->id3_tag.tag_id [0] == 'T') {
+	if (!stricmp (item, "title"))
+	    tagcpy (lvalue, m_tag->id3_tag.title, sizeof (m_tag->id3_tag.title));
+	else if (!stricmp (item, "artist"))
+	    tagcpy (lvalue, m_tag->id3_tag.artist, sizeof (m_tag->id3_tag.artist));
+	else if (!stricmp (item, "album"))
+	    tagcpy (lvalue, m_tag->id3_tag.album, sizeof (m_tag->id3_tag.album));
+	else if (!stricmp (item, "year"))
+	    tagcpy (lvalue, m_tag->id3_tag.year, sizeof (m_tag->id3_tag.year));
+	else if (!stricmp (item, "comment"))
+	    tagcpy (lvalue, m_tag->id3_tag.comment, sizeof (m_tag->id3_tag.title));
+    }
+    else
+	return FALSE;
+
+    if (lvalue [0]) {
+	if (value && size >= 4) {
+	    if (strlen (lvalue) >= size) {
+		lvalue [size - 4] = lvalue [size - 3] = lvalue [size - 2] = '.';
+		lvalue [size - 1] = 0;
+	    }
+
+	    strcpy (value, lvalue);
+	}
+
+	return TRUE;
+    }
+    else
+	return FALSE;
+}
+
+#endif
+
+#endif
+
+#ifdef PACK
+
+// Open context for writing WavPack files. The returned context pointer is used
+// in all following calls to the library. The "blockout" function will be used
+// to store the actual completed WavPack blocks and will be called with the id
+// pointers containing user defined data (one for the wv file and one for the
+// wvc file). A return value of NULL indicates that memory could not be
+// allocated for the context.
+
+WavpackContext *WavpackOpenFileOutput (blockout blockout, void *wv_id, void *wvc_id)
+{
+    WavpackContext *wpc = malloc (sizeof (WavpackContext));
+
+    if (!wpc)
+	return NULL;
+
+    CLEAR (*wpc);
+    wpc->blockout = blockout;
+    wpc->wv_out = wv_id;
+    wpc->wvc_out = wvc_id;
+    return wpc;
+}
+
+// Set configuration for writing WavPack files. This must be done before
+// sending any actual samples, however it is okay to send wrapper or other
+// metadata before calling this. The "config" structure contains the following
+// required information:
+
+// config->bytes_per_sample     see WavpackGetBytesPerSample() for info
+// config->bits_per_sample      see WavpackGetBitsPerSample() for info
+// config->channel_mask         Microsoft standard (mono = 4, stereo = 3)
+// config->num_channels         self evident
+// config->sample_rate          self evident
+
+// In addition, the following fields and flags may be set: 
+
+// config->flags:
+// --------------
+// o CONFIG_HYBRID_FLAG         select hybrid mode (must set bitrate)
+// o CONFIG_JOINT_STEREO        select joint stereo (must set override also)
+// o CONFIG_JOINT_OVERRIDE      override default joint stereo selection
+// o CONFIG_HYBRID_SHAPE        select hybrid noise shaping (set override &
+//                                                      shaping_weight != 0.0)
+// o CONFIG_SHAPE_OVERRIDE      override default hybrid noise shaping
+//                               (set CONFIG_HYBRID_SHAPE and shaping_weight)
+// o CONFIG_FAST_FLAG           "fast" compression mode
+// o CONFIG_HIGH_FLAG           "high" compression mode
+// o CONFIG_BITRATE_KBPS        hybrid bitrate is kbps, not bits / sample
+// o CONFIG_CREATE_WVC          create correction file
+// o CONFIG_OPTIMIZE_WVC        maximize bybrid compression (-cc option)
+// o CONFIG_CALC_NOISE          calc noise in hybrid mode
+// o CONFIG_EXTRA_MODE          extra processing mode (slow!)
+// o CONFIG_SKIP_WVX		no wvx stream for floats & large ints
+
+// config->bitrate              hybrid bitrate in either bits/sample or kbps
+// config->shaping_weight       hybrid noise shaping coefficient override
+// config->block_samples        force samples per WavPack block (0 = use deflt)
+// config->float_norm_exp       select floating-point data (127 for +/-1.0)
+// config->xmode                extra mode processing value override
+
+// If the number of samples to be written is known then it should be passed
+// here. If the duration is not known then pass -1. In the case that the size
+// is not known (or the writing is terminated early) then it is suggested that
+// the application retrieve the first block written and let the library update
+// the total samples indication. A function is provided to do this update and
+// it should be done to the "correction" file also. If this cannot be done
+// (because a pipe is being used, for instance) then a valid WavPack will still
+// be created, but when applications want to access that file they will have
+// to seek all the way to the end to determine the actual duration. Also, if
+// a RIFF header has been included then it should be updated as well or the
+// WavPack file will not be directly unpackable to a valid wav file (although
+// it will still be usable by itself). A return of FALSE indicates an error.
+
+static const ulong xtable [] = { 123, 3, 27, 59, 123, 187, 251 };
+static const ulong f_xtable [] = { 251, 3, 27, 59, 123, 187, 251 };
+static const ulong h_xtable [] = { 91, 3, 27, 91, 123, 187, 251 };
+
+int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, ulong total_samples)
+{
+    ulong flags = (config->bytes_per_sample - 1), bps = 0, shift = 0;
+    ulong chan_mask = config->channel_mask;
+    int num_chans = config->num_channels;
+    int i;
+
+    wpc->total_samples = total_samples;
+    wpc->config.sample_rate = config->sample_rate;
+    wpc->config.num_channels = config->num_channels;
+    wpc->config.channel_mask = config->channel_mask;
+    wpc->config.bits_per_sample = config->bits_per_sample;
+    wpc->config.bytes_per_sample = config->bytes_per_sample;
+    wpc->config.block_samples = config->block_samples;
+    wpc->config.flags = config->flags;
+
+    if (config->float_norm_exp) {
+	wpc->config.float_norm_exp = config->float_norm_exp;
+	flags |= FLOAT_DATA;    
+    }
+    else
+	shift = (config->bytes_per_sample * 8) - config->bits_per_sample;
+
+    for (i = 0; i < 15; ++i)
+	if (wpc->config.sample_rate == sample_rates [i])
+	    break;
+
+    flags |= i << SRATE_LSB;
+    flags |= shift << SHIFT_LSB;
+
+    if (config->flags & CONFIG_HYBRID_FLAG) {
+	flags |= HYBRID_FLAG | HYBRID_BITRATE | HYBRID_BALANCE;
+
+	if (!(wpc->config.flags & CONFIG_SHAPE_OVERRIDE)) {
+	    wpc->config.flags |= CONFIG_HYBRID_SHAPE | CONFIG_AUTO_SHAPING;
+	    flags |= HYBRID_SHAPE | NEW_SHAPING;
+	}
+	else if (wpc->config.flags & CONFIG_HYBRID_SHAPE) {
+	    wpc->config.shaping_weight = config->shaping_weight;
+	    flags |= HYBRID_SHAPE | NEW_SHAPING;
+	}
+
+	if (wpc->config.flags & CONFIG_OPTIMIZE_WVC)
+	    flags |= CROSS_DECORR;
+
+	if (config->flags & CONFIG_BITRATE_KBPS) {
+	    bps = floor (config->bitrate * 256000.0 / config->sample_rate / config->num_channels + 0.5);
+
+	    if (bps > (64 << 8))
+		bps = 64 << 8;
+	}
+	else
+	    bps = floor (config->bitrate * 256.0 + 0.5);
+    }
+    else
+	flags |= CROSS_DECORR;
+
+    if (!(config->flags & CONFIG_JOINT_OVERRIDE) || (config->flags & CONFIG_JOINT_STEREO))
+	flags |= JOINT_STEREO;
+
+    if (config->flags & CONFIG_CREATE_WVC)
+	wpc->wvc_flag = TRUE;
+
+    for (wpc->current_stream = 0; num_chans; wpc->current_stream++) {
+	WavpackStream *wps = malloc (sizeof (WavpackStream));
+	ulong stereo_mask, mono_mask;
+	int pos, chans = 0;
+
+	wpc->streams [wpc->current_stream] = wps;
+	CLEAR (*wps);
+
+	for (pos = 1; pos <= 18; ++pos) {
+	    stereo_mask = 3 << (pos - 1);
+	    mono_mask = 1 << (pos - 1);
+
+	    if ((chan_mask & stereo_mask) == stereo_mask && (mono_mask & 0x251)) {
+		chan_mask &= ~stereo_mask;
+		chans = 2;
+		break;
+	    }
+	    else if (chan_mask & mono_mask) {
+		chan_mask &= ~mono_mask;
+		chans = 1;
+		break;
+	    }
+	}
+
+	if (pos == 19)
+	    chans = num_chans > 1 ? 2 : 1;
+
+	num_chans -= chans;
+
+	if (num_chans && wpc->current_stream == MAX_STREAMS - 1)
+	    break;
+
+	memcpy (wps->wphdr.ckID, "wvpk", 4);
+	wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
+	wps->wphdr.total_samples = wpc->total_samples;
+	wps->wphdr.version = 0x403;
+	wps->wphdr.flags = flags;
+	wps->bits = bps;
+
+	if (!wpc->current_stream)
+	    wps->wphdr.flags |= INITIAL_BLOCK;
+
+	if (!num_chans)
+	    wps->wphdr.flags |= FINAL_BLOCK;
+
+	if (chans == 1) {
+	    wps->wphdr.flags &= ~(JOINT_STEREO | CROSS_DECORR | HYBRID_BALANCE);
+	    wps->wphdr.flags |= MONO_FLAG;
+	}
+    }
+
+    wpc->num_streams = wpc->current_stream;
+    wpc->current_stream = 0;
+
+    if (num_chans) {
+	strcpy (wpc->error_message, "too many channels!");
+	return FALSE;
+    }
+
+    if (config->flags & CONFIG_EXTRA_MODE) {
+
+	if (config->flags & CONFIG_HIGH_FLAG)
+	    wpc->config.extra_flags = h_xtable [config->xmode];
+	else if (config->flags & CONFIG_FAST_FLAG)
+	    wpc->config.extra_flags = f_xtable [config->xmode];
+	else
+	    wpc->config.extra_flags = xtable [config->xmode];
+
+	if (config->flags & CONFIG_JOINT_OVERRIDE)
+	    wpc->config.extra_flags &= ~EXTRA_STEREO_MODES;
+    }
+
+    return TRUE;
+}
+
+// Prepare to actually pack samples by determining the size of the WavPack
+// blocks and allocating sample buffers and initializing each stream. Call
+// after WavpackSetConfiguration() and before WavpackPackSamples(). A return
+// of FALSE indicates an error.
+
+int WavpackPackInit (WavpackContext *wpc)
+{
+    if (wpc->metabytes > 4096)
+	write_metadata_block (wpc);
+
+    if (wpc->config.block_samples)
+	wpc->block_samples = wpc->config.block_samples;
+    else {
+	if (wpc->config.flags & CONFIG_HIGH_FLAG)
+	    wpc->block_samples = wpc->config.sample_rate;
+//	else if ((wpc->config.flags & CONFIG_FAST_FLAG) && !(wpc->config.sample_rate % 4))
+//	    wpc->block_samples = wpc->config.sample_rate / 4;
+	else if (!(wpc->config.sample_rate % 2))
+	    wpc->block_samples = wpc->config.sample_rate / 2;
+	else
+	    wpc->block_samples = wpc->config.sample_rate;
+
+	while (wpc->block_samples * wpc->config.num_channels > 100000)
+	    wpc->block_samples /= 2;
+
+	while (wpc->block_samples * wpc->config.num_channels < 40000)
+	    wpc->block_samples *= 2;
+    }
+
+    wpc->max_samples = wpc->block_samples + (wpc->block_samples >> 1);
+
+    for (wpc->current_stream = 0; wpc->streams [wpc->current_stream]; wpc->current_stream++) {
+	WavpackStream *wps = wpc->streams [wpc->current_stream];
+
+	wps->sample_buffer = malloc (wpc->max_samples * (wps->wphdr.flags & MONO_FLAG ? 4 : 8));
+	pack_init (wpc);
+    }
+
+    return TRUE;
+}
+
+// Pack the specified samples. Samples must be stored in longs in the native
+// endian format of the executing processor. The number of samples specified
+// indicates composite samples (sometimes called "frames"). So, the actual
+// number of data points would be this "sample_count" times the number of
+// channels. Note that samples are accumulated here until enough exist to
+// create a complete WavPack block (or several blocks for multichannel audio).
+// If an application wants to break a block at a specific sample, then it must
+// simply call WavpackFlushSamples() to force an early termination. Completed
+// WavPack blocks are send to the function provided in the initial call to
+// WavpackOpenFileOutput(). A return of FALSE indicates an error.
+
+static int pack_streams (WavpackContext *wpc, ulong block_samples);
+
+int WavpackPackSamples (WavpackContext *wpc, long *sample_buffer, ulong sample_count)
+{
+    int nch = wpc->config.num_channels;
+
+    while (sample_count) {
+	long *source_pointer = sample_buffer;
+	uint samples_to_copy;
+
+	if (wpc->acc_samples + sample_count > wpc->max_samples)
+	    samples_to_copy = wpc->max_samples - wpc->acc_samples;
+	else
+	    samples_to_copy = sample_count;
+   
+	for (wpc->current_stream = 0; wpc->streams [wpc->current_stream]; wpc->current_stream++) {
+	    WavpackStream *wps = wpc->streams [wpc->current_stream];
+	    long *dptr, *sptr, cnt;
+
+	    dptr = wps->sample_buffer + wpc->acc_samples * (wps->wphdr.flags & MONO_FLAG ? 1 : 2);
+	    sptr = source_pointer;
+	    cnt = samples_to_copy;
+
+	    if (wps->wphdr.flags & MONO_FLAG) {
+		while (cnt--) {
+		    *dptr++ = *sptr;
+		    sptr += nch;
+		}
+
+		source_pointer++;
+	    }
+	    else {
+		while (cnt--) {
+		    *dptr++ = sptr [0];
+		    *dptr++ = sptr [1];
+		    sptr += nch;
+		}
+
+		source_pointer += 2;
+	    }
+	}
+
+	sample_buffer += samples_to_copy * nch;
+	sample_count -= samples_to_copy;
+
+	if ((wpc->acc_samples += samples_to_copy) == wpc->max_samples &&
+	    !pack_streams (wpc, wpc->block_samples))
+		return FALSE;
+    }
+
+    return TRUE;
+}
+
+// Flush all accumulated samples into WavPack blocks. This is normally called
+// after all samples have been sent to WavpackPackSamples(), but can also be
+// called to terminate a WavPack block at a specific sample (in other words it
+// is possible to continue after this operation). This is also called to
+// dump non-audio blocks like those holding metadata for various purposes.
+// A return of FALSE indicates an error.
+
+int WavpackFlushSamples (WavpackContext *wpc)
+{
+    while (wpc->acc_samples) {
+	ulong block_samples;
+
+	if (wpc->acc_samples > wpc->block_samples)
+	    block_samples = wpc->acc_samples / 2;
+	else
+	    block_samples = wpc->acc_samples;
+
+	if (!pack_streams (wpc, block_samples))
+	    return FALSE;
+    }
+
+    if (wpc->metacount)
+	write_metadata_block (wpc);
+
+    return TRUE;
+}
+
+// Add wrapper (currently RIFF only) to WavPack blocks. This should be called
+// before sending any audio samples for the RIFF header or after all samples
+// have been sent for any RIFF trailer. WavpackFlushSamples() should be called
+// between sending the last samples and calling this for trailer data to make
+// sure that headers and trailers don't get mixed up in very short files. If
+// the exact contents of the RIFF header are not known because, for example,
+// the file duration is uncertain or trailing chunks are possible, simply write
+// a "dummy" header of the correct length. When all data has been written it
+// will be possible to read the first block written and update the header
+// directly. An example of this can be found in the Audition filter. A
+// return of FALSE indicates an error.
+
+int WavpackAddWrapper (WavpackContext *wpc, void *data, ulong bcount)
+{
+    ulong index = WavpackGetSampleIndex (wpc);
+
+    return add_to_metadata (wpc, data, bcount,
+	(uchar)((!index || index == (ulong) -1) ? ID_RIFF_HEADER : ID_RIFF_TRAILER));
+}
+
+// Store computed MD5 sum in WavPack metadata. Note that the user must compute
+// the 16 byte sum; it is not done here. A return of FALSE indicates an error.
+
+int WavpackStoreMD5Sum (WavpackContext *wpc, uchar data [16])
+{
+    return add_to_metadata (wpc, data, 16, ID_MD5_CHECKSUM);
+}
+
+static int pack_streams (WavpackContext *wpc, ulong block_samples)
+{
+    ulong max_blocksize = block_samples * 10 + 4096, bcount;
+    uchar *outbuff, *outend, *out2buff, *out2end;
+    int result = 0;
+
+    out2buff = (wpc->wvc_flag) ? malloc (max_blocksize) : NULL;
+    out2end = out2buff + max_blocksize;
+    outbuff = malloc (max_blocksize);
+    outend = outbuff + max_blocksize;
+
+    for (wpc->current_stream = 0; wpc->streams [wpc->current_stream]; wpc->current_stream++) {
+	WavpackStream *wps = wpc->streams [wpc->current_stream];
+	ulong flags = wps->wphdr.flags;
+
+	flags &= ~MAG_MASK;
+	flags += (1 << MAG_LSB) * ((flags & BYTES_STORED) * 8 + 7);
+
+	wps->wphdr.block_index = wps->sample_index;
+	wps->wphdr.block_samples = block_samples;
+	wps->wphdr.flags = flags;
+	wps->block2buff = out2buff;
+	wps->block2end = out2end;
+	wps->blockbuff = outbuff;
+	wps->blockend = outend;
+
+	result = pack_block (wpc, wps->sample_buffer);
+	wps->blockbuff = wps->block2buff = NULL;
+
+	if (!result) {
+	    strcpy (wpc->error_message, "output buffer overflowed!");
+	    break;
+	}
+
+	bcount = ((WavpackHeader *) outbuff)->ckSize + 8;
+	native_to_little_endian ((WavpackHeader *) outbuff, WavpackHeaderFormat);
+	result = wpc->blockout (wpc->wv_out, outbuff, bcount);
+
+	if (!result) {
+	    strcpy (wpc->error_message, "can't write WavPack data, disk probably full!");
+	    break;
+	}
+
+	wpc->filelen += bcount;
+
+	if (out2buff) {
+	    bcount = ((WavpackHeader *) out2buff)->ckSize + 8;
+	    native_to_little_endian ((WavpackHeader *) out2buff, WavpackHeaderFormat);
+	    result = wpc->blockout (wpc->wvc_out, out2buff, bcount);
+
+	    if (!result) {
+		strcpy (wpc->error_message, "can't write WavPack data, disk probably full!");
+		break;
+	    }
+
+	    wpc->file2len += bcount;
+	}
+
+	if (wpc->acc_samples != block_samples)
+	    memcpy (wps->sample_buffer, wps->sample_buffer + block_samples * (flags & MONO_FLAG ? 1 : 2),
+		(wpc->acc_samples - block_samples) * sizeof (long) * (flags & MONO_FLAG ? 1 : 2));
+    }
+
+    wpc->current_stream = 0;
+    wpc->acc_samples -= block_samples;
+    free (outbuff);
+
+    if (out2buff)
+	free (out2buff);
+
+    return result;
+}
+
+// Given the pointer to the first block written (to either a .wv or .wvc file),
+// update the block with the actual number of samples written. This should
+// be done if WavpackSetConfiguration() was called with an incorrect number
+// of samples (or -1). It is the responsibility of the application to read and
+// rewrite the block. An example of this can be found in the Audition filter.
+
+void WavpackUpdateNumSamples (WavpackContext *wpc, void *first_block)
+{
+    ((WavpackHeader *) first_block)->total_samples = WavpackGetSampleIndex (wpc);
+}
+
+// Given the pointer to the first block written to a WavPack file, this
+// function returns the location of the stored RIFF header that was originally
+// written with WavpackAddWrapper(). This would normally be used to update
+// the wav header to indicate that a different number of samples was actually
+// written or if additional RIFF chunks are written at the end of the file.
+// It is the responsibility of the application to read and rewrite the block.
+// An example of this can be found in the Audition filter.
+
+void *WavpackGetWrapperLocation (void *first_block)
+{
+    if (((uchar *) first_block) [32] == ID_RIFF_HEADER)
+	return ((uchar *) first_block) + 34;
+    else
+	return NULL;
+}
+
+#endif
+
+// Get total number of samples contained in the WavPack file, or -1 if unknown
+
+ulong WavpackGetNumSamples (WavpackContext *wpc)
+{
+    return wpc ? wpc->total_samples : (ulong) -1;
+}
+
+// Get the current sample index position, or -1 if unknown
+
+ulong WavpackGetSampleIndex (WavpackContext *wpc)
+{
+    if (wpc) {
+#ifdef VER3
+	if (wpc->stream3)
+	    return get_sample_index3 (wpc);
+	else if (wpc->streams [0])
+	    return wpc->streams [0]->sample_index;
+#else
+	if (wpc->streams [0])
+	    return wpc->streams [0]->sample_index;
+#endif
+    }
+
+    return (ulong) -1;
+}
+
+// Get the number of errors encountered so far
+
+int WavpackGetNumErrors (WavpackContext *wpc)
+{
+    return wpc ? wpc->crc_errors : 0;
+}
+
+// return TRUE if any uncorrected lossy blocks were actually written or read
+
+int WavpackLossyBlocks (WavpackContext *wpc)
+{
+    return wpc ? wpc->lossy_blocks : 0;
+}
+
+// Calculate the progress through the file as a double from 0.0 (for begin)
+// to 1.0 (for done). A return value of -1.0 indicates that the progress is
+// unknown.
+
+double WavpackGetProgress (WavpackContext *wpc)
+{
+    if (wpc && wpc->total_samples != (ulong) -1 && wpc->total_samples != 0)
+	return (double) WavpackGetSampleIndex (wpc) / wpc->total_samples;
+    else
+	return -1.0;
+}
+
+// Return the total size of the WavPack file(s) in bytes.
+
+ulong WavpackGetFileSize (WavpackContext *wpc)
+{
+    return wpc ? wpc->filelen + wpc->file2len : 0;
+}
+
+// Calculate the ratio of the specified WavPack file size to the size of the
+// original audio data as a double greater than 0.0 and (usually) smaller than
+// 1.0. A value greater than 1.0 represents "negative" compression and a
+// return value of 0.0 indicates that the ratio cannot be determined.
+
+double WavpackGetRatio (WavpackContext *wpc)
+{
+    if (wpc && wpc->total_samples != (ulong) -1 && wpc->filelen) {
+	double output_size = (double) wpc->total_samples * wpc->config.num_channels *
+	    wpc->config.bytes_per_sample;
+	double input_size = (double) wpc->filelen + wpc->file2len;
+
+	if (output_size >= 1.0 && input_size >= 1.0)
+	    return input_size / output_size;
+    }
+
+    return 0.0;
+}
+
+// Calculate the average bitrate of the WavPack file in bits per second. A
+// return of 0.0 indicates that the bitrate cannot be determined. An option is
+// provided to use (or not use) any attendant .wvc file.
+
+double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc)
+{
+    if (wpc && wpc->total_samples != (ulong) -1 && wpc->filelen) {
+	double output_time = (double) wpc->total_samples / wpc->config.sample_rate;
+	double input_size = (double) wpc->filelen + (count_wvc ? wpc->file2len : 0);
+
+	if (output_time >= 1.0 && input_size >= 1.0)
+	    return input_size * 8.0 / output_time;
+    }
+
+    return 0.0;
+}
+
+#ifdef UNPACK
+
+// Calculate the bitrate of the current WavPack file block in bits per second.
+// This can be used for an "instant" bit display and gets updated from about
+// 1 to 4 times per second. A return of 0.0 indicates that the bitrate cannot
+// be determined.
+
+double WavpackGetInstantBitrate (WavpackContext *wpc)
+{
+    if (wpc->stream3)
+	return WavpackGetAverageBitrate (wpc, TRUE);
+
+    if (wpc && wpc->streams [0] && wpc->streams [0]->wphdr.block_samples) {
+	double output_time = (double) wpc->streams [0]->wphdr.block_samples / wpc->config.sample_rate;
+	double input_size = 0;
+	int si;
+
+	for (si = 0; si < wpc->num_streams; ++si) {
+	    if (wpc->streams [si]->blockbuff)
+		input_size += ((WavpackHeader *) wpc->streams [si]->blockbuff)->ckSize;
+
+	    if (wpc->streams [si]->block2buff)
+		input_size += ((WavpackHeader *) wpc->streams [si]->block2buff)->ckSize;
+	}
+
+	if (output_time > 0.0 && input_size >= 1.0)
+	    return input_size * 8.0 / output_time;
+    }
+
+    return 0.0;
+}
+
+#endif
+
+// Close the specified WavPack file and release all resources used by it.
+// Returns NULL.
+
+WavpackContext *WavpackCloseFile (WavpackContext *wpc)
+{
+    free_streams (wpc);
+
+    if (wpc->streams [0])
+	free (wpc->streams [0]);
+
+#ifdef VER3
+    if (wpc->stream3)
+	free_stream3 (wpc);
+#endif
+
+#ifdef UNPACK
+    if (wpc->close_files) {
+#ifdef USE_FSTREAMS
+	if (wpc->wv_in != NULL)
+	    fclose (wpc->wv_in);
+
+	if (wpc->wvc_in != NULL)
+	    fclose (wpc->wvc_in);
+#endif
+    }
+
+    WavpackFreeWrapper (wpc);
+#endif
+
+#ifdef TAGS
+    free_tag (&wpc->m_tag);
+#endif
+
+    free (wpc);
+
+    return NULL;
+}
+
+// Returns the sample rate of the specified WavPack file
+
+ulong WavpackGetSampleRate (WavpackContext *wpc)
+{
+    return wpc ? wpc->config.sample_rate : 44100;
+}
+
+// Returns the number of channels of the specified WavPack file. Note that
+// this is the actual number of channels contained in the file even if the
+// OPEN_2CH_MAX flag was specified when the file was opened.
+
+int WavpackGetNumChannels (WavpackContext *wpc)
+{
+    return wpc ? wpc->config.num_channels : 2;
+}
+
+int WavpackGetFloatNormExp (WavpackContext *wpc)
+{
+    return wpc->config.float_norm_exp;
+}
+
+// Returns the actual number of valid bits per sample contained in the
+// original file, which may or may not be a multiple of 8. Floating data
+// always has 32 bits, integers may be from 1 to 32 bits each. When this
+// value is not a multiple of 8, then the "extra" bits are located in the
+// LSBs of the results. That is, values are right justified when unpacked
+// into longs, but are left justified in the number of bytes used by the
+// original data.
+
+int WavpackGetBitsPerSample (WavpackContext *wpc)
+{
+    return wpc ? wpc->config.bits_per_sample : 16;
+}
+
+// Returns the number of bytes used for each sample (1 to 4) in the original
+// file. This is required information for the user of this module because the
+// audio data is returned in the LOWER bytes of the long buffer and must be
+// left-shifted 8, 16, or 24 bits if normalized longs are required.
+
+int WavpackGetBytesPerSample (WavpackContext *wpc)
+{
+    return wpc ? wpc->config.bytes_per_sample : 2;
+}
+
+#if defined(UNPACK) || defined(INFO_ONLY)
+
+// If the OPEN_2CH_MAX flag is specified when opening the file, this function
+// will return the actual number of channels decoded from the file (which may
+// or may not be less than the actual number of channels, but will always be
+// 1 or 2). Normally, this will be the front left and right channels of a
+// multichannel file.
+
+int WavpackGetReducedChannels (WavpackContext *wpc)
+{
+    if (wpc)
+	return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels;
+    else
+	return 2;
+}
+
+// These routines are used to access (and free) header and trailer data that
+// was retrieved from the Wavpack file. The header will be available before
+// the samples are decoded and the trailer will be available after all samples
+// have been read.
+
+ulong WavpackGetWrapperBytes (WavpackContext *wpc)
+{
+    return wpc ? wpc->wrapper_bytes : 0;
+}
+
+uchar *WavpackGetWrapperData (WavpackContext *wpc)
+{
+    return wpc ? wpc->wrapper_data : NULL;
+}
+
+void WavpackFreeWrapper (WavpackContext *wpc)
+{
+    if (wpc && wpc->wrapper_data) {
+	free (wpc->wrapper_data);
+	wpc->wrapper_data = NULL;
+	wpc->wrapper_bytes = 0;
+    }
+}
+
+// Get any MD5 checksum stored in the metadata (should be called after reading
+// last sample or an extra seek will occur). A return value of FALSE indicates
+// that no MD5 checksum was stored.
+
+static int seek_md5 (stream_reader *reader, void *id, uchar data [16]);
+
+int WavpackGetMD5Sum (WavpackContext *wpc, uchar data [16])
+{
+    if (wpc->config.flags & CONFIG_MD5_CHECKSUM) {
+	if (wpc->config.md5_read) {
+	    memcpy (data, wpc->config.md5_checksum, 16);
+	    return TRUE;
+	}
+	else if (wpc->reader->can_seek (wpc->wv_in)) {
+	    ulong pos_save = wpc->reader->get_pos (wpc->wv_in);
+
+	    wpc->config.md5_read = seek_md5 (wpc->reader, wpc->wv_in, wpc->config.md5_checksum);
+	    wpc->reader->set_pos_abs (wpc->wv_in, pos_save);
+
+	    if (wpc->config.md5_read) {
+		memcpy (data, wpc->config.md5_checksum, 16);
+		return TRUE;
+	    }
+	    else
+		return FALSE;
+	}
+    }
+
+    return FALSE;
+}
+
+#endif
+
+// Free all memory allocated for raw WavPack blocks (for all allocated streams)
+// and free all additonal streams. This does not free the default stream ([0])
+// which is always kept around.
+
+static void free_streams (WavpackContext *wpc)
+{
+    int si = wpc->num_streams;
+
+    while (si--) {
+	if (wpc->streams [si]->blockbuff) {
+	    free (wpc->streams [si]->blockbuff);
+	    wpc->streams [si]->blockbuff = NULL;
+	}
+
+	if (wpc->streams [si]->block2buff) {
+	    free (wpc->streams [si]->block2buff);
+	    wpc->streams [si]->block2buff = NULL;
+	}
+
+	if (wpc->streams [si]->sample_buffer) {
+	    free (wpc->streams [si]->sample_buffer);
+	    wpc->streams [si]->sample_buffer = NULL;
+	}
+
+	if (si) {
+	    wpc->num_streams--;
+	    free (wpc->streams [si]);
+	    wpc->streams [si] = NULL;
+	}
+    }
+
+    wpc->current_stream = 0;
+}
+
+#if defined(UNPACK) || defined(INFO_ONLY)
+
+// 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 (stream_reader *reader, void *id, 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 (reader->read_bytes (id, buffer + bleft, sizeof (*wphdr) - bleft) != 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;
+    }
+}
+
+// This function is used to seek to end of a file to determine its actual
+// length in samples by reading the last header block containing data.
+// Currently, all WavPack files contain the sample length in the first block
+// containing samples, however this might not always be the case. Obviously,
+// this function requires a seekable file or stream and leaves the file
+// pointer undefined. A return value of -1 indicates the length could not
+// be determined.
+
+static ulong seek_final_index (stream_reader *reader, void *id)
+{
+    ulong result = (ulong) -1, bcount;
+    WavpackHeader wphdr;
+
+    if (reader->get_length (id) > 1200000L)
+	reader->set_pos_rel (id, -1048576L, SEEK_END);
+
+    while (1) {
+	bcount = read_next_header (reader, id, &wphdr);
+
+	if (bcount == (ulong) -1)
+	    return result;
+
+	if (wphdr.block_samples && (wphdr.flags & FINAL_BLOCK))
+	    result = wphdr.block_index + wphdr.block_samples;
+
+	if (wphdr.ckSize > sizeof (WavpackHeader) - 8)
+	    reader->set_pos_rel (id, wphdr.ckSize - sizeof (WavpackHeader) + 8, SEEK_CUR);
+    }
+}
+
+static int seek_md5 (stream_reader *reader, void *id, uchar data [16])
+{
+    uchar meta_id, c1, c2;
+    ulong bcount, meta_bc;
+    WavpackHeader wphdr;
+
+    if (reader->get_length (id) > 1200000L)
+	reader->set_pos_rel (id, -1048576L, SEEK_END);
+
+    while (1) {
+	bcount = read_next_header (reader, id, &wphdr);
+
+	if (bcount == (ulong) -1)
+	    return FALSE;
+
+	bcount = wphdr.ckSize - sizeof (WavpackHeader) + 8;
+
+	while (bcount >= 2) {
+	    if (reader->read_bytes (id, &meta_id, 1) != 1 ||
+		reader->read_bytes (id, &c1, 1) != 1)
+		    return FALSE;
+
+	    meta_bc = c1 << 1;
+	    bcount -= 2;
+
+	    if (meta_id & ID_LARGE) {
+		if (bcount < 2 || reader->read_bytes (id, &c1, 1) != 1 ||
+		    reader->read_bytes (id, &c2, 1) != 1)
+			return FALSE;
+
+		meta_bc += ((ulong) c1 << 9) + ((ulong) c2 << 17);
+		bcount -= 2;
+	    }
+
+	    if (meta_id == ID_MD5_CHECKSUM)
+		return (meta_bc == 16 && bcount >= 16 &&
+		    reader->read_bytes (id, data, 16) == 16);
+
+	    reader->set_pos_rel (id, meta_bc, SEEK_CUR);
+	    bcount -= meta_bc;
+	}
+    }
+}
+
+#ifdef SEEKING
+// Find a valid WavPack header, searching either from the current file position
+// (or from the specified position if not -1) and store it (endian corrected)
+// at the specified pointer. The return value is the exact file position of the
+// header, although we may have actually read past it. Because this function
+// is used for seeking to a specific audio sample, it only considers blocks
+// that contain audio samples for the initial stream to be valid.
+
+#define BUFSIZE 4096
+
+static ulong find_header (stream_reader *reader, void *id, ulong filepos, WavpackHeader *wphdr)
+{
+    char *buffer = malloc (BUFSIZE), *sp = buffer, *ep = buffer;
+
+    if (filepos != (ulong) -1 && reader->set_pos_abs (id, filepos)) {
+	free (buffer);
+	return -1;
+    }
+
+    while (1) {
+	int bleft;
+
+	if (sp < ep) {
+	    bleft = ep - sp;
+	    memcpy (buffer, sp, bleft);
+	    ep -= (sp - buffer);
+	    sp = buffer;
+	}
+	else {
+	    if (sp > ep)
+		if (reader->set_pos_rel (id, sp - ep, SEEK_CUR)) {
+		    free (buffer);
+		    return -1;
+		}
+
+	    sp = ep = buffer;
+	    bleft = 0;
+	}
+
+	ep += reader->read_bytes (id, ep, BUFSIZE - bleft);
+
+	if (ep - sp < 32) {
+	    free (buffer);
+	    return -1;
+	}
+
+	while (sp + 32 <= ep)
+	    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, sp - 4, sizeof (*wphdr));
+		    little_endian_to_native (wphdr, WavpackHeaderFormat);
+
+		    if (wphdr->block_samples && (wphdr->flags & INITIAL_BLOCK)) {
+			free (buffer);
+			return reader->get_pos (id) - (ep - sp + 4);
+		    }
+
+		    if (wphdr->ckSize > 1024)
+			sp += wphdr->ckSize - 1024;
+	    }
+    }
+}
+
+// Find the WavPack block that contains the specified sample. If "header_pos"
+// is zero, then no information is assumed except the total number of samples
+// in the file and its size in bytes. If "header_pos" is non-zero then we
+// assume that it is the file position of the valid header image contained in
+// the first stream and we can limit our search to either the portion above
+// or below that point. If a .wvc file is being used, then this must be called
+// for that file also.
+
+static ulong find_sample (WavpackContext *wpc, void *infile, ulong header_pos, ulong sample)
+{
+    WavpackStream *wps = wpc->streams [wpc->current_stream];
+    ulong file_pos1 = 0, file_pos2 = wpc->reader->get_length (infile);
+    ulong sample_pos1 = 0, sample_pos2 = wpc->total_samples;
+    double ratio = 0.96;
+    int file_skip = 0;
+
+    if (sample >= wpc->total_samples)
+	return -1;
+
+    if (header_pos) {
+	if (wps->wphdr.block_index > sample) {
+	    sample_pos2 = wps->wphdr.block_index;
+	    file_pos2 = header_pos;
+	}
+	else if (wps->wphdr.block_index + wps->wphdr.block_samples <= sample) {
+	    sample_pos1 = wps->wphdr.block_index;
+	    file_pos1 = header_pos;
+	}
+	else
+	    return header_pos;
+    }
+
+    while (1) {
+	double bytes_per_sample;
+	ulong seek_pos;
+
+	bytes_per_sample = file_pos2 - file_pos1;
+	bytes_per_sample /= sample_pos2 - sample_pos1;
+	seek_pos = file_pos1 + (file_skip ? 32 : 0);
+	seek_pos += (ulong)(bytes_per_sample * (sample - sample_pos1) * ratio);
+
+	seek_pos = find_header (wpc->reader, infile, seek_pos, &wps->wphdr);
+
+	if (seek_pos == (ulong) -1 || seek_pos >= file_pos2) {
+	    if (ratio > 0.0) {
+		if ((ratio -= 0.24) < 0.0)
+		    ratio = 0.0;
+	    }
+	    else
+		return -1;
+	}
+	else if (wps->wphdr.block_index > sample) {
+	    sample_pos2 = wps->wphdr.block_index;
+	    file_pos2 = seek_pos;
+	}
+	else if (wps->wphdr.block_index + wps->wphdr.block_samples <= sample) {
+
+	    if (seek_pos == file_pos1)
+		file_skip = 1;
+	    else {
+		sample_pos1 = wps->wphdr.block_index;
+		file_pos1 = seek_pos;
+	    }
+	}
+	else
+	    return seek_pos;
+    }
+}
+#endif
+
+#ifdef TAGS
+
+// This function attempts to load an ID3v1 or APEv2 tag from the specified
+// file into the specified M_Tag structure. The ID3 tag fits in completely,
+// but an APEv2 tag is variable length and so space must be allocated here
+// to accomodate the data, and this will need to be freed later. A return
+// value of TRUE indicates a valid tag was found and loaded. Note that the
+// file pointer is undefined when this function exits.
+
+static int load_tag (WavpackContext *wpc)
+{
+    M_Tag *m_tag = &wpc->m_tag;
+
+    CLEAR (*m_tag);
+
+    // First, attempt to find an APEv2 tag...
+
+    wpc->reader->set_pos_rel (wpc->wv_in, -sizeof (APE_Tag_Hdr), SEEK_END);
+
+    if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->ape_tag_hdr, sizeof (APE_Tag_Hdr)) == sizeof (APE_Tag_Hdr) &&
+	!strncmp (m_tag->ape_tag_hdr.ID, "APETAGEX", 8)) {
+
+	    little_endian_to_native (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
+
+	    if (m_tag->ape_tag_hdr.version == 2000 && m_tag->ape_tag_hdr.item_count &&
+		m_tag->ape_tag_hdr.length > sizeof (m_tag->ape_tag_hdr) &&
+		m_tag->ape_tag_hdr.length < (1024 * 1024) &&
+		(m_tag->ape_tag_data = malloc (m_tag->ape_tag_hdr.length)) != NULL) {
+
+		    memset (m_tag->ape_tag_data, 0, m_tag->ape_tag_hdr.length);
+		    wpc->reader->set_pos_rel (wpc->wv_in, -m_tag->ape_tag_hdr.length, SEEK_END);
+
+		    if (wpc->reader->read_bytes (wpc->wv_in, m_tag->ape_tag_data, m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr)) !=
+			m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr)) {
+			    free (m_tag->ape_tag_data);
+			    CLEAR (*m_tag);
+			    return FALSE;
+		    }
+		    else
+			return TRUE;
+	    }
+    }
+
+    // ...if not, try a ID3v1 tag
+
+    wpc->reader->set_pos_rel (wpc->wv_in, -sizeof (ID3_Tag), SEEK_END);
+
+    if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->id3_tag, sizeof (ID3_Tag)) == sizeof (ID3_Tag) &&
+	!strncmp (m_tag->id3_tag.tag_id, "TAG", 3))
+	    return TRUE;
+    else {
+	CLEAR (*m_tag);
+	return FALSE;
+    }
+}
+
+// Copy the specified ID3v1 tag value (with specified field size) from the
+// source pointer to the destination, eliminating leading spaces and trailing
+// spaces and nulls.
+
+static void tagcpy (char *dest, char *src, int tag_size)
+{
+    char *s1 = src, *s2 = src + tag_size - 1;
+
+    while (s1 <= s2)
+	if (*s1 == ' ')
+	    ++s1;
+	else if (!*s2 || *s2 == ' ')
+	    --s2;
+	else
+	    break;
+
+    while (*s1 && s1 <= s2)
+	*dest++ = *s1++;
+
+    *dest = 0;
+}
+
+// Return TRUE is a valid ID3v1 or APEv2 tag has been loaded.
+
+static int valid_tag (M_Tag *m_tag)
+{
+    if (m_tag->ape_tag_hdr.ID [0] == 'A')
+	return 'A';
+    else if (m_tag->id3_tag.tag_id [0] == 'T')
+	return 'T';
+    else
+	return 0;
+}
+
+// Free the data for any APEv2 tag that was allocated.
+
+static void free_tag (M_Tag *m_tag)
+{
+    if (m_tag->ape_tag_data) {
+	free (m_tag->ape_tag_data);
+	m_tag->ape_tag_data = 0;
+    }
+}
+
+#endif
+
+#endif
--- /dev/null	2005-03-14 14:14:46.000000000 +0000
+++ ext/wavpack/license.txt	2005-04-01 20:23:34.000000000 +0100
@@ -0,0 +1,25 @@
+               Copyright (c) 1998 - 2004 Conifer Software
+                          All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of Conifer Software nor the names of its contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
