about summary refs log tree commit diff
path: root/src/wavpack/wputils.c
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2007-05-22 15:03:32 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2007-05-22 15:03:32 +0000
commit73aa9b71c1d8b5c5065d1e474f13601da3ca6b20 (patch)
tree88b6a0a4a2ebdd33a88f4a25682581d329d33f6b /src/wavpack/wputils.c
downloadzcatch-73aa9b71c1d8b5c5065d1e474f13601da3ca6b20.tar.gz
zcatch-73aa9b71c1d8b5c5065d1e474f13601da3ca6b20.zip
started the major restructure of svn
Diffstat (limited to 'src/wavpack/wputils.c')
-rw-r--r--src/wavpack/wputils.c351
1 files changed, 351 insertions, 0 deletions
diff --git a/src/wavpack/wputils.c b/src/wavpack/wputils.c
new file mode 100644
index 00000000..0d71af53
--- /dev/null
+++ b/src/wavpack/wputils.c
@@ -0,0 +1,351 @@
+////////////////////////////////////////////////////////////////////////////

+//                           **** WAVPACK ****                            //

+//                  Hybrid Lossless Wavefile Compressor                   //

+//              Copyright (c) 1998 - 2006 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 <string.h>

+

+///////////////////////////// local table storage ////////////////////////////

+

+const uint32_t sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050,

+    24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 };

+

+///////////////////////////// executable code ////////////////////////////////

+

+static uint32_t 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;

+    uint32_t bcount;

+

+    CLEAR (wpc);

+    wpc.infile = infile;

+    wpc.total_samples = (uint32_t) -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 == (uint32_t) -1) {

+            strcpy (error, "not compatible with this version of WavPack file!");

+            return NULL;

+        }

+

+        if (wps->wphdr.block_samples && wps->wphdr.total_samples != (uint32_t) -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.flags & FLOAT_DATA) {

+        wpc.config.bytes_per_sample = 3;

+        wpc.config.bits_per_sample = 24;

+    }

+

+    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 (but will provided

+//               in 24-bit integers for convenience)

+// 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 will be returned as 24-bit integers (and may

+// also be clipped). 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.

+

+uint32_t WavpackUnpackSamples (WavpackContext *wpc, int32_t *buffer, uint32_t samples)

+{

+    WavpackStream *wps = &wpc->stream;

+    uint32_t 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 == (uint32_t) -1)

+                    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

+

+uint32_t WavpackGetNumSamples (WavpackContext *wpc)

+{

+    return wpc ? wpc->total_samples : (uint32_t) -1;

+}

+

+// Get the current sample index position, or -1 if unknown

+

+uint32_t WavpackGetSampleIndex (WavpackContext *wpc)

+{

+    if (wpc)

+        return wpc->stream.sample_index;

+

+    return (uint32_t) -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

+

+uint32_t 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 uint32_t read_next_header (read_stream infile, WavpackHeader *wphdr)

+{

+    char buffer [sizeof (*wphdr)], *sp = buffer + sizeof (*wphdr), *ep = sp;

+    uint32_t 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) != (int32_t) 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] >= (MIN_STREAM_VERS & 0xff) && sp [4] <= (MAX_STREAM_VERS & 0xff)) {

+                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) > 1048576L)

+            return -1;

+    }

+}