Merge pull request 'WavPack and APEv2 tags support' (#218) from ayumi/tangara-fw:wavpack into main
Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/218custom
commit
5995b3a48e
@ -0,0 +1,233 @@ |
|||||||
|
#include <math.h> |
||||||
|
#include "tagspriv.h" |
||||||
|
|
||||||
|
#define leu16int(d) (u16int)(((uchar*)(d))[1]<<8 | ((uchar*)(d))[0]<<0) |
||||||
|
|
||||||
|
enum |
||||||
|
{ |
||||||
|
HeaderSize = 32, |
||||||
|
FooterSize = HeaderSize, |
||||||
|
|
||||||
|
MagicOffset = 0, |
||||||
|
VersionOffset = 8, |
||||||
|
SizeOffset = 12, |
||||||
|
CountOffset = 16, |
||||||
|
FlagsOffset = 20, |
||||||
|
|
||||||
|
WvHeaderSize = 32, |
||||||
|
WvMagicOffset = 0, |
||||||
|
WvVersionOffset = 8, |
||||||
|
WvSamplesHighOffset = 11, |
||||||
|
WvSamplesLowOffset = 12, |
||||||
|
WvFlagsOffset = 24, |
||||||
|
WvSampleRateMask = 0xf << 23, |
||||||
|
WvSampleRateShift = 23, |
||||||
|
WvCustomSampleRate = 16, |
||||||
|
WvMonoMask = 4, |
||||||
|
}; |
||||||
|
|
||||||
|
typedef enum |
||||||
|
{ |
||||||
|
TagUTF8, |
||||||
|
TagBinary, |
||||||
|
TagExternal, |
||||||
|
TagReserved, |
||||||
|
|
||||||
|
TagInvalid, |
||||||
|
} TagType; |
||||||
|
|
||||||
|
static int |
||||||
|
isWavpack(Tagctx *ctx) |
||||||
|
{ |
||||||
|
uchar header[WvHeaderSize]; |
||||||
|
int size; |
||||||
|
u16int version; |
||||||
|
u32int flags, samplerate; |
||||||
|
uvlong samples; |
||||||
|
|
||||||
|
if(ctx->seek(ctx, 0, 0) < 0) |
||||||
|
return 0; |
||||||
|
if(ctx->read(ctx, header, WvHeaderSize) != WvHeaderSize) |
||||||
|
return 0; |
||||||
|
if(memcmp(header+WvMagicOffset, "wvpk", 4)) |
||||||
|
return 0; |
||||||
|
version = leu16int(header+WvVersionOffset); |
||||||
|
if(version<0x402 || version>0x410) |
||||||
|
return 0; |
||||||
|
samples = (uvlong)(*(uchar*)(header+WvSamplesHighOffset))<<32 | leuint(header+WvSamplesLowOffset); |
||||||
|
flags = leuint(header+WvFlagsOffset); |
||||||
|
if((flags&WvSampleRateMask)>>WvSampleRateShift != WvCustomSampleRate){ |
||||||
|
const u32int samplerates[] = {6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000}; |
||||||
|
samplerate = samplerates[(flags&WvSampleRateMask)>>WvSampleRateShift]; |
||||||
|
ctx->samplerate = samplerate; |
||||||
|
uvlong duration = round((double)samples/samplerate*1000); |
||||||
|
ctx->duration = duration; |
||||||
|
} |
||||||
|
ctx->channels = flags&WvMonoMask ? 1 : 2; |
||||||
|
if(ctx->seek(ctx, 0, 0) < 0) |
||||||
|
return 0; |
||||||
|
if(size = ctx->seek(ctx, 0, 2), size < 0) |
||||||
|
return 0; |
||||||
|
ctx->bitrate = (double)size*8.0/(samples/samplerate)/1000; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
detectFormat(Tagctx *ctx) |
||||||
|
{ |
||||||
|
if(isWavpack(ctx)) |
||||||
|
return Fwavpack; |
||||||
|
return Funknown; |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
tagHasHeader(u32int tags) |
||||||
|
{ |
||||||
|
return (tags&(1<<31)) >> 31; |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
tagIsHeader(u32int tags) |
||||||
|
{ |
||||||
|
return (tags&(1<<29)) >> 29; |
||||||
|
} |
||||||
|
|
||||||
|
static TagType |
||||||
|
tagGetType(u32int tags) |
||||||
|
{ |
||||||
|
switch((tags&(1<<1))>>1 | (tags&(1<<2))>>1 | (tags&(1<<3))>>1 | (tags&(1<<4))>>1){ |
||||||
|
case 0: |
||||||
|
return TagUTF8; |
||||||
|
case 1: |
||||||
|
return TagBinary; |
||||||
|
case 2: |
||||||
|
return TagExternal; |
||||||
|
case 3: |
||||||
|
return TagReserved; |
||||||
|
default: |
||||||
|
return TagInvalid; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
tagTagType(char *name) |
||||||
|
{ |
||||||
|
if(!strcmp(name, "Album")) |
||||||
|
return Talbum; |
||||||
|
else if(!strcmp(name, "Album Artist")) |
||||||
|
return Talbumartist; |
||||||
|
else if(!strcmp(name, "Artist")) |
||||||
|
return Tartist; |
||||||
|
else if(!strcmp(name, "Comment")) |
||||||
|
return Tcomment; |
||||||
|
else if(!strcmp(name, "Composer")) |
||||||
|
return Tcomposer; |
||||||
|
else if(!strncmp(name, "Cover Art (", 11)){ |
||||||
|
if(name[strlen(name)-1] == ')') |
||||||
|
return Timage; |
||||||
|
}else if(!strcmp(name, "Genre")) |
||||||
|
return Tgenre; |
||||||
|
else if(!strcmp(name, "Replaygain_Album_Gain")) |
||||||
|
return Talbumgain; |
||||||
|
else if(!strcmp(name, "Replaygain_Album_Peak")) |
||||||
|
return Talbumpeak; |
||||||
|
else if(!strcmp(name, "Replaygain_Track_Gain")) |
||||||
|
return Ttrackgain; |
||||||
|
else if(!strcmp(name, "Replaygain_Track_Peak")) |
||||||
|
return Ttrackpeak; |
||||||
|
else if(!strcmp(name, "Title")) |
||||||
|
return Ttitle; |
||||||
|
else if(!strcmp(name, "Track")) |
||||||
|
return Ttrack; |
||||||
|
else if(!strcmp(name, "Year")) |
||||||
|
return Tdate; |
||||||
|
return Tunknown; |
||||||
|
} |
||||||
|
|
||||||
|
int |
||||||
|
tagape(Tagctx *ctx) |
||||||
|
{ |
||||||
|
uchar footer[FooterSize]; |
||||||
|
u32int i, count; |
||||||
|
|
||||||
|
ctx->format = detectFormat(ctx); |
||||||
|
|
||||||
|
if(ctx->seek(ctx, -FooterSize, 2) < 0) |
||||||
|
return -1; |
||||||
|
if(ctx->read(ctx, footer, FooterSize) != FooterSize) |
||||||
|
return -1; |
||||||
|
if(memcmp(footer+MagicOffset, "APETAGEX", 8)) |
||||||
|
return -1; |
||||||
|
if(leuint(footer+VersionOffset) != 2000) |
||||||
|
return -1; |
||||||
|
if(tagIsHeader(leuint(footer+FlagsOffset))) |
||||||
|
return -1; |
||||||
|
|
||||||
|
if(ctx->seek(ctx, -FooterSize-leuint(footer+SizeOffset), 2) < 0) |
||||||
|
return -1; |
||||||
|
if(tagHasHeader(leuint(footer+FlagsOffset))){ |
||||||
|
uchar header[HeaderSize]; |
||||||
|
if(ctx->read(ctx, header, HeaderSize) != HeaderSize) |
||||||
|
return -1; |
||||||
|
if(memcmp(header, footer, 23)) |
||||||
|
return -1; |
||||||
|
if(!tagHasHeader(leuint(header+FlagsOffset))) |
||||||
|
return -1; |
||||||
|
if(!tagIsHeader(leuint(header+FlagsOffset))) |
||||||
|
return -1; |
||||||
|
}else if(ctx->seek(ctx, HeaderSize, 1) < 0) |
||||||
|
return -1; |
||||||
|
|
||||||
|
for(i = 0, count = leuint(footer+CountOffset); i < count; i++){ |
||||||
|
int valueOffset = 0; |
||||||
|
char c; |
||||||
|
u32int d, length, flags; |
||||||
|
|
||||||
|
if(ctx->read(ctx, &d, 4) != 4) |
||||||
|
return -1; |
||||||
|
length = leuint(&d); |
||||||
|
if(ctx->read(ctx, &d, 4) != 4) |
||||||
|
return -1; |
||||||
|
flags = leuint(&d); |
||||||
|
|
||||||
|
do{ |
||||||
|
if(valueOffset == ctx->bufsz) |
||||||
|
return -1; |
||||||
|
if(ctx->read(ctx, &c, 1) != 1) |
||||||
|
return -1; |
||||||
|
if(c<' ' || c>'~') |
||||||
|
if(c != '\0') |
||||||
|
return -1; |
||||||
|
ctx->buf[valueOffset++] = c; |
||||||
|
}while(c != '\0'); |
||||||
|
if(valueOffset+1+(int)length>ctx->bufsz && tagTagType(ctx->buf)!=Timage){ |
||||||
|
if(ctx->seek(ctx, length, 1) < 0) |
||||||
|
return -1; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
switch(tagGetType(flags)){ |
||||||
|
u32int keyOffset; |
||||||
|
case TagUTF8: |
||||||
|
if(ctx->read(ctx, ctx->buf+valueOffset, length) != (int)length) |
||||||
|
return -1; |
||||||
|
(ctx->buf+valueOffset)[length] = '\0'; |
||||||
|
for(keyOffset = 0; keyOffset != length;){ |
||||||
|
txtcb(ctx, tagTagType(ctx->buf), ctx->buf, ctx->buf+valueOffset+keyOffset); |
||||||
|
if(keyOffset += strlen(ctx->buf+valueOffset+keyOffset), keyOffset != length) |
||||||
|
keyOffset++; |
||||||
|
} |
||||||
|
break; |
||||||
|
case TagBinary: |
||||||
|
if(tagTagType(ctx->buf) == Timage) |
||||||
|
tagscallcb(ctx, Timage, ctx->buf, ctx->buf, ctx->seek(ctx, 0, 1), length, NULL); |
||||||
|
if(ctx->seek(ctx, length, 1) < 0) |
||||||
|
return -1; |
||||||
|
break; |
||||||
|
default: |
||||||
|
if(ctx->seek(ctx, length, 1) < 0) |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
idf_component_register( |
||||||
|
SRCS bits.c float.c wputils.c metadata.c unpack.c words.c |
||||||
|
INCLUDE_DIRS . |
||||||
|
) |
@ -0,0 +1,141 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// **** WAVPACK **** //
|
||||||
|
// Hybrid Lossless Wavefile Compressor //
|
||||||
|
// Copyright (c) 1998 - 2006 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 <string.h> |
||||||
|
#include <ctype.h> |
||||||
|
|
||||||
|
////////////////////////// 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, void *user_data, uint32_t 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; |
||||||
|
bs->user_data = user_data; |
||||||
|
} |
||||||
|
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) { |
||||||
|
uint32_t 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->user_data, 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; |
||||||
|
int32_t temp; |
||||||
|
|
||||||
|
while (*format) { |
||||||
|
switch (*format) { |
||||||
|
case 'L': |
||||||
|
temp = cp [0] + ((int32_t) cp [1] << 8) + ((int32_t) cp [2] << 16) + ((int32_t) cp [3] << 24); |
||||||
|
* (int32_t *) cp = temp; |
||||||
|
cp += 4; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'S': |
||||||
|
temp = cp [0] + (cp [1] << 8); |
||||||
|
* (short *) cp = (short) temp; |
||||||
|
cp += 2; |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
if (isdigit ((unsigned char) *format)) |
||||||
|
cp += *format - '0'; |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
format++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void native_to_little_endian (void *data, char *format) |
||||||
|
{ |
||||||
|
uchar *cp = (uchar *) data; |
||||||
|
int32_t temp; |
||||||
|
|
||||||
|
while (*format) { |
||||||
|
switch (*format) { |
||||||
|
case 'L': |
||||||
|
temp = * (int32_t *) 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 ((unsigned char) *format)) |
||||||
|
cp += *format - '0'; |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
format++; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// **** WAVPACK **** //
|
||||||
|
// Hybrid Lossless Wavefile Compressor //
|
||||||
|
// Copyright (c) 1998 - 2006 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, int32_t *values, int32_t num_values) |
||||||
|
{ |
||||||
|
int shift = wps->float_max_exp - wps->float_norm_exp + wps->float_shift; |
||||||
|
|
||||||
|
if (shift > 32) |
||||||
|
shift = 32; |
||||||
|
else if (shift < -32) |
||||||
|
shift = -32; |
||||||
|
|
||||||
|
while (num_values--) { |
||||||
|
if (shift > 0) |
||||||
|
*values <<= shift; |
||||||
|
else if (shift < 0) |
||||||
|
*values >>= -shift; |
||||||
|
|
||||||
|
if (*values > 8388607L) |
||||||
|
*values = 8388607L; |
||||||
|
else if (*values < -8388608L) |
||||||
|
*values = -8388608L; |
||||||
|
|
||||||
|
values++; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
Copyright (c) 1998 - 2006 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. |
@ -0,0 +1,105 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// **** WAVPACK **** //
|
||||||
|
// Hybrid Lossless Wavefile Compressor //
|
||||||
|
// Copyright (c) 1998 - 2006 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 (wpc->user_data, &wpmd->id, 1) || !wpc->infile (wpc->user_data, &tchar, 1)) |
||||||
|
return FALSE; |
||||||
|
|
||||||
|
wpmd->byte_length = tchar << 1; |
||||||
|
|
||||||
|
if (wpmd->id & ID_LARGE) { |
||||||
|
wpmd->id &= ~ID_LARGE; |
||||||
|
|
||||||
|
if (!wpc->infile (wpc->user_data, &tchar, 1)) |
||||||
|
return FALSE; |
||||||
|
|
||||||
|
wpmd->byte_length += (int32_t) tchar << 9;
|
||||||
|
|
||||||
|
if (!wpc->infile (wpc->user_data, &tchar, 1)) |
||||||
|
return FALSE; |
||||||
|
|
||||||
|
wpmd->byte_length += (int32_t) 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)) { |
||||||
|
uint32_t bytes_to_read = wpmd->byte_length + (wpmd->byte_length & 1); |
||||||
|
|
||||||
|
if (wpc->infile (wpc->user_data, wpc->read_buffer, bytes_to_read) != (int32_t) 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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
//////////////////////////////////////////////////////////////////////////// |
||||||
|
// **** WAVPACK **** // |
||||||
|
// Hybrid Lossless Wavefile Compressor // |
||||||
|
// Copyright (c) 1998 - 2006 Conifer Software. // |
||||||
|
// All Rights Reserved. // |
||||||
|
// Distributed under the BSD Software License (see license.txt) // |
||||||
|
//////////////////////////////////////////////////////////////////////////// |
||||||
|
|
||||||
|
This package contains a tiny version of the WavPack 4.40 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. The WIN32 macro |
||||||
|
is used for Windows to force the stdin and stdout streams to be binary mode. |
||||||
|
|
||||||
|
Compared to the previous version, this library has been optimized somewhat |
||||||
|
for improved performance in exchange for slightly larger code size. The |
||||||
|
library also now includes hand-optimized assembly language versions of the |
||||||
|
decorrelation functions for both the ColdFire (w/EMAC) and ARM processors. |
||||||
|
|
||||||
|
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. |
||||||
|
|
||||||
|
The previous version of this library would handle float files by returning |
||||||
|
32-bit floating-point data (even though no floating point math was used). |
||||||
|
Because this library would normally be used for simply playing WavPack |
||||||
|
files where lossless performance (beyond 24-bits) is not relevant, I have |
||||||
|
changed this behavior. Now, these files will generate clipped 24-bit data. |
||||||
|
The MODE_FLOAT flag will still be returned by WavpackGetMode(), but the |
||||||
|
BitsPerSample and BytesPerSample queries will be 24 and 3, respectfully. |
||||||
|
What this means is that an application that can handle 24-bit data will |
||||||
|
now be able to handle floating point data (assuming that the MODE_FLOAT |
||||||
|
flag is ignored). |
||||||
|
|
||||||
|
To make this code viable on the greatest number of hardware platforms, the |
||||||
|
following are true: |
||||||
|
|
||||||
|
speed is about 5x 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 40K |
||||||
|
no malloc / free usage |
||||||
|
|
||||||
|
To maintain compatibility on various platforms, the following conventions |
||||||
|
are used: |
||||||
|
|
||||||
|
a "char" must be exactly 8-bits |
||||||
|
a "short" must be exactly 16-bits |
||||||
|
an "int" must be at least 16-bits, but may be larger |
||||||
|
the "long" type is not used to avoid problems with 64-bit compilers |
||||||
|
|
||||||
|
Questions or comments should be directed to david@wavpack.com |
@ -0,0 +1,785 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// **** WAVPACK **** //
|
||||||
|
// Hybrid Lossless Wavefile Compressor //
|
||||||
|
// Copyright (c) 1998 - 2006 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 <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#define LOSSY_MUTE |
||||||
|
|
||||||
|
///////////////////////////// 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 != (uint32_t) -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, (unsigned char *) wpmd->data + wpmd->byte_length, NULL, NULL, 0); |
||||||
|
else if (wpmd->byte_length) |
||||||
|
bs_open_read (&wps->wvbits, wpc->read_buffer, wpc->read_buffer + sizeof (wpc->read_buffer), |
||||||
|
wpc->infile, wpc->user_data, 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; |
||||||
|
signed char *byteptr = wpmd->data; |
||||||
|
struct decorr_pass *dpp; |
||||||
|
|
||||||
|
if (!(wps->wphdr.flags & MONO_DATA)) |
||||||
|
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_DATA)) |
||||||
|
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_DATA)) |
||||||
|
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_DATA)) { |
||||||
|
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_DATA)) { |
||||||
|
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; |
||||||
|
uint32_t mask = 0; |
||||||
|
|
||||||
|
if (!bytecnt || bytecnt > 5) |
||||||
|
return FALSE; |
||||||
|
|
||||||
|
wpc->config.num_channels = *byteptr++; |
||||||
|
|
||||||
|
while (--bytecnt) { |
||||||
|
mask |= (uint32_t) *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 |= (int32_t) *byteptr++ << 8; |
||||||
|
wpc->config.flags |= (int32_t) *byteptr++ << 16; |
||||||
|
wpc->config.flags |= (int32_t) *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 int32_ts. 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.
|
||||||
|
|
||||||
|
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) |
||||||
|
extern void decorr_stereo_pass_cont_mcf5249 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); |
||||||
|
#elif defined(CPU_ARM) && !defined(SIMULATOR) |
||||||
|
extern void decorr_stereo_pass_cont_arm (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); |
||||||
|
extern void decorr_stereo_pass_cont_arml (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); |
||||||
|
#else |
||||||
|
static void decorr_stereo_pass_cont (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); |
||||||
|
#endif |
||||||
|
|
||||||
|
static void decorr_mono_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); |
||||||
|
static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); |
||||||
|
static void fixup_samples (WavpackStream *wps, int32_t *buffer, uint32_t sample_count); |
||||||
|
|
||||||
|
int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count) |
||||||
|
{ |
||||||
|
WavpackStream *wps = &wpc->stream; |
||||||
|
uint32_t flags = wps->wphdr.flags, crc = wps->crc, i; |
||||||
|
int32_t mute_limit = (1L << ((flags & MAG_MASK) >> MAG_LSB)) + 2; |
||||||
|
struct decorr_pass *dpp; |
||||||
|
int32_t *bptr, *eptr; |
||||||
|
int tcount; |
||||||
|
|
||||||
|
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_DATA) { |
||||||
|
eptr = buffer + sample_count; |
||||||
|
i = get_words (buffer, sample_count, flags, &wps->w, &wps->wvbits); |
||||||
|
|
||||||
|
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) |
||||||
|
decorr_mono_pass (dpp, buffer, sample_count); |
||||||
|
|
||||||
|
for (bptr = buffer; bptr < eptr; ++bptr) { |
||||||
|
if (labs (bptr [0]) > mute_limit) { |
||||||
|
i = bptr - buffer; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
crc = crc * 3 + bptr [0]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//////////////////// handle version 4 stereo data ////////////////////////
|
||||||
|
|
||||||
|
else { |
||||||
|
eptr = buffer + (sample_count * 2); |
||||||
|
i = get_words (buffer, sample_count, flags, &wps->w, &wps->wvbits); |
||||||
|
|
||||||
|
if (sample_count < 16) |
||||||
|
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) |
||||||
|
decorr_stereo_pass (dpp, buffer, sample_count); |
||||||
|
else |
||||||
|
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { |
||||||
|
decorr_stereo_pass (dpp, buffer, 8); |
||||||
|
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) |
||||||
|
decorr_stereo_pass_cont_mcf5249 (dpp, buffer + 16, sample_count - 8); |
||||||
|
#elif defined(CPU_ARM) && !defined(SIMULATOR) |
||||||
|
if (((flags & MAG_MASK) >> MAG_LSB) > 15) |
||||||
|
decorr_stereo_pass_cont_arml (dpp, buffer + 16, sample_count - 8); |
||||||
|
else |
||||||
|
decorr_stereo_pass_cont_arm (dpp, buffer + 16, sample_count - 8); |
||||||
|
#else |
||||||
|
decorr_stereo_pass_cont (dpp, buffer + 16, sample_count - 8); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
if (flags & JOINT_STEREO) |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
bptr [0] += (bptr [1] -= (bptr [0] >> 1)); |
||||||
|
|
||||||
|
if (labs (bptr [0]) > mute_limit || labs (bptr [1]) > mute_limit) { |
||||||
|
i = (bptr - buffer) / 2; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
crc = (crc * 3 + bptr [0]) * 3 + bptr [1]; |
||||||
|
} |
||||||
|
else |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
if (labs (bptr [0]) > mute_limit || labs (bptr [1]) > mute_limit) { |
||||||
|
i = (bptr - buffer) / 2; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
crc = (crc * 3 + bptr [0]) * 3 + bptr [1]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (i != sample_count) { |
||||||
|
memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8)); |
||||||
|
wps->mute_error = TRUE; |
||||||
|
i = sample_count; |
||||||
|
} |
||||||
|
|
||||||
|
fixup_samples (wps, buffer, i); |
||||||
|
|
||||||
|
if (flags & FALSE_STEREO) { |
||||||
|
int32_t *dptr = buffer + i * 2; |
||||||
|
int32_t *sptr = buffer + i; |
||||||
|
int32_t c = i; |
||||||
|
|
||||||
|
while (c--) { |
||||||
|
*--dptr = *--sptr; |
||||||
|
*--dptr = *sptr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
wps->sample_index += i; |
||||||
|
wps->crc = crc; |
||||||
|
|
||||||
|
return i; |
||||||
|
} |
||||||
|
|
||||||
|
static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) |
||||||
|
{ |
||||||
|
int32_t delta = dpp->delta, weight_A = dpp->weight_A, weight_B = dpp->weight_B; |
||||||
|
int32_t *bptr, *eptr = buffer + (sample_count * 2), sam_A, sam_B; |
||||||
|
int m, k; |
||||||
|
|
||||||
|
switch (dpp->term) { |
||||||
|
|
||||||
|
case 17: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; |
||||||
|
dpp->samples_A [1] = dpp->samples_A [0]; |
||||||
|
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0]; |
||||||
|
update_weight (weight_A, delta, sam_A, bptr [0]); |
||||||
|
bptr [0] = dpp->samples_A [0]; |
||||||
|
|
||||||
|
sam_A = 2 * dpp->samples_B [0] - dpp->samples_B [1]; |
||||||
|
dpp->samples_B [1] = dpp->samples_B [0]; |
||||||
|
dpp->samples_B [0] = apply_weight (weight_B, sam_A) + bptr [1]; |
||||||
|
update_weight (weight_B, delta, sam_A, bptr [1]); |
||||||
|
bptr [1] = dpp->samples_B [0]; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 18: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; |
||||||
|
dpp->samples_A [1] = dpp->samples_A [0]; |
||||||
|
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0]; |
||||||
|
update_weight (weight_A, delta, sam_A, bptr [0]); |
||||||
|
bptr [0] = dpp->samples_A [0]; |
||||||
|
|
||||||
|
sam_A = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; |
||||||
|
dpp->samples_B [1] = dpp->samples_B [0]; |
||||||
|
dpp->samples_B [0] = apply_weight (weight_B, sam_A) + bptr [1]; |
||||||
|
update_weight (weight_B, delta, sam_A, bptr [1]); |
||||||
|
bptr [1] = dpp->samples_B [0]; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
sam_A = dpp->samples_A [m]; |
||||||
|
dpp->samples_A [k] = apply_weight (weight_A, sam_A) + bptr [0]; |
||||||
|
update_weight (weight_A, delta, sam_A, bptr [0]); |
||||||
|
bptr [0] = dpp->samples_A [k]; |
||||||
|
|
||||||
|
sam_A = dpp->samples_B [m]; |
||||||
|
dpp->samples_B [k] = apply_weight (weight_B, sam_A) + bptr [1]; |
||||||
|
update_weight (weight_B, delta, sam_A, bptr [1]); |
||||||
|
bptr [1] = dpp->samples_B [k]; |
||||||
|
|
||||||
|
m = (m + 1) & (MAX_TERM - 1); |
||||||
|
k = (k + 1) & (MAX_TERM - 1); |
||||||
|
} |
||||||
|
|
||||||
|
if (m) { |
||||||
|
int32_t temp_samples [MAX_TERM]; |
||||||
|
|
||||||
|
memcpy (temp_samples, dpp->samples_A, sizeof (dpp->samples_A)); |
||||||
|
|
||||||
|
for (k = 0; k < MAX_TERM; k++, m++) |
||||||
|
dpp->samples_A [k] = temp_samples [m & (MAX_TERM - 1)]; |
||||||
|
|
||||||
|
memcpy (temp_samples, dpp->samples_B, sizeof (dpp->samples_B)); |
||||||
|
|
||||||
|
for (k = 0; k < MAX_TERM; k++, m++) |
||||||
|
dpp->samples_B [k] = temp_samples [m & (MAX_TERM - 1)]; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case -1: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
sam_A = bptr [0] + apply_weight (weight_A, dpp->samples_A [0]); |
||||||
|
update_weight_clip (weight_A, delta, dpp->samples_A [0], bptr [0]); |
||||||
|
bptr [0] = sam_A; |
||||||
|
dpp->samples_A [0] = bptr [1] + apply_weight (weight_B, sam_A); |
||||||
|
update_weight_clip (weight_B, delta, sam_A, bptr [1]); |
||||||
|
bptr [1] = dpp->samples_A [0]; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case -2: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
sam_B = bptr [1] + apply_weight (weight_B, dpp->samples_B [0]); |
||||||
|
update_weight_clip (weight_B, delta, dpp->samples_B [0], bptr [1]); |
||||||
|
bptr [1] = sam_B; |
||||||
|
dpp->samples_B [0] = bptr [0] + apply_weight (weight_A, sam_B); |
||||||
|
update_weight_clip (weight_A, delta, sam_B, bptr [0]); |
||||||
|
bptr [0] = dpp->samples_B [0]; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case -3: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
sam_A = bptr [0] + apply_weight (weight_A, dpp->samples_A [0]); |
||||||
|
update_weight_clip (weight_A, delta, dpp->samples_A [0], bptr [0]); |
||||||
|
sam_B = bptr [1] + apply_weight (weight_B, dpp->samples_B [0]); |
||||||
|
update_weight_clip (weight_B, delta, dpp->samples_B [0], bptr [1]); |
||||||
|
bptr [0] = dpp->samples_B [0] = sam_A; |
||||||
|
bptr [1] = dpp->samples_A [0] = sam_B; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
dpp->weight_A = weight_A; |
||||||
|
dpp->weight_B = weight_B; |
||||||
|
} |
||||||
|
|
||||||
|
#if (!defined(CPU_COLDFIRE) && !defined(CPU_ARM)) || defined(SIMULATOR) |
||||||
|
|
||||||
|
static void decorr_stereo_pass_cont (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) |
||||||
|
{ |
||||||
|
int32_t delta = dpp->delta, weight_A = dpp->weight_A, weight_B = dpp->weight_B; |
||||||
|
int32_t *bptr, *tptr, *eptr = buffer + (sample_count * 2), sam_A, sam_B; |
||||||
|
int k, i; |
||||||
|
|
||||||
|
switch (dpp->term) { |
||||||
|
|
||||||
|
case 17: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
sam_A = 2 * bptr [-2] - bptr [-4]; |
||||||
|
bptr [0] = apply_weight (weight_A, sam_A) + (sam_B = bptr [0]); |
||||||
|
update_weight (weight_A, delta, sam_A, sam_B); |
||||||
|
|
||||||
|
sam_A = 2 * bptr [-1] - bptr [-3]; |
||||||
|
bptr [1] = apply_weight (weight_B, sam_A) + (sam_B = bptr [1]); |
||||||
|
update_weight (weight_B, delta, sam_A, sam_B); |
||||||
|
} |
||||||
|
|
||||||
|
dpp->samples_B [0] = bptr [-1]; |
||||||
|
dpp->samples_A [0] = bptr [-2]; |
||||||
|
dpp->samples_B [1] = bptr [-3]; |
||||||
|
dpp->samples_A [1] = bptr [-4]; |
||||||
|
break; |
||||||
|
|
||||||
|
case 18: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
sam_A = (3 * bptr [-2] - bptr [-4]) >> 1; |
||||||
|
bptr [0] = apply_weight (weight_A, sam_A) + (sam_B = bptr [0]); |
||||||
|
update_weight (weight_A, delta, sam_A, sam_B); |
||||||
|
|
||||||
|
sam_A = (3 * bptr [-1] - bptr [-3]) >> 1; |
||||||
|
bptr [1] = apply_weight (weight_B, sam_A) + (sam_B = bptr [1]); |
||||||
|
update_weight (weight_B, delta, sam_A, sam_B); |
||||||
|
} |
||||||
|
|
||||||
|
dpp->samples_B [0] = bptr [-1]; |
||||||
|
dpp->samples_A [0] = bptr [-2]; |
||||||
|
dpp->samples_B [1] = bptr [-3]; |
||||||
|
dpp->samples_A [1] = bptr [-4]; |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
for (bptr = buffer, tptr = buffer - (dpp->term * 2); bptr < eptr; bptr += 2, tptr += 2) { |
||||||
|
bptr [0] = apply_weight (weight_A, tptr [0]) + (sam_A = bptr [0]); |
||||||
|
update_weight (weight_A, delta, tptr [0], sam_A); |
||||||
|
|
||||||
|
bptr [1] = apply_weight (weight_B, tptr [1]) + (sam_A = bptr [1]); |
||||||
|
update_weight (weight_B, delta, tptr [1], sam_A); |
||||||
|
} |
||||||
|
|
||||||
|
for (k = dpp->term - 1, i = 8; i--; k--) { |
||||||
|
dpp->samples_B [k & (MAX_TERM - 1)] = *--bptr; |
||||||
|
dpp->samples_A [k & (MAX_TERM - 1)] = *--bptr; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case -1: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
bptr [0] = apply_weight (weight_A, bptr [-1]) + (sam_A = bptr [0]); |
||||||
|
update_weight_clip (weight_A, delta, bptr [-1], sam_A); |
||||||
|
bptr [1] = apply_weight (weight_B, bptr [0]) + (sam_A = bptr [1]); |
||||||
|
update_weight_clip (weight_B, delta, bptr [0], sam_A); |
||||||
|
} |
||||||
|
|
||||||
|
dpp->samples_A [0] = bptr [-1]; |
||||||
|
break; |
||||||
|
|
||||||
|
case -2: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
bptr [1] = apply_weight (weight_B, bptr [-2]) + (sam_A = bptr [1]); |
||||||
|
update_weight_clip (weight_B, delta, bptr [-2], sam_A); |
||||||
|
bptr [0] = apply_weight (weight_A, bptr [1]) + (sam_A = bptr [0]); |
||||||
|
update_weight_clip (weight_A, delta, bptr [1], sam_A); |
||||||
|
} |
||||||
|
|
||||||
|
dpp->samples_B [0] = bptr [-2]; |
||||||
|
break; |
||||||
|
|
||||||
|
case -3: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr += 2) { |
||||||
|
bptr [0] = apply_weight (weight_A, bptr [-1]) + (sam_A = bptr [0]); |
||||||
|
update_weight_clip (weight_A, delta, bptr [-1], sam_A); |
||||||
|
bptr [1] = apply_weight (weight_B, bptr [-2]) + (sam_A = bptr [1]); |
||||||
|
update_weight_clip (weight_B, delta, bptr [-2], sam_A); |
||||||
|
} |
||||||
|
|
||||||
|
dpp->samples_A [0] = bptr [-1]; |
||||||
|
dpp->samples_B [0] = bptr [-2]; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
dpp->weight_A = weight_A; |
||||||
|
dpp->weight_B = weight_B; |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
static void decorr_mono_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) |
||||||
|
{ |
||||||
|
int32_t delta = dpp->delta, weight_A = dpp->weight_A; |
||||||
|
int32_t *bptr, *eptr = buffer + sample_count, sam_A; |
||||||
|
int m, k; |
||||||
|
|
||||||
|
switch (dpp->term) { |
||||||
|
|
||||||
|
case 17: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr++) { |
||||||
|
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; |
||||||
|
dpp->samples_A [1] = dpp->samples_A [0]; |
||||||
|
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0]; |
||||||
|
update_weight (weight_A, delta, sam_A, bptr [0]); |
||||||
|
bptr [0] = dpp->samples_A [0]; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 18: |
||||||
|
for (bptr = buffer; bptr < eptr; bptr++) { |
||||||
|
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; |
||||||
|
dpp->samples_A [1] = dpp->samples_A [0]; |
||||||
|
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0]; |
||||||
|
update_weight (weight_A, delta, sam_A, bptr [0]); |
||||||
|
bptr [0] = dpp->samples_A [0]; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr++) { |
||||||
|
sam_A = dpp->samples_A [m]; |
||||||
|
dpp->samples_A [k] = apply_weight (weight_A, sam_A) + bptr [0]; |
||||||
|
update_weight (weight_A, delta, sam_A, bptr [0]); |
||||||
|
bptr [0] = dpp->samples_A [k]; |
||||||
|
m = (m + 1) & (MAX_TERM - 1); |
||||||
|
k = (k + 1) & (MAX_TERM - 1); |
||||||
|
} |
||||||
|
|
||||||
|
if (m) { |
||||||
|
int32_t temp_samples [MAX_TERM]; |
||||||
|
|
||||||
|
memcpy (temp_samples, dpp->samples_A, sizeof (dpp->samples_A)); |
||||||
|
|
||||||
|
for (k = 0; k < MAX_TERM; k++, m++) |
||||||
|
dpp->samples_A [k] = temp_samples [m & (MAX_TERM - 1)]; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
dpp->weight_A = weight_A; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// 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, int32_t *buffer, uint32_t sample_count) |
||||||
|
{ |
||||||
|
uint32_t 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) { |
||||||
|
uint32_t 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; |
||||||
|
int32_t *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) { |
||||||
|
int32_t 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: |
||||||
|
default: |
||||||
|
min_shifted = (min_value = (int32_t) 0x80000000 >> shift) << shift; |
||||||
|
max_shifted = (max_value = (int32_t) 0x7FFFFFFF >> 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; |
||||||
|
} |
@ -0,0 +1,394 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// **** 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
|
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <sys/types.h> |
||||||
|
|
||||||
|
// This header file contains all the definitions required by WavPack.
|
||||||
|
|
||||||
|
#ifdef __BORLANDC__ |
||||||
|
typedef unsigned long uint32_t; |
||||||
|
typedef long int32_t; |
||||||
|
#elif defined(_WIN32) && !defined(__MINGW32__) |
||||||
|
#include <stdlib.h> |
||||||
|
typedef unsigned __int64 uint64_t; |
||||||
|
typedef unsigned __int32 uint32_t; |
||||||
|
typedef __int64 int64_t; |
||||||
|
typedef __int32 int32_t; |
||||||
|
#else |
||||||
|
#include <inttypes.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef unsigned char uchar; |
||||||
|
|
||||||
|
#if !defined(__GNUC__) || defined(WIN32) |
||||||
|
typedef unsigned short ushort; |
||||||
|
typedef unsigned int uint; |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
#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]; |
||||||
|
uint32_t ckSize; |
||||||
|
short version; |
||||||
|
uchar track_no, index_no; |
||||||
|
uint32_t 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 FALSE_STEREO 0x40000000 // block is stereo, but data is mono
|
||||||
|
|
||||||
|
#define IGNORED_FLAGS 0x18000000 // reserved, but ignore if encountered
|
||||||
|
#define NEW_SHAPING 0x20000000 // use IIR filter for negative shaping
|
||||||
|
#define UNKNOWN_FLAGS 0x80000000 // also reserved, but refuse decode if
|
||||||
|
// encountered
|
||||||
|
|
||||||
|
#define MONO_DATA (MONO_FLAG | FALSE_STEREO) |
||||||
|
|
||||||
|
#define MIN_STREAM_VERS 0x402 // lowest stream version we'll decode
|
||||||
|
#define MAX_STREAM_VERS 0x410 // highest stream version we'll decode
|
||||||
|
|
||||||
|
//////////////////////////// WavPack Metadata /////////////////////////////////
|
||||||
|
|
||||||
|
// This is an internal representation of metadata.
|
||||||
|
|
||||||
|
typedef struct { |
||||||
|
int32_t 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 num_channels, float_norm_exp; |
||||||
|
uint32_t 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_FAST_FLAG 0x200 // fast mode
|
||||||
|
#define CONFIG_HIGH_FLAG 0x800 // high quality mode
|
||||||
|
#define CONFIG_VERY_HIGH_FLAG 0x1000 // very high
|
||||||
|
#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_CREATE_EXE 0x40000 // create executable
|
||||||
|
#define CONFIG_CREATE_WVC 0x80000 // create correction file
|
||||||
|
#define CONFIG_OPTIMIZE_WVC 0x100000 // maximize bybrid compression
|
||||||
|
#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_OPTIMIZE_MONO 0x80000000 // optimize for mono streams posing as stereo
|
||||||
|
|
||||||
|
//////////////////////////////// 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 int32_t (*read_stream)(void *, void *, int32_t); |
||||||
|
|
||||||
|
typedef struct bs { |
||||||
|
uchar *buf, *end, *ptr; |
||||||
|
void (*wrap)(struct bs *bs); |
||||||
|
uint32_t file_bytes, sr; |
||||||
|
int error, bc; |
||||||
|
read_stream file; |
||||||
|
void *user_data; |
||||||
|
} Bitstream; |
||||||
|
|
||||||
|
#define MAX_NTERMS 16 |
||||||
|
#define MAX_TERM 8 |
||||||
|
|
||||||
|
struct decorr_pass { |
||||||
|
short term, delta, weight_A, weight_B; |
||||||
|
int32_t samples_A [MAX_TERM], samples_B [MAX_TERM]; |
||||||
|
}; |
||||||
|
|
||||||
|
struct entropy_data { |
||||||
|
uint32_t median [3], slow_level, error_limit; |
||||||
|
}; |
||||||
|
|
||||||
|
struct words_data { |
||||||
|
uint32_t bitrate_delta [2], bitrate_acc [2]; |
||||||
|
uint32_t pend_data, holding_one, zeros_acc; |
||||||
|
int holding_zero, pend_count; |
||||||
|
struct entropy_data c [2]; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
WavpackHeader wphdr; |
||||||
|
Bitstream wvbits; |
||||||
|
|
||||||
|
struct words_data w; |
||||||
|
|
||||||
|
int num_terms, mute_error; |
||||||
|
uint32_t sample_index, crc; |
||||||
|
|
||||||
|
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]; |
||||||
|
|
||||||
|
} 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; |
||||||
|
void *user_data; |
||||||
|
uint32_t 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, void *user_data, uint32_t 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 |= (int32_t)*((bs)->ptr) << (bs)->bc; \
|
||||||
|
(bs)->bc += 8; \
|
||||||
|
} \
|
||||||
|
*(value) = (bs)->sr; \
|
||||||
|
if ((bs)->bc > 32) { \
|
||||||
|
(bs)->bc -= (nbits); \
|
||||||
|
(bs)->sr = *((bs)->ptr) >> (8 - (bs)->bc); \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
(bs)->bc -= (nbits); \
|
||||||
|
(bs)->sr >>= (nbits); \
|
||||||
|
} \
|
||||||
|
} |
||||||
|
|
||||||
|
void little_endian_to_native (void *data, char *format); |
||||||
|
void native_to_little_endian (void *data, char *format); |
||||||
|
|
||||||
|
// These macros implement the weight application and update operations
|
||||||
|
// that are at the heart of the decorrelation loops. Note that when there
|
||||||
|
// are several alternative versions of the same macro (marked with PERFCOND)
|
||||||
|
// then the versions are functionally equivalent with respect to WavPack
|
||||||
|
// decoding and the user should choose the one that provides the best
|
||||||
|
// performance. This may be easier to check when NOT using the assembly
|
||||||
|
// language optimizations.
|
||||||
|
|
||||||
|
#if 1 // PERFCOND
|
||||||
|
#define apply_weight_i(weight, sample) ((weight * sample + 512) >> 10) |
||||||
|
#else |
||||||
|
#define apply_weight_i(weight, sample) ((((weight * sample) >> 8) + 2) >> 2) |
||||||
|
#endif |
||||||
|
|
||||||
|
#define apply_weight_f(weight, sample) (((((sample & 0xffffL) * weight) >> 9) + \ |
||||||
|
(((sample & ~0xffffL) >> 9) * weight) + 1) >> 1) |
||||||
|
|
||||||
|
#if 0 // PERFCOND
|
||||||
|
#define apply_weight(weight, sample) (sample != (short) sample ? \ |
||||||
|
apply_weight_f (weight, sample) : apply_weight_i (weight, sample)) |
||||||
|
#else |
||||||
|
#define apply_weight(weight, sample) ((int32_t)((weight * (int64_t) sample + 512) >> 10)) |
||||||
|
#endif |
||||||
|
|
||||||
|
#if 0 // PERFCOND
|
||||||
|
#define update_weight(weight, delta, source, result) \ |
||||||
|
if (source && result) { int32_t s = (int32_t) (source ^ result) >> 31; weight = (delta ^ s) + (weight - s); } |
||||||
|
#elif 0 |
||||||
|
#define update_weight(weight, delta, source, result) \ |
||||||
|
if (source && result) weight += (((source ^ result) >> 30) | 1) * delta |
||||||
|
#else |
||||||
|
#define update_weight(weight, delta, source, result) \ |
||||||
|
if (source && result) (source ^ result) < 0 ? (weight -= delta) : (weight += delta) |
||||||
|
#endif |
||||||
|
|
||||||
|
#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 |
||||||
|
|
||||||
|
// 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); |
||||||
|
int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t 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); |
||||||
|
int32_t get_words (int32_t *buffer, int nsamples, uint32_t flags, |
||||||
|
struct words_data *w, Bitstream *bs); |
||||||
|
int32_t exp2s (int log); |
||||||
|
int restore_weight (signed char weight); |
||||||
|
|
||||||
|
#define WORD_EOF (1L << 31) |
||||||
|
|
||||||
|
// float.c
|
||||||
|
|
||||||
|
int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd); |
||||||
|
void float_values (WavpackStream *wps, int32_t *values, int32_t num_values); |
||||||
|
|
||||||
|
// wputils.c
|
||||||
|
|
||||||
|
int WavpackOpenFileInput (WavpackContext *wpc, read_stream infile, void *user_data, 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 |
||||||
|
|
||||||
|
uint32_t WavpackUnpackSamples (WavpackContext *wpc, int32_t *buffer, uint32_t samples); |
||||||
|
uint32_t WavpackGetNumSamples (WavpackContext *wpc); |
||||||
|
uint32_t WavpackGetSampleIndex (WavpackContext *wpc); |
||||||
|
int WavpackGetNumErrors (WavpackContext *wpc); |
||||||
|
int WavpackLossyBlocks (WavpackContext *wpc); |
||||||
|
uint32_t WavpackGetSampleRate (WavpackContext *wpc); |
||||||
|
int WavpackGetBitsPerSample (WavpackContext *wpc); |
||||||
|
int WavpackGetBytesPerSample (WavpackContext *wpc); |
||||||
|
int WavpackGetNumChannels (WavpackContext *wpc); |
||||||
|
int WavpackGetReducedChannels (WavpackContext *wpc); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,560 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// **** WAVPACK **** //
|
||||||
|
// Hybrid Lossless Wavefile Compressor //
|
||||||
|
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||||
|
// All Rights Reserved. //
|
||||||
|
// Distributed under the BSD Software License (see license.txt) //
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// 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 <string.h> |
||||||
|
|
||||||
|
//////////////////////////////// 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) (((c->median [med]) >> 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() (c->median [0] += ((c->median [0] + DIV0) / DIV0) * 5) |
||||||
|
#define DEC_MED0() (c->median [0] -= ((c->median [0] + (DIV0-2)) / DIV0) * 2) |
||||||
|
#define INC_MED1() (c->median [1] += ((c->median [1] + DIV1) / DIV1) * 5) |
||||||
|
#define DEC_MED1() (c->median [1] -= ((c->median [1] + (DIV1-2)) / DIV1) * 2) |
||||||
|
#define INC_MED2() (c->median [2] += ((c->median [2] + DIV2) / DIV2) * 5) |
||||||
|
#define DEC_MED2() (c->median [2] -= ((c->median [2] + (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 |
||||||
|
}; |
||||||
|
|
||||||
|
static const char ones_count_table [] = { |
||||||
|
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, |
||||||
|
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6, |
||||||
|
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, |
||||||
|
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7, |
||||||
|
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, |
||||||
|
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6, |
||||||
|
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5, |
||||||
|
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 |
||||||
|
}; |
||||||
|
|
||||||
|
///////////////////////////// executable code ////////////////////////////////
|
||||||
|
|
||||||
|
void init_words (WavpackStream *wps) |
||||||
|
{ |
||||||
|
CLEAR (wps->w); |
||||||
|
} |
||||||
|
|
||||||
|
static int mylog2 (uint32_t 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_DATA) ? 6 : 12)) |
||||||
|
return FALSE; |
||||||
|
|
||||||
|
wps->w.c [0].median [0] = exp2s (byteptr [0] + (byteptr [1] << 8)); |
||||||
|
wps->w.c [0].median [1] = exp2s (byteptr [2] + (byteptr [3] << 8)); |
||||||
|
wps->w.c [0].median [2] = exp2s (byteptr [4] + (byteptr [5] << 8)); |
||||||
|
|
||||||
|
if (!(wps->wphdr.flags & MONO_DATA)) { |
||||||
|
wps->w.c [1].median [0] = exp2s (byteptr [6] + (byteptr [7] << 8)); |
||||||
|
wps->w.c [1].median [1] = exp2s (byteptr [8] + (byteptr [9] << 8)); |
||||||
|
wps->w.c [1].median [2] = 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.c [0].slow_level = exp2s (byteptr [0] + (byteptr [1] << 8)); |
||||||
|
byteptr += 2; |
||||||
|
|
||||||
|
if (!(wps->wphdr.flags & MONO_DATA)) { |
||||||
|
wps->w.c [1].slow_level = exp2s (byteptr [0] + (byteptr [1] << 8)); |
||||||
|
byteptr += 2; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
wps->w.bitrate_acc [0] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16; |
||||||
|
byteptr += 2; |
||||||
|
|
||||||
|
if (!(wps->wphdr.flags & MONO_DATA)) { |
||||||
|
wps->w.bitrate_acc [1] = (int32_t)(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_DATA)) { |
||||||
|
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.
|
||||||
|
|
||||||
|
void update_error_limit (struct words_data *w, uint32_t flags) |
||||||
|
{ |
||||||
|
int bitrate_0 = (w->bitrate_acc [0] += w->bitrate_delta [0]) >> 16; |
||||||
|
|
||||||
|
if (flags & MONO_DATA) { |
||||||
|
if (flags & HYBRID_BITRATE) { |
||||||
|
int slow_log_0 = (w->c [0].slow_level + SLO) >> SLS; |
||||||
|
|
||||||
|
if (slow_log_0 - bitrate_0 > -0x100) |
||||||
|
w->c [0].error_limit = exp2s (slow_log_0 - bitrate_0 + 0x100); |
||||||
|
else |
||||||
|
w->c [0].error_limit = 0; |
||||||
|
} |
||||||
|
else |
||||||
|
w->c [0].error_limit = exp2s (bitrate_0); |
||||||
|
} |
||||||
|
else { |
||||||
|
int bitrate_1 = (w->bitrate_acc [1] += w->bitrate_delta [1]) >> 16; |
||||||
|
|
||||||
|
if (flags & HYBRID_BITRATE) { |
||||||
|
int slow_log_0 = (w->c [0].slow_level + SLO) >> SLS; |
||||||
|
int slow_log_1 = (w->c [1].slow_level + SLO) >> SLS; |
||||||
|
|
||||||
|
if (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) |
||||||
|
w->c [0].error_limit = exp2s (slow_log_0 - bitrate_0 + 0x100); |
||||||
|
else |
||||||
|
w->c [0].error_limit = 0; |
||||||
|
|
||||||
|
if (slow_log_1 - bitrate_1 > -0x100) |
||||||
|
w->c [1].error_limit = exp2s (slow_log_1 - bitrate_1 + 0x100); |
||||||
|
else |
||||||
|
w->c [1].error_limit = 0; |
||||||
|
} |
||||||
|
else { |
||||||
|
w->c [0].error_limit = exp2s (bitrate_0); |
||||||
|
w->c [1].error_limit = exp2s (bitrate_1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static uint32_t read_code (Bitstream *bs, uint32_t 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.
|
||||||
|
|
||||||
|
int32_t get_words (int32_t *buffer, int nsamples, uint32_t flags, |
||||||
|
struct words_data *w, Bitstream *bs) |
||||||
|
{ |
||||||
|
register struct entropy_data *c = w->c; |
||||||
|
int csamples; |
||||||
|
|
||||||
|
if (!(flags & MONO_DATA)) |
||||||
|
nsamples *= 2; |
||||||
|
|
||||||
|
for (csamples = 0; csamples < nsamples; ++csamples) { |
||||||
|
uint32_t ones_count, low, mid, high; |
||||||
|
|
||||||
|
if (!(flags & MONO_DATA)) |
||||||
|
c = w->c + (csamples & 1); |
||||||
|
|
||||||
|
if (!(w->c [0].median [0] & ~1) && !w->holding_zero && !w->holding_one && !(w->c [1].median [0] & ~1)) { |
||||||
|
uint32_t mask; |
||||||
|
int cbits; |
||||||
|
|
||||||
|
if (w->zeros_acc) { |
||||||
|
if (--w->zeros_acc) { |
||||||
|
c->slow_level -= (c->slow_level + SLO) >> SLS; |
||||||
|
*buffer++ = 0; |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
for (cbits = 0; cbits < 33 && getbit (bs); ++cbits); |
||||||
|
|
||||||
|
if (cbits == 33) |
||||||
|
break; |
||||||
|
|
||||||
|
if (cbits < 2) |
||||||
|
w->zeros_acc = cbits; |
||||||
|
else { |
||||||
|
for (mask = 1, w->zeros_acc = 0; --cbits; mask <<= 1) |
||||||
|
if (getbit (bs)) |
||||||
|
w->zeros_acc |= mask; |
||||||
|
|
||||||
|
w->zeros_acc |= mask; |
||||||
|
} |
||||||
|
|
||||||
|
if (w->zeros_acc) { |
||||||
|
c->slow_level -= (c->slow_level + SLO) >> SLS; |
||||||
|
CLEAR (w->c [0].median); |
||||||
|
CLEAR (w->c [1].median); |
||||||
|
*buffer++ = 0; |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (w->holding_zero) |
||||||
|
ones_count = w->holding_zero = 0; |
||||||
|
else { |
||||||
|
int next8; |
||||||
|
|
||||||
|
if (bs->bc < 8) { |
||||||
|
if (++(bs->ptr) == bs->end) |
||||||
|
bs->wrap (bs); |
||||||
|
|
||||||
|
next8 = (bs->sr |= *(bs->ptr) << bs->bc) & 0xff; |
||||||
|
bs->bc += 8; |
||||||
|
} |
||||||
|
else |
||||||
|
next8 = bs->sr & 0xff; |
||||||
|
|
||||||
|
if (next8 == 0xff) { |
||||||
|
bs->bc -= 8; |
||||||
|
bs->sr >>= 8; |
||||||
|
|
||||||
|
for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (bs); ++ones_count); |
||||||
|
|
||||||
|
if (ones_count == (LIMIT_ONES + 1)) |
||||||
|
break; |
||||||
|
|
||||||
|
if (ones_count == LIMIT_ONES) { |
||||||
|
uint32_t mask; |
||||||
|
int cbits; |
||||||
|
|
||||||
|
for (cbits = 0; cbits < 33 && getbit (bs); ++cbits); |
||||||
|
|
||||||
|
if (cbits == 33) |
||||||
|
break; |
||||||
|
|
||||||
|
if (cbits < 2) |
||||||
|
ones_count = cbits; |
||||||
|
else { |
||||||
|
for (mask = 1, ones_count = 0; --cbits; mask <<= 1) |
||||||
|
if (getbit (bs)) |
||||||
|
ones_count |= mask; |
||||||
|
|
||||||
|
ones_count |= mask; |
||||||
|
} |
||||||
|
|
||||||
|
ones_count += LIMIT_ONES; |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
bs->bc -= (ones_count = ones_count_table [next8]) + 1; |
||||||
|
bs->sr >>= ones_count + 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (w->holding_one) { |
||||||
|
w->holding_one = ones_count & 1; |
||||||
|
ones_count = (ones_count >> 1) + 1; |
||||||
|
} |
||||||
|
else { |
||||||
|
w->holding_one = ones_count & 1; |
||||||
|
ones_count >>= 1; |
||||||
|
} |
||||||
|
|
||||||
|
w->holding_zero = ~w->holding_one & 1; |
||||||
|
} |
||||||
|
|
||||||
|
if ((flags & HYBRID_FLAG) && ((flags & MONO_DATA) || !(csamples & 1))) |
||||||
|
update_error_limit (w, flags); |
||||||
|
|
||||||
|
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 (!c->error_limit) |
||||||
|
mid = read_code (bs, high - low) + low; |
||||||
|
else while (high - low > c->error_limit) { |
||||||
|
if (getbit (bs)) |
||||||
|
mid = (high + (low = mid) + 1) >> 1; |
||||||
|
else |
||||||
|
mid = ((high = mid - 1) + low + 1) >> 1; |
||||||
|
} |
||||||
|
|
||||||
|
*buffer++ = getbit (bs) ? ~mid : mid; |
||||||
|
|
||||||
|
if (flags & HYBRID_BITRATE) |
||||||
|
c->slow_level = c->slow_level - ((c->slow_level + SLO) >> SLS) + mylog2 (mid); |
||||||
|
} |
||||||
|
|
||||||
|
return (flags & MONO_DATA) ? csamples : (csamples / 2); |
||||||
|
} |
||||||
|
|
||||||
|
// 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 uint32_t read_code (Bitstream *bs, uint32_t maxcode) |
||||||
|
{ |
||||||
|
int bitcount = count_bits (maxcode); |
||||||
|
uint32_t 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 mylog2 (uint32_t 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 log2 for the specified 32-bit signed value.
|
||||||
|
// All input values are valid and the return values are in the range of
|
||||||
|
// +/- 8192.
|
||||||
|
|
||||||
|
int log2s (int32_t value) |
||||||
|
{ |
||||||
|
return (value < 0) ? -mylog2 (-value) : mylog2 (value); |
||||||
|
} |
||||||
|
|
||||||
|
// This function returns the original integer represented by the supplied
|
||||||
|
// logarithm (at least within the provided accuracy). The log is signed,
|
||||||
|
// but since a full 32-bit value is returned this can be used for unsigned
|
||||||
|
// conversions as well (i.e. the input range is -8192 to +8447).
|
||||||
|
|
||||||
|
int32_t exp2s (int log) |
||||||
|
{ |
||||||
|
uint32_t 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 (signed char weight) |
||||||
|
{ |
||||||
|
int result; |
||||||
|
|
||||||
|
if ((result = (int) weight << 3) > 0) |
||||||
|
result += (result + 64) >> 7; |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,350 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// **** 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, void *user_data, 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).
|
||||||
|
|
||||||
|
int WavpackOpenFileInput (WavpackContext *wpc, read_stream infile, void *user_data, char *error) |
||||||
|
{ |
||||||
|
WavpackStream *wps = &wpc->stream; |
||||||
|
uint32_t bcount; |
||||||
|
|
||||||
|
//CLEAR (wpc);
|
||||||
|
wpc->infile = infile; |
||||||
|
wpc->user_data = user_data; |
||||||
|
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, wpc->user_data, &wps->wphdr); |
||||||
|
|
||||||
|
if (bcount == (uint32_t) -1) { |
||||||
|
strcpy (error, "not compatible with this version of WavPack file!"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
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 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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 1; |
||||||
|
} |
||||||
|
|
||||||
|
// 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, wpc->user_data, &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, void *user_data, 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 (user_data, 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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,200 @@ |
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// **** WAVPACK **** //
|
||||||
|
// Hybrid Lossless Wavefile Compressor //
|
||||||
|
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||||
|
// All Rights Reserved. //
|
||||||
|
// Distributed under the BSD Software License (see license.txt) //
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// wv_filter.c
|
||||||
|
|
||||||
|
// This is the main module for the demonstration WavPack command-line
|
||||||
|
// decoder filter. It uses the tiny "hardware" version of the decoder and
|
||||||
|
// accepts WavPack files on stdin and outputs a standard MS wav file to
|
||||||
|
// stdout. Note that this involves converting the data to little-endian
|
||||||
|
// (if the executing processor is not), possibly packing the data into
|
||||||
|
// fewer bytes per sample, and generating an appropriate riff wav header.
|
||||||
|
// Note that this is NOT the copy of the RIFF header that might be stored
|
||||||
|
// in the file, and any additional RIFF information and tags are lost.
|
||||||
|
// See wputils.c for further limitations.
|
||||||
|
|
||||||
|
#include "wavpack.h" |
||||||
|
|
||||||
|
#if defined(WIN32) |
||||||
|
#include <io.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
// These structures are used to place a wav riff header at the beginning of
|
||||||
|
// the output.
|
||||||
|
|
||||||
|
typedef struct { |
||||||
|
char ckID [4]; |
||||||
|
uint32_t ckSize; |
||||||
|
char formType [4]; |
||||||
|
} RiffChunkHeader; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
char ckID [4]; |
||||||
|
uint32_t ckSize; |
||||||
|
} ChunkHeader; |
||||||
|
|
||||||
|
#define ChunkHeaderFormat "4L" |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
ushort FormatTag, NumChannels; |
||||||
|
uint32_t SampleRate, BytesPerSecond; |
||||||
|
ushort BlockAlign, BitsPerSample; |
||||||
|
} WaveHeader; |
||||||
|
|
||||||
|
#define WaveHeaderFormat "SSLLSS" |
||||||
|
|
||||||
|
static uchar *format_samples (int bps, uchar *dst, int32_t *src, uint32_t samcnt); |
||||||
|
static int32_t read_bytes (void *buff, int32_t bcount); |
||||||
|
static int32_t temp_buffer [256]; |
||||||
|
|
||||||
|
int main () |
||||||
|
{ |
||||||
|
ChunkHeader FormatChunkHeader, DataChunkHeader; |
||||||
|
RiffChunkHeader RiffChunkHeader; |
||||||
|
WaveHeader WaveHeader; |
||||||
|
|
||||||
|
uint32_t total_unpacked_samples = 0, total_samples; |
||||||
|
int num_channels, bps; |
||||||
|
WavpackContext *wpc; |
||||||
|
char error [80]; |
||||||
|
|
||||||
|
#if defined(WIN32) |
||||||
|
setmode (fileno (stdin), O_BINARY); |
||||||
|
setmode (fileno (stdout), O_BINARY); |
||||||
|
#endif |
||||||
|
|
||||||
|
wpc = WavpackOpenFileInput (read_bytes, error); |
||||||
|
|
||||||
|
if (!wpc) { |
||||||
|
fputs (error, stderr); |
||||||
|
fputs ("\n", stderr); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
num_channels = WavpackGetReducedChannels (wpc); |
||||||
|
total_samples = WavpackGetNumSamples (wpc); |
||||||
|
bps = WavpackGetBytesPerSample (wpc); |
||||||
|
|
||||||
|
strncpy (RiffChunkHeader.ckID, "RIFF", sizeof (RiffChunkHeader.ckID)); |
||||||
|
RiffChunkHeader.ckSize = total_samples * num_channels * bps + sizeof (ChunkHeader) * 2 + sizeof (WaveHeader) + 4; |
||||||
|
strncpy (RiffChunkHeader.formType, "WAVE", sizeof (RiffChunkHeader.formType)); |
||||||
|
|
||||||
|
strncpy (FormatChunkHeader.ckID, "fmt ", sizeof (FormatChunkHeader.ckID)); |
||||||
|
FormatChunkHeader.ckSize = sizeof (WaveHeader); |
||||||
|
|
||||||
|
WaveHeader.FormatTag = 1; |
||||||
|
WaveHeader.NumChannels = num_channels; |
||||||
|
WaveHeader.SampleRate = WavpackGetSampleRate (wpc); |
||||||
|
WaveHeader.BlockAlign = num_channels * bps; |
||||||
|
WaveHeader.BytesPerSecond = WaveHeader.SampleRate * WaveHeader.BlockAlign; |
||||||
|
WaveHeader.BitsPerSample = WavpackGetBitsPerSample (wpc); |
||||||
|
|
||||||
|
strncpy (DataChunkHeader.ckID, "data", sizeof (DataChunkHeader.ckID)); |
||||||
|
DataChunkHeader.ckSize = total_samples * num_channels * bps; |
||||||
|
|
||||||
|
native_to_little_endian (&RiffChunkHeader, ChunkHeaderFormat); |
||||||
|
native_to_little_endian (&FormatChunkHeader, ChunkHeaderFormat); |
||||||
|
native_to_little_endian (&WaveHeader, WaveHeaderFormat); |
||||||
|
native_to_little_endian (&DataChunkHeader, ChunkHeaderFormat); |
||||||
|
|
||||||
|
if (!fwrite (&RiffChunkHeader, sizeof (RiffChunkHeader), 1, stdout) || |
||||||
|
!fwrite (&FormatChunkHeader, sizeof (FormatChunkHeader), 1, stdout) || |
||||||
|
!fwrite (&WaveHeader, sizeof (WaveHeader), 1, stdout) || |
||||||
|
!fwrite (&DataChunkHeader, sizeof (DataChunkHeader), 1, stdout)) { |
||||||
|
fputs ("can't write .WAV data, disk probably full!\n", stderr); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
while (1) { |
||||||
|
uint32_t samples_unpacked; |
||||||
|
|
||||||
|
samples_unpacked = WavpackUnpackSamples (wpc, temp_buffer, 256 / num_channels); |
||||||
|
total_unpacked_samples += samples_unpacked; |
||||||
|
|
||||||
|
if (samples_unpacked) { |
||||||
|
format_samples (bps, (uchar *) temp_buffer, temp_buffer, samples_unpacked *= num_channels); |
||||||
|
|
||||||
|
if (fwrite (temp_buffer, bps, samples_unpacked, stdout) != samples_unpacked) { |
||||||
|
fputs ("can't write .WAV data, disk probably full!\n", stderr); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!samples_unpacked) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
fflush (stdout); |
||||||
|
|
||||||
|
if (WavpackGetNumSamples (wpc) != (uint32_t) -1 && |
||||||
|
total_unpacked_samples != WavpackGetNumSamples (wpc)) { |
||||||
|
fputs ("incorrect number of samples!\n", stderr); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (WavpackGetNumErrors (wpc)) { |
||||||
|
fputs ("crc errors detected!\n", stderr); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int32_t read_bytes (void *buff, int32_t bcount) |
||||||
|
{ |
||||||
|
return fread (buff, 1, bcount, stdin); |
||||||
|
} |
||||||
|
|
||||||
|
// Reformat samples from longs in processor's native endian mode to
|
||||||
|
// little-endian data with (possibly) less than 4 bytes / sample.
|
||||||
|
|
||||||
|
static uchar *format_samples (int bps, uchar *dst, int32_t *src, uint32_t samcnt) |
||||||
|
{ |
||||||
|
int32_t temp; |
||||||
|
|
||||||
|
switch (bps) { |
||||||
|
|
||||||
|
case 1: |
||||||
|
while (samcnt--) |
||||||
|
*dst++ = *src++ + 128; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 2: |
||||||
|
while (samcnt--) { |
||||||
|
*dst++ = (uchar)(temp = *src++); |
||||||
|
*dst++ = (uchar)(temp >> 8); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 3: |
||||||
|
while (samcnt--) { |
||||||
|
*dst++ = (uchar)(temp = *src++); |
||||||
|
*dst++ = (uchar)(temp >> 8); |
||||||
|
*dst++ = (uchar)(temp >> 16); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 4: |
||||||
|
while (samcnt--) { |
||||||
|
*dst++ = (uchar)(temp = *src++); |
||||||
|
*dst++ = (uchar)(temp >> 8); |
||||||
|
*dst++ = (uchar)(temp >> 16); |
||||||
|
*dst++ = (uchar)(temp >> 24); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return dst; |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2025 ayumi <ayumi@noreply.codeberg.org> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-3.0-only |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cstddef> |
||||||
|
#include <cstdint> |
||||||
|
#include <memory> |
||||||
|
#include <optional> |
||||||
|
#include <string> |
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#include "wavpack.h" |
||||||
|
#include "sample.hpp" |
||||||
|
|
||||||
|
#include "codec.hpp" |
||||||
|
|
||||||
|
namespace codecs { |
||||||
|
|
||||||
|
class WavPackDecoder : public ICodec { |
||||||
|
public: |
||||||
|
WavPackDecoder(); |
||||||
|
~WavPackDecoder(); |
||||||
|
|
||||||
|
auto OpenStream(std::shared_ptr<IStream> input, uint32_t offset) |
||||||
|
-> cpp::result<OutputFormat, Error> override; |
||||||
|
|
||||||
|
auto DecodeTo(std::span<sample::Sample> destination) |
||||||
|
-> cpp::result<OutputInfo, Error> override; |
||||||
|
|
||||||
|
WavPackDecoder(const WavPackDecoder&) = delete; |
||||||
|
WavPackDecoder& operator=(const WavPackDecoder&) = delete; |
||||||
|
|
||||||
|
private: |
||||||
|
std::shared_ptr<IStream> input_; |
||||||
|
WavpackContext wavpack_; |
||||||
|
int32_t *buf_; |
||||||
|
uint8_t bitdepth_; |
||||||
|
uint8_t channels_; |
||||||
|
size_t size_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace codecs
|
@ -0,0 +1,161 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2025 ayumi <ayumi@noreply.codeberg.org> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-3.0-only |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "wavpack.hpp" |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <cstring> |
||||||
|
#include <algorithm> |
||||||
|
#include <optional> |
||||||
|
|
||||||
|
#include "esp_heap_caps.h" |
||||||
|
#include "codec.hpp" |
||||||
|
#include "esp_log.h" |
||||||
|
#include "result.hpp" |
||||||
|
#include "sample.hpp" |
||||||
|
#include "types.hpp" |
||||||
|
|
||||||
|
namespace codecs { |
||||||
|
|
||||||
|
[[maybe_unused]] static constexpr const char kTag[] = "wavpack"; |
||||||
|
// kBufSize and audio::kCodecBufferLength must be equal
|
||||||
|
static constexpr const size_t kBufSize = 2048; |
||||||
|
|
||||||
|
static inline constexpr auto loadLe16(std::byte* data) -> uint16_t { |
||||||
|
return *reinterpret_cast<uint16_t*>(data); |
||||||
|
} |
||||||
|
|
||||||
|
static inline constexpr auto loadLe32(std::byte* data) -> uint32_t { |
||||||
|
return *reinterpret_cast<uint32_t*>(data); |
||||||
|
} |
||||||
|
|
||||||
|
static auto readProc(void* data, void* buf, long size) -> long { |
||||||
|
IStream* stream = static_cast<IStream*>(data); |
||||||
|
const int32_t res = stream->Read({ |
||||||
|
static_cast<std::byte*>(buf), |
||||||
|
static_cast<std::span<std::byte>::size_type>(size) |
||||||
|
}); |
||||||
|
return res < 0 ? 0 : res; |
||||||
|
} |
||||||
|
|
||||||
|
WavPackDecoder::WavPackDecoder() : input_(), buf_() { |
||||||
|
buf_ = static_cast<int32_t*>( |
||||||
|
heap_caps_malloc( |
||||||
|
kBufSize * sizeof(int32_t), |
||||||
|
MALLOC_CAP_INTERNAL | MALLOC_CAP_CACHE_ALIGNED |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
WavPackDecoder::~WavPackDecoder() { |
||||||
|
heap_caps_free(buf_); |
||||||
|
} |
||||||
|
|
||||||
|
auto WavPackDecoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset) |
||||||
|
-> cpp::result<OutputFormat, ICodec::Error> { |
||||||
|
char error[80]; |
||||||
|
input_ = input; |
||||||
|
wavpack_ = {}; |
||||||
|
if (!WavpackOpenFileInput(&wavpack_, readProc, input_.get(), error)) { |
||||||
|
ESP_LOGE(kTag, "WavpackOpenFileInput: %s", error); |
||||||
|
return cpp::fail(Error::kMalformedData); |
||||||
|
} |
||||||
|
|
||||||
|
channels_ = WavpackGetReducedChannels(&wavpack_); |
||||||
|
bitdepth_ = WavpackGetBitsPerSample(&wavpack_); |
||||||
|
size_ = kBufSize / channels_; |
||||||
|
const std::optional total = WavpackGetNumSamples(&wavpack_) == -1 |
||||||
|
? std::nullopt |
||||||
|
: std::optional( |
||||||
|
static_cast<uint64_t>(WavpackGetNumSamples(&wavpack_)) * channels_ |
||||||
|
); |
||||||
|
const auto rate = WavpackGetSampleRate(&wavpack_); |
||||||
|
if (offset && total && input_.get()->CanSeek()) { |
||||||
|
const uint32_t want = offset * rate - 1; |
||||||
|
if (total < want) { |
||||||
|
ESP_LOGE(kTag, "seeking: offset points beyond the end of the file"); |
||||||
|
return cpp::fail(Error::kInternalError); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t target; |
||||||
|
input_->SeekTo(0, IStream::SeekFrom::kStartOfStream); |
||||||
|
while (true) { |
||||||
|
std::byte header[32]; |
||||||
|
input_->Read(header); |
||||||
|
if (memcmp(header, "wvpk", 4) != 0) { |
||||||
|
ESP_LOGE(kTag, "seeking: header expected, but not found"); |
||||||
|
return cpp::fail(Error::kMalformedData); |
||||||
|
} |
||||||
|
const uint32_t size = loadLe32(header + 4); |
||||||
|
const uint16_t version = loadLe16(header + 8); |
||||||
|
if (version < 0x402 || version > 0x410) { |
||||||
|
ESP_LOGE(kTag, "seeking: bad WavPack version (%x)", version); |
||||||
|
return cpp::fail(Error::kMalformedData); |
||||||
|
} |
||||||
|
const uint32_t blockIndex = loadLe32(header + 16); |
||||||
|
const uint32_t blockSamples = loadLe32(header + 20); |
||||||
|
if (want >= blockIndex && want <= blockIndex + blockSamples) { |
||||||
|
input_->SeekTo(-32, IStream::SeekFrom::kCurrentPosition); |
||||||
|
target = want - blockIndex; |
||||||
|
break; |
||||||
|
} |
||||||
|
input_->SeekTo(size - 24, IStream::SeekFrom::kCurrentPosition); |
||||||
|
} |
||||||
|
|
||||||
|
wavpack_ = {}; |
||||||
|
if (!WavpackOpenFileInput(&wavpack_, readProc, input_.get(), error)) { |
||||||
|
ESP_LOGE(kTag, "WavpackOpenFileInput: %s", error); |
||||||
|
return cpp::fail(Error::kMalformedData); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t samples = 0; |
||||||
|
for (size_t i = 0, n = target / size_; i < n; i++) |
||||||
|
samples += WavpackUnpackSamples(&wavpack_, buf_, size_); |
||||||
|
samples += WavpackUnpackSamples(&wavpack_, buf_, target % size_); |
||||||
|
if (WavpackGetNumErrors(&wavpack_) != 0) { |
||||||
|
ESP_LOGE(kTag, "CRC error"); |
||||||
|
return cpp::fail(Error::kMalformedData); |
||||||
|
} else if (samples != target || WavpackGetSampleIndex(&wavpack_) != want) { |
||||||
|
ESP_LOGE(kTag, "seeking: seeking unsuccessful: want %lu, got %lu", |
||||||
|
target, samples |
||||||
|
); |
||||||
|
return cpp::fail(Error::kInternalError); |
||||||
|
} |
||||||
|
} else if (offset && (!total || !input_.get()->CanSeek())) { |
||||||
|
ESP_LOGE(kTag, "seeking: can’t seek"); |
||||||
|
return cpp::fail(Error::kInternalError); |
||||||
|
} |
||||||
|
|
||||||
|
const auto size = input->Size(); |
||||||
|
return OutputFormat{ |
||||||
|
.num_channels = channels_, |
||||||
|
.sample_rate_hz = rate, |
||||||
|
.total_samples = total, |
||||||
|
.bitrate_kbps = size && total |
||||||
|
? std::optional( |
||||||
|
((double)size.value() * 8.0) |
||||||
|
/ ((double)total.value() / channels_ / rate) / 1000 |
||||||
|
) |
||||||
|
: std::nullopt, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
auto WavPackDecoder::DecodeTo(std::span<sample::Sample> output) |
||||||
|
-> cpp::result<OutputInfo, Error> { |
||||||
|
const auto size = std::min(size_, output.size() / channels_); |
||||||
|
const auto samples = WavpackUnpackSamples(&wavpack_, buf_, size) * channels_; |
||||||
|
if (WavpackGetNumErrors(&wavpack_) != 0) { |
||||||
|
ESP_LOGE(kTag, "CRC error"); |
||||||
|
return cpp::fail(Error::kMalformedData); |
||||||
|
} |
||||||
|
for (size_t i = 0; i < samples; i++) |
||||||
|
output[i] = sample::FromSigned(buf_[i], bitdepth_); |
||||||
|
return OutputInfo{ |
||||||
|
.samples_written = samples, |
||||||
|
.is_stream_finished = samples == 0, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace codecs
|
Loading…
Reference in new issue