From e449d88b3e6b584998f8f38ed61467c35ca74466 Mon Sep 17 00:00:00 2001 From: Christian Gmeiner Date: Fri, 25 Feb 2005 17:05:30 +0000 Subject: Initial import of libwavpack git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6056 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/libwavpack/LICENSE | 25 ++ apps/codecs/libwavpack/README | 52 +++ apps/codecs/libwavpack/README.rockbox | 15 + apps/codecs/libwavpack/SOURCES | 6 + apps/codecs/libwavpack/bits.c | 140 +++++++++ apps/codecs/libwavpack/float.c | 83 +++++ apps/codecs/libwavpack/make.bat | 1 + apps/codecs/libwavpack/metadata.c | 105 +++++++ apps/codecs/libwavpack/unpack.c | 576 ++++++++++++++++++++++++++++++++++ apps/codecs/libwavpack/wavpack.h | 330 +++++++++++++++++++ apps/codecs/libwavpack/words.c | 503 +++++++++++++++++++++++++++++ apps/codecs/libwavpack/wputils.c | 354 +++++++++++++++++++++ 12 files changed, 2190 insertions(+) create mode 100644 apps/codecs/libwavpack/LICENSE create mode 100644 apps/codecs/libwavpack/README create mode 100644 apps/codecs/libwavpack/README.rockbox create mode 100644 apps/codecs/libwavpack/SOURCES create mode 100644 apps/codecs/libwavpack/bits.c create mode 100644 apps/codecs/libwavpack/float.c create mode 100644 apps/codecs/libwavpack/make.bat create mode 100644 apps/codecs/libwavpack/metadata.c create mode 100644 apps/codecs/libwavpack/unpack.c create mode 100644 apps/codecs/libwavpack/wavpack.h create mode 100644 apps/codecs/libwavpack/words.c create mode 100644 apps/codecs/libwavpack/wputils.c (limited to 'apps/codecs') diff --git a/apps/codecs/libwavpack/LICENSE b/apps/codecs/libwavpack/LICENSE new file mode 100644 index 0000000000..3ee485817d --- /dev/null +++ b/apps/codecs/libwavpack/LICENSE @@ -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. diff --git a/apps/codecs/libwavpack/README b/apps/codecs/libwavpack/README new file mode 100644 index 0000000000..b373235e1c --- /dev/null +++ b/apps/codecs/libwavpack/README @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2004 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +This package contains a tiny version of the WavPack 4.0 decoder that might +be used in a "resource limited" CPU environment or form the basis for a +hardware decoding implementation. It is packaged with a demo command-line +program that accepts a WavPack audio file on stdin and outputs a RIFF wav +file to stdout. The program is standard C, and a win32 executable is +included which was compiled under MS Visual C++ 6.0 using this command: + +cl /O1 /DWIN32 wvfilter.c wputils.c unpack.c float.c metadata.c words.c bits.c + +WavPack data is read with a stream reading callback. No direct seeking is +provided for, but it is possible to start decoding anywhere in a WavPack +stream. In this case, WavPack will be able to provide the sample-accurate +position when it synchs with the data and begins decoding. + +For demonstration purposes this uses a single static copy of the +WavpackContext structure, so obviously it cannot be used for more than one +file at a time. Also, this decoder will not handle "correction" files, plays +only the first two channels of multi-channel files, and is limited in +resolution in some large integer or floating point files (but always +provides at least 24 bits of resolution). It also will not accept WavPack +files from before version 4.0. + +To make this code viable on the greatest number of hardware platforms, the +following are true: + + speed is about 4x realtime on an AMD K6 300 MHz + ("high" mode 16/44 stereo; normal mode is about twice that fast) + + no floating-point math required; just 32b * 32b = 32b int multiply + + large data areas are static and less than 4K total + executable code and tables are less than 32K + no malloc / free usage + +To maintain compatibility on various platforms, the following conventions +are used: + + a "short" must be 16-bits + a "long" must be 32-bits + an "int" must be at least 16-bits, but may be larger + a "char" must default to signed + + +Questions or comments should be directed to david@wavpack.com diff --git a/apps/codecs/libwavpack/README.rockbox b/apps/codecs/libwavpack/README.rockbox new file mode 100644 index 0000000000..a74b0c511c --- /dev/null +++ b/apps/codecs/libwavpack/README.rockbox @@ -0,0 +1,15 @@ +Library: wavpack - Release 4.1 - September 14, 2004 +Imported: 2005-02-25 by Christian Gmeiner + + +This directory contains a "tiny" decoder version of wavpack for version 4.x. + +LICENSING INFORMATION + +wavpack is released under the BSD License as described +in the LICENSE file in this directory. + + +IMPORT DETAILS + +Excluded is wvfilter.c, because it is only a test programm. diff --git a/apps/codecs/libwavpack/SOURCES b/apps/codecs/libwavpack/SOURCES new file mode 100644 index 0000000000..9b79d70b22 --- /dev/null +++ b/apps/codecs/libwavpack/SOURCES @@ -0,0 +1,6 @@ +bits.c +float.c +metadata.c +unpack.c +words.c +wputils.c \ No newline at end of file diff --git a/apps/codecs/libwavpack/bits.c b/apps/codecs/libwavpack/bits.c new file mode 100644 index 0000000000..25a222780f --- /dev/null +++ b/apps/codecs/libwavpack/bits.c @@ -0,0 +1,140 @@ +//////////////////////////////////////////////////////////////////////////// +// **** 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 +#include + +////////////////////////// Bitstream functions //////////////////////////////// + +// 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, read_stream file, ulong file_bytes) +{ + CLEAR (*bs); + bs->buf = buffer_start; + bs->end = buffer_end; + + if (file) { + bs->ptr = bs->end - 1; + bs->file_bytes = file_bytes; + bs->file = file; + } + else + bs->ptr = bs->buf - 1; + + 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) +{ + if (bs->file && bs->file_bytes) { + ulong bytes_read, bytes_to_read = bs->end - bs->buf; + + if (bytes_to_read > bs->file_bytes) + bytes_to_read = bs->file_bytes; + + bytes_read = bs->file (bs->buf, bytes_to_read); + + if (bytes_read) { + bs->end = bs->buf + bytes_read; + bs->file_bytes -= bytes_read; + } + else { + memset (bs->buf, -1, bs->end - bs->buf); + bs->error = 1; + } + } + else + bs->error = 1; + + if (bs->error) + memset (bs->buf, -1, bs->end - bs->buf); + + bs->ptr = bs->buf; +} + +/////////////////////// 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++; + } +} diff --git a/apps/codecs/libwavpack/float.c b/apps/codecs/libwavpack/float.c new file mode 100644 index 0000000000..3e678e824d --- /dev/null +++ b/apps/codecs/libwavpack/float.c @@ -0,0 +1,83 @@ +//////////////////////////////////////////////////////////////////////////// +// **** 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" + +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; +} + +void float_values (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) + outval.exponent = 255; + 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++; + } +} diff --git a/apps/codecs/libwavpack/make.bat b/apps/codecs/libwavpack/make.bat new file mode 100644 index 0000000000..0ca72d7e55 --- /dev/null +++ b/apps/codecs/libwavpack/make.bat @@ -0,0 +1 @@ +cl /O1 /DWIN32 wvfilter.c wputils.c unpack.c float.c metadata.c words.c bits.c diff --git a/apps/codecs/libwavpack/metadata.c b/apps/codecs/libwavpack/metadata.c new file mode 100644 index 0000000000..40ede99cd4 --- /dev/null +++ b/apps/codecs/libwavpack/metadata.c @@ -0,0 +1,105 @@ +//////////////////////////////////////////////////////////////////////////// +// **** 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" + +int read_metadata_buff (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + uchar tchar; + + if (!wpc->infile (&wpmd->id, 1) || !wpc->infile (&tchar, 1)) + return FALSE; + + wpmd->byte_length = tchar << 1; + + if (wpmd->id & ID_LARGE) { + wpmd->id &= ~ID_LARGE; + + if (!wpc->infile (&tchar, 1)) + return FALSE; + + wpmd->byte_length += (long) tchar << 9; + + if (!wpc->infile (&tchar, 1)) + return FALSE; + + wpmd->byte_length += (long) tchar << 17; + } + + if (wpmd->id & ID_ODD_SIZE) { + wpmd->id &= ~ID_ODD_SIZE; + wpmd->byte_length--; + } + + if (wpmd->byte_length && wpmd->byte_length <= sizeof (wpc->read_buffer)) { + ulong bytes_to_read = wpmd->byte_length + (wpmd->byte_length & 1); + + if (wpc->infile (wpc->read_buffer, bytes_to_read) != (long) bytes_to_read) { + wpmd->data = NULL; + return FALSE; + } + + wpmd->data = wpc->read_buffer; + } + else + wpmd->data = NULL; + + return TRUE; +} + +int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + WavpackStream *wps = &wpc->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_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 (wpc, wpmd); + + case ID_SHAPING_WEIGHTS: + case ID_WVC_BITSTREAM: + case ID_WVX_BITSTREAM: + return TRUE; + + default: + return (wpmd->id & ID_OPTIONAL_DATA) ? TRUE : FALSE; + } +} diff --git a/apps/codecs/libwavpack/unpack.c b/apps/codecs/libwavpack/unpack.c new file mode 100644 index 0000000000..e2e27b4999 --- /dev/null +++ b/apps/codecs/libwavpack/unpack.c @@ -0,0 +1,576 @@ +//////////////////////////////////////////////////////////////////////////// +// **** 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.c module. For +// maximum efficiency, the conversion is isolated to tight loops that handle +// an entire buffer. + +#include "wavpack.h" + +#include +#include + +#define LOSSY_MUTE + +//////////////////////////////// local macros ///////////////////////////////// + +#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; + +///////////////////////////// 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). This is where all the metadata blocks are +// scanned up to the one containing the audio bitstream. + +int unpack_init (WavpackContext *wpc) +{ + WavpackStream *wps = &wpc->stream; + 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 = 0xffffffff; + CLEAR (wps->wvbits); + CLEAR (wps->decorr_passes); + CLEAR (wps->w); + + while (read_metadata_buff (wpc, &wpmd)) { + if (!process_metadata (wpc, &wpmd)) { + strcpy (wpc->error_message, "invalid metadata!"); + return FALSE; + } + + if (wpmd.id == ID_WV_BITSTREAM) + break; + } + + if (wps->wphdr.block_samples && !bs_is_open (&wps->wvbits)) { + strcpy (wpc->error_message, "invalid WavPack file!"); + return FALSE; + } + + if (wps->wphdr.block_samples) { + 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 (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + WavpackStream *wps = &wpc->stream; + + if (wpmd->data) + bs_open_read (&wps->wvbits, wpmd->data, (char *) wpmd->data + wpmd->byte_length, NULL, 0); + else if (wpmd->byte_length) + bs_open_read (&wps->wvbits, wpc->read_buffer, wpc->read_buffer + sizeof (wpc->read_buffer), + wpc->infile, wpmd->byte_length + (wpmd->byte_length & 1)); + + 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)) { + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_FLAG)) + 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 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; +} + +// 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 (WavpackStream *wps, long *buffer, ulong sample_count); + +long unpack_samples (WavpackContext *wpc, long *buffer, ulong sample_count) +{ + WavpackStream *wps = &wpc->stream; + ulong flags = wps->wphdr.flags, crc = wps->crc, i; + long mute_limit = (1L << ((flags & MAG_MASK) >> MAG_LSB)) + 2; + struct decorr_pass *dpp; + long read_word, *bptr; + 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) + mute_limit *= 2; + + ///////////////////// handle version 4 mono data ///////////////////////// + + if (flags & MONO_FLAG) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + if ((read_word = get_word (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 stereo data //////////////////////// + + else + for (bptr = buffer, i = 0; i < sample_count; ++i) { + long left, right, left2, right2; + + if ((left = get_word (wps, 0)) == WORD_EOF || + (right = get_word (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; + } + + if (i != sample_count) { + memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8)); + wps->mute_error = TRUE; + i = sample_count; + } + + while (m--) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0 && dpp->term <= MAX_TERM) { + long temp = dpp->samples_A [0]; + memcpy (dpp->samples_A, dpp->samples_A + 1, sizeof (dpp->samples_A) - sizeof (dpp->samples_A [0])); + dpp->samples_A [MAX_TERM - 1] = temp; + temp = dpp->samples_B [0]; + memcpy (dpp->samples_B, dpp->samples_B + 1, sizeof (dpp->samples_B) - sizeof (dpp->samples_B [0])); + dpp->samples_B [MAX_TERM - 1] = temp; + } + + fixup_samples (wps, buffer, i); + + if (flags & FLOAT_DATA) + 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 (WavpackStream *wps, long *buffer, ulong sample_count) +{ + ulong flags = wps->wphdr.flags; + 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 mask = (1 << sent_bits) - 1; + long *dptr = buffer; + + if (!(flags & HYBRID_FLAG) && !sent_bits && (zeros + ones + dups)) + 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 (flags & HYBRID_FLAG) { + long min_value, max_value, min_shifted, max_shifted; + + 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_shifted = (min_value = -(long)2147483648 >> shift) << shift; + max_shifted = (max_value = (long) 2147483647 >> shift) << 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) +{ + WavpackStream *wps = &wpc->stream; + int result = 0; + + if (wps->crc != wps->wphdr.crc) + ++result; + + return result; +} diff --git a/apps/codecs/libwavpack/wavpack.h b/apps/codecs/libwavpack/wavpack.h new file mode 100644 index 0000000000..af9d88dfe6 --- /dev/null +++ b/apps/codecs/libwavpack/wavpack.h @@ -0,0 +1,330 @@ +//////////////////////////////////////////////////////////////////////////// +// **** 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 + +#include + +// 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 + +// 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 + +#define FALSE 0 +#define TRUE 1 + +////////////////////////////// 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 { + 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; +} 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 (not yet) +#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 % + +//////////////////////////////// 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 long (*read_stream)(void *, long); + +typedef struct bs { + uchar *buf, *end, *ptr; + void (*wrap)(struct bs *bs); + ulong file_bytes, sr; + int error, bc; + read_stream file; +} Bitstream; + +#define MAX_NTERMS 16 +#define MAX_TERM 8 + +struct decorr_pass { + short term, delta, weight_A, weight_B; + long samples_A [MAX_TERM], samples_B [MAX_TERM]; +}; + +typedef struct { + WavpackHeader wphdr; + + int num_terms, mute_error; + ulong sample_index, crc; + Bitstream wvbits; + + uchar int32_sent_bits, int32_zeros, int32_ones, int32_dups; + uchar float_flags, float_shift, float_max_exp, float_norm_exp; + + 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 { + WavpackConfig config; + WavpackStream stream; + + uchar read_buffer [1024]; + char error_message [80]; + + read_stream infile; + ulong total_samples, crc_errors, first_flags; + int open_flags, norm_offset, reduced_channels, lossy_blocks; + +} WavpackContext; + +//////////////////////// function prototypes and macros ////////////////////// + +#define CLEAR(destin) memset (&destin, 0, sizeof (destin)); + +// bits.c + +void bs_open_read (Bitstream *bs, uchar *buffer_start, uchar *buffer_end, read_stream file, ulong file_bytes); + +#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); \ +} + +void little_endian_to_native (void *data, char *format); +void native_to_little_endian (void *data, char *format); + +// unpack.c + +int unpack_init (WavpackContext *wpc); +int init_wv_bitstream (WavpackContext *wpc, 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_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); +long unpack_samples (WavpackContext *wpc, long *buffer, ulong sample_count); +int check_crc_error (WavpackContext *wpc); + +// metadata.c stuff + +int read_metadata_buff (WavpackContext *wpc, WavpackMetadata *wpmd); +int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd); + +// words.c stuff + +int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd); +int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd); +long get_word (WavpackStream *wps, int chan); +long exp2s (int log); +int restore_weight (char weight); + +#define WORD_EOF (1L << 31) + +// float.c + +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); + +// wputils.c + +WavpackContext *WavpackOpenFileInput (read_stream infile, char *error); + +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 + +ulong WavpackUnpackSamples (WavpackContext *wpc, long *buffer, ulong samples); +ulong WavpackGetNumSamples (WavpackContext *wpc); +ulong WavpackGetSampleIndex (WavpackContext *wpc); +int WavpackGetNumErrors (WavpackContext *wpc); +int WavpackLossyBlocks (WavpackContext *wpc); +ulong WavpackGetSampleRate (WavpackContext *wpc); +int WavpackGetBitsPerSample (WavpackContext *wpc); +int WavpackGetBytesPerSample (WavpackContext *wpc); +int WavpackGetNumChannels (WavpackContext *wpc); +int WavpackGetReducedChannels (WavpackContext *wpc); diff --git a/apps/codecs/libwavpack/words.c b/apps/codecs/libwavpack/words.c new file mode 100644 index 0000000000..35061b69a9 --- /dev/null +++ b/apps/codecs/libwavpack/words.c @@ -0,0 +1,503 @@ +//////////////////////////////////////////////////////////////////////////// +// **** 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 + +//////////////////////////////// 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 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 log2 (unsigned long avalue); + +// 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); + } + } +} + +static ulong 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 get_word (WavpackStream *wps, int chan) +{ + ulong ones_count, low, mid, high; + int sign; + + 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 (wps->wphdr.flags & HYBRID_BITRATE) { + wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS; + wps->w.slow_level [chan] += log2 (mid); + } + + return sign ? ~mid : mid; +} + +// 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 read_code (Bitstream *bs, ulong maxcode) +{ + int bitcount = count_bits (maxcode); + ulong extras = (1L << bitcount) - maxcode - 1, code; + + if (!bitcount) + return 0; + + getbits (&code, bitcount - 1, bs); + code &= (1L << (bitcount - 1)) - 1; + + if (code >= extras) { + code = (code << 1) - extras; + + if (getbit (bs)) + ++code; + } + + return code; +} + +// 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 log2 (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 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. + +int restore_weight (char weight) +{ + int result; + + if ((result = (int) weight << 3) > 0) + result += (result + 64) >> 7; + + return result; +} diff --git a/apps/codecs/libwavpack/wputils.c b/apps/codecs/libwavpack/wputils.c new file mode 100644 index 0000000000..21c399f517 --- /dev/null +++ b/apps/codecs/libwavpack/wputils.c @@ -0,0 +1,354 @@ +//////////////////////////////////////////////////////////////////////////// +// **** 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 for decoding WavPack 4.0 audio +// streams and files. WavPack data is read with a stream reading callback. No +// direct seeking is provided for, but it is possible to start decoding +// anywhere in a WavPack stream. In this case, WavPack will be able to provide +// the sample-accurate position when it synchs with the data and begins +// decoding. + +#include "wavpack.h" + +#include + +///////////////////////////// local table storage //////////////////////////// + +const ulong sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 }; + +///////////////////////////// executable code //////////////////////////////// + +static ulong read_next_header (read_stream infile, WavpackHeader *wphdr); + +// This function reads data from the specified stream in search of a valid +// WavPack 4.0 audio block. If this fails in 1 megabyte (or an invalid or +// unsupported WavPack block is encountered) 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). This can be initiated at the beginning of a +// WavPack file, or anywhere inside a WavPack file. To determine the exact +// position within the file use WavpackGetSampleIndex(). For demonstration +// purposes this uses a single static copy of the WavpackContext structure, +// so obviously it cannot be used for more than one file at a time. Also, +// this function will not handle "correction" files, plays only the first +// two channels of multi-channel files, and is limited in resolution in some +// large integer or floating point files (but always provides at least 24 bits +// of resolution). + +static WavpackContext wpc; + +WavpackContext *WavpackOpenFileInput (read_stream infile, char *error) +{ + WavpackStream *wps = &wpc.stream; + ulong bcount; + + CLEAR (wpc); + wpc.infile = infile; + wpc.total_samples = (ulong) -1; + wpc.norm_offset = 0; + wpc.open_flags = 0; + + // open the source file for reading and store the size + + while (!wps->wphdr.block_samples) { + + bcount = read_next_header (wpc.infile, &wps->wphdr); + + if (bcount == (ulong) -1) { + strcpy (error, "not compatible with this version of WavPack file!"); + return NULL; + } + + 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 NULL; + } + + if (wps->wphdr.block_samples && wps->wphdr.total_samples != (ulong) -1) + wpc.total_samples = wps->wphdr.total_samples; + + if (!unpack_init (&wpc)) { + strcpy (error, wpc.error_message [0] ? wpc.error_message : + "not compatible with this version of WavPack file!"); + + return NULL; + } + } + + 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 (!(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_LOSSLESS: file is lossless (pure lossless only) +// MODE_HYBRID: file is hybrid mode (lossy part only) +// MODE_FLOAT: audio data is 32-bit ieee floating point +// MODE_HIGH: file was created in "high" mode (information only) +// MODE_FAST: file was created in "fast" mode (information only) + +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->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; + } + + return mode; +} + +// 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. 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 is normalized to +/-1.0). 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->stream; + ulong bcount, samples_unpacked = 0, samples_to_unpack; + int num_channels = wpc->config.num_channels; + + while (samples) { + if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || + wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples) { + bcount = read_next_header (wpc->infile, &wps->wphdr); + + if (bcount == (ulong) -1) + 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 || 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; + + 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)) + wpc->crc_errors++; + } + + if (wps->sample_index == wpc->total_samples) + break; + } + + return samples_unpacked; +} + +// 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) + return wpc->stream.sample_index; + + 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; +} + +// 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, but this +// version can only decode the first two. + +int WavpackGetNumChannels (WavpackContext *wpc) +{ + return wpc ? wpc->config.num_channels : 2; +} + +// 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; +} + +// 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 multi-channel file. + +int WavpackGetReducedChannels (WavpackContext *wpc) +{ + if (wpc) + return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels; + else + return 2; +} + +// Read from current file position until a valid 32-byte WavPack 4.0 header is +// found and read into the specified pointer. The number of bytes skipped is +// returned. If no WavPack header is found within 1 meg, then a -1 is returned +// to indicate the error. No additional bytes are read past the header and it +// is returned in the processor's native endian mode. Seeking is not required. + +static ulong read_next_header (read_stream infile, WavpackHeader *wphdr) +{ + char buffer [sizeof (*wphdr)], *sp = buffer + sizeof (*wphdr), *ep = sp; + ulong bytes_skipped = 0; + int bleft; + + while (1) { + if (sp < ep) { + bleft = ep - sp; + memcpy (buffer, sp, bleft); + } + else + bleft = 0; + + if (infile (buffer + bleft, sizeof (*wphdr) - bleft) != (long) sizeof (*wphdr) - bleft) + return -1; + + sp = buffer; + + if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' && + !(*++sp & 1) && sp [2] < 16 && !sp [3] && sp [5] == 4 && sp [4] >= 2 && sp [4] <= 0xf) { + memcpy (wphdr, buffer, sizeof (*wphdr)); + little_endian_to_native (wphdr, WavpackHeaderFormat); + return bytes_skipped; + } + + while (sp < ep && *sp != 'w') + sp++; + + if ((bytes_skipped += sp - buffer) > 1024 * 1024) + return -1; + } +} -- cgit v1.2.3