summaryrefslogtreecommitdiff
path: root/apps/codecs/libwavpack/pack.c
diff options
context:
space:
mode:
authorDave Bryant <bryant@rockbox.org>2005-07-04 06:38:00 +0000
committerDave Bryant <bryant@rockbox.org>2005-07-04 06:38:00 +0000
commitdacbc16d5b2c2a113eab6b9295db12795d98e2cc (patch)
tree6040249a9776880f02d478532028f41065a65451 /apps/codecs/libwavpack/pack.c
parent1d5f07b0a654ca0ee0b6f4785388801ed809af33 (diff)
downloadrockbox-dacbc16d5b2c2a113eab6b9295db12795d98e2cc.zip
rockbox-dacbc16d5b2c2a113eab6b9295db12795d98e2cc.tar.gz
rockbox-dacbc16d5b2c2a113eab6b9295db12795d98e2cc.tar.bz2
rockbox-dacbc16d5b2c2a113eab6b9295db12795d98e2cc.tar.xz
Added lossless encoding to WavPack library. Also made a few changes to
decoding stuff in preparation for future optimization and eliminated all tabs. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7009 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/libwavpack/pack.c')
-rw-r--r--apps/codecs/libwavpack/pack.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/apps/codecs/libwavpack/pack.c b/apps/codecs/libwavpack/pack.c
new file mode 100644
index 0000000..e695388
--- /dev/null
+++ b/apps/codecs/libwavpack/pack.c
@@ -0,0 +1,450 @@
+////////////////////////////////////////////////////////////////////////////
+// **** WAVPACK **** //
+// Hybrid Lossless Wavefile Compressor //
+// Copyright (c) 1998 - 2005 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 <string.h>
+
+// This flag provides faster encoding speed at the expense of more code. The
+// improvement applies to 16-bit stereo lossless only.
+
+//////////////////////////////// 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).
+
+static const char default_terms [] = { 18,18,2,3,-2,0 };
+static const char high_terms [] = { 18,18,2,3,-2,18,2,4,7,5,3,6,8,-1,18,2,0 };
+static 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->stream;
+ ulong flags = wps->wphdr.flags;
+ struct decorr_pass *dpp;
+ const char *term_string;
+ int ti;
+
+ wps->sample_index = 0;
+ CLEAR (wps->decorr_passes);
+
+ 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; term_string [ti]; 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.
+
+static void write_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+ int tcount = wps->num_terms;
+ struct decorr_pass *dpp;
+ char *byteptr;
+
+ byteptr = wpmd->data = wpmd->temp_data;
+ 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
+
+static void write_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd)
+{
+ int tcount = wps->num_terms;
+ struct decorr_pass *dpp;
+ char *byteptr;
+
+ byteptr = wpmd->data = wpmd->temp_data;
+ 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.
+
+static 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 = wpmd->temp_data;
+ 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 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).
+
+static void write_config_info (WavpackContext *wpc, WavpackMetadata *wpmd)
+{
+ char *byteptr;
+
+ byteptr = wpmd->data = wpmd->temp_data;
+ 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 pack_samples (WavpackContext *wpc, long *buffer);
+
+int pack_block (WavpackContext *wpc, long *buffer)
+{
+ WavpackStream *wps = &wpc->stream;
+ ulong flags = wps->wphdr.flags, sflags = wps->wphdr.flags;
+ ulong sample_count = wps->wphdr.block_samples;
+
+ 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 (!pack_samples (wpc, buffer)) {
+ wps->wphdr.flags = sflags;
+ return FALSE;
+ }
+ else {
+ wps->wphdr.flags = sflags;
+ return TRUE;
+ }
+}
+
+// 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->stream;
+ ulong sample_count = wps->wphdr.block_samples;
+ ulong flags = wps->wphdr.flags, data_count;
+ struct decorr_pass *dpp;
+ WavpackMetadata wpmd;
+ int tcount, m = 0;
+ ulong crc, i;
+ long *bptr;
+
+ crc = 0xffffffff;
+ wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
+ memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader));
+
+ if (wpc->wrapper_bytes) {
+ wpmd.id = ID_RIFF_HEADER;
+ wpmd.byte_length = wpc->wrapper_bytes;
+ wpmd.data = wpc->wrapper_data;
+ copy_metadata (&wpmd, wps->blockbuff, wps->blockend);
+ free_metadata (&wpmd);
+ wpc->wrapper_data = NULL;
+ wpc->wrapper_bytes = 0;
+ }
+
+ 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 & 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);
+
+ /////////////////////// 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_i (dpp->weight_A, sam);
+ update_weight (dpp->weight_A, 2, 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_i (dpp->weight_A, sam_A);
+ right -= apply_weight_i (dpp->weight_B, sam_B);
+ update_weight (dpp->weight_A, 2, sam_A, left);
+ update_weight (dpp->weight_B, 2, 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_i (dpp->weight_A, sam_A);
+ right -= apply_weight_i (dpp->weight_B, sam_B);
+ update_weight_clip (dpp->weight_A, 2, sam_A, left);
+ update_weight_clip (dpp->weight_B, 2, sam_B, right);
+ }
+ }
+
+ m = (m + 1) & (MAX_TERM - 1);
+ send_word_lossless (wps, left, 0);
+ send_word_lossless (wps, right, 1);
+ }
+
+ 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);
+ }
+ }
+
+ 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;
+
+ wps->sample_index += sample_count;
+ return TRUE;
+}