diff options
| author | Dave Bryant <bryant@rockbox.org> | 2005-07-04 06:38:00 +0000 |
|---|---|---|
| committer | Dave Bryant <bryant@rockbox.org> | 2005-07-04 06:38:00 +0000 |
| commit | dacbc16d5b2c2a113eab6b9295db12795d98e2cc (patch) | |
| tree | 6040249a9776880f02d478532028f41065a65451 /apps/codecs/libwavpack/pack.c | |
| parent | 1d5f07b0a654ca0ee0b6f4785388801ed809af33 (diff) | |
| download | rockbox-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.c | 450 |
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; +} |