From 180dcd01f2321e18814deea6eaa5dc2f7b0e917a Mon Sep 17 00:00:00 2001 From: vspader Date: Tue, 7 Jun 2005 04:16:15 +0000 Subject: [PATCH] Fixed up CVS, added missing files. --- Libraries/FAAD2/Files/common/faad/aacinfo.c | 366 +++ Libraries/FAAD2/Files/common/faad/aacinfo.h | 54 + .../FAAD2/Files/common/faad/filestream.c | 470 ++++ .../FAAD2/Files/common/faad/filestream.h | 57 + Libraries/FAAD2/Files/common/faad/getopt.c | 755 ++++++ Libraries/FAAD2/Files/common/faad/getopt.h | 130 + Libraries/FAAD2/Files/common/faad/id3v2tag.c | 1124 +++++++++ Libraries/FAAD2/Files/common/faad/id3v2tag.h | 54 + Libraries/FAAD2/Files/common/mp4ff/drms.c | 1321 +++++++++++ Libraries/FAAD2/Files/common/mp4ff/drms.h | 30 + .../FAAD2/Files/common/mp4ff/drmstables.h | 288 +++ Libraries/FAAD2/Files/common/mp4ff/mp4atom.c | 901 +++++++ Libraries/FAAD2/Files/common/mp4ff/mp4ff.c | 474 ++++ Libraries/FAAD2/Files/common/mp4ff/mp4ff.h | 131 + .../Files/common/mp4ff/mp4ff_int_types.h | 23 + Libraries/FAAD2/Files/common/mp4ff/mp4ffint.h | 373 +++ Libraries/FAAD2/Files/common/mp4ff/mp4meta.c | 426 ++++ .../FAAD2/Files/common/mp4ff/mp4sample.c | 152 ++ .../FAAD2/Files/common/mp4ff/mp4tagupdate.c | 645 +++++ Libraries/FAAD2/Files/common/mp4ff/mp4util.c | 188 ++ Libraries/Shorten/Files/AUTHORS | 11 + Libraries/Shorten/Files/COPYING | 340 +++ Libraries/Shorten/Files/ChangeLog | 6 + Libraries/Shorten/Files/Makefile.am | 1 + Libraries/Shorten/Files/NEWS | 2 + Libraries/Shorten/Files/README | 45 + Libraries/Shorten/Files/TODO | 7 + Libraries/Shorten/Files/configure.ac | 78 + Libraries/Shorten/Files/shorten/Makefile.am | 1 + .../Shorten/Files/shorten/doc/LICENSE.shorten | 20 + .../Files/shorten/doc/xmms-shn/AUTHORS | 1 + .../Files/shorten/doc/xmms-shn/CREDITS | 39 + .../Shorten/Files/shorten/doc/xmms-shn/NEWS | 1 + .../Shorten/Files/shorten/doc/xmms-shn/README | 346 +++ .../Shorten/Files/shorten/include/bitshift.h | 33 + .../Shorten/Files/shorten/include/config.h | 96 + .../Shorten/Files/shorten/include/config.h.in | 96 + .../Shorten/Files/shorten/include/decode.h | 45 + Libraries/Shorten/Files/shorten/include/shn.h | 284 +++ .../Shorten/Files/shorten/include/shorten.h | 220 ++ .../Shorten/Files/shorten/src/Makefile.am | 21 + Libraries/Shorten/Files/shorten/src/array.c | 40 + Libraries/Shorten/Files/shorten/src/convert.c | 42 + Libraries/Shorten/Files/shorten/src/decode.c | 1154 +++++++++ Libraries/Shorten/Files/shorten/src/fixio.c | 276 +++ Libraries/Shorten/Files/shorten/src/id3v2.c | 120 + Libraries/Shorten/Files/shorten/src/misc.c | 149 ++ Libraries/Shorten/Files/shorten/src/output.c | 99 + Libraries/Shorten/Files/shorten/src/seek.c | 284 +++ Libraries/Shorten/Files/shorten/src/shorten.c | 54 + .../Shorten/Files/shorten/src/sulawalaw.c | 192 ++ Libraries/Shorten/Files/shorten/src/vario.c | 146 ++ Libraries/Shorten/Files/shorten/src/wave.c | 267 +++ .../Shorten/Files/shorten/util/Makefile.am | 5 + .../Shorten/Files/shorten/util/Sulawalaw.c | 192 ++ Libraries/Shorten/Files/shorten/util/array.c | 63 + Libraries/Shorten/Files/shorten/util/exit.c | 230 ++ .../Shorten/Files/shorten/util/mkbshift.c | 110 + .../Shorten/Files/shorten/util/mkbshift.h | 237 ++ Libraries/Shorten/Files/src/Makefile.am | 7 + Libraries/Shorten/Files/src/libinputshorten.c | 250 ++ Libraries/WavPack/Files/AUTHORS | 0 Libraries/WavPack/Files/ChangeLog | 106 + Libraries/WavPack/Files/Makefile.am | 27 + Libraries/WavPack/Files/NEWS | 0 Libraries/WavPack/Files/README | 86 + Libraries/WavPack/Files/autogen.sh | 7 + Libraries/WavPack/Files/bits.c | 259 ++ Libraries/WavPack/Files/compile | 142 ++ Libraries/WavPack/Files/configure.ac | 62 + Libraries/WavPack/Files/depcomp | 529 +++++ Libraries/WavPack/Files/extra1.c | 563 +++++ Libraries/WavPack/Files/extra2.c | 792 +++++++ Libraries/WavPack/Files/float.c | 371 +++ Libraries/WavPack/Files/format.txt | 96 + Libraries/WavPack/Files/install-sh | 323 +++ Libraries/WavPack/Files/license.txt | 25 + Libraries/WavPack/Files/md5.c | 263 ++ Libraries/WavPack/Files/md5.h | 26 + Libraries/WavPack/Files/metadata.c | 310 +++ Libraries/WavPack/Files/missing | 357 +++ Libraries/WavPack/Files/pack.c | 1413 +++++++++++ Libraries/WavPack/Files/unpack.c | 1453 ++++++++++++ Libraries/WavPack/Files/unpack3.c | 2010 ++++++++++++++++ Libraries/WavPack/Files/unpack3.h | 113 + Libraries/WavPack/Files/utils.c | 640 +++++ Libraries/WavPack/Files/wavpack.c | 1448 +++++++++++ Libraries/WavPack/Files/wavpack.h | 656 +++++ Libraries/WavPack/Files/wavpack.pc.in | 11 + Libraries/WavPack/Files/words.c | 1437 +++++++++++ Libraries/WavPack/Files/wputils.c | 2107 +++++++++++++++++ Libraries/WavPack/Files/wputils.h | 163 ++ Libraries/WavPack/Files/wvunpack.c | 926 ++++++++ 93 files changed, 30293 insertions(+) create mode 100644 Libraries/FAAD2/Files/common/faad/aacinfo.c create mode 100644 Libraries/FAAD2/Files/common/faad/aacinfo.h create mode 100644 Libraries/FAAD2/Files/common/faad/filestream.c create mode 100644 Libraries/FAAD2/Files/common/faad/filestream.h create mode 100644 Libraries/FAAD2/Files/common/faad/getopt.c create mode 100644 Libraries/FAAD2/Files/common/faad/getopt.h create mode 100644 Libraries/FAAD2/Files/common/faad/id3v2tag.c create mode 100644 Libraries/FAAD2/Files/common/faad/id3v2tag.h create mode 100644 Libraries/FAAD2/Files/common/mp4ff/drms.c create mode 100644 Libraries/FAAD2/Files/common/mp4ff/drms.h create mode 100644 Libraries/FAAD2/Files/common/mp4ff/drmstables.h create mode 100644 Libraries/FAAD2/Files/common/mp4ff/mp4atom.c create mode 100644 Libraries/FAAD2/Files/common/mp4ff/mp4ff.c create mode 100644 Libraries/FAAD2/Files/common/mp4ff/mp4ff.h create mode 100644 Libraries/FAAD2/Files/common/mp4ff/mp4ff_int_types.h create mode 100644 Libraries/FAAD2/Files/common/mp4ff/mp4ffint.h create mode 100644 Libraries/FAAD2/Files/common/mp4ff/mp4meta.c create mode 100644 Libraries/FAAD2/Files/common/mp4ff/mp4sample.c create mode 100644 Libraries/FAAD2/Files/common/mp4ff/mp4tagupdate.c create mode 100644 Libraries/FAAD2/Files/common/mp4ff/mp4util.c create mode 100644 Libraries/Shorten/Files/AUTHORS create mode 100644 Libraries/Shorten/Files/COPYING create mode 100644 Libraries/Shorten/Files/ChangeLog create mode 100644 Libraries/Shorten/Files/Makefile.am create mode 100644 Libraries/Shorten/Files/NEWS create mode 100644 Libraries/Shorten/Files/README create mode 100644 Libraries/Shorten/Files/TODO create mode 100644 Libraries/Shorten/Files/configure.ac create mode 100644 Libraries/Shorten/Files/shorten/Makefile.am create mode 100644 Libraries/Shorten/Files/shorten/doc/LICENSE.shorten create mode 100644 Libraries/Shorten/Files/shorten/doc/xmms-shn/AUTHORS create mode 100644 Libraries/Shorten/Files/shorten/doc/xmms-shn/CREDITS create mode 100644 Libraries/Shorten/Files/shorten/doc/xmms-shn/NEWS create mode 100644 Libraries/Shorten/Files/shorten/doc/xmms-shn/README create mode 100644 Libraries/Shorten/Files/shorten/include/bitshift.h create mode 100644 Libraries/Shorten/Files/shorten/include/config.h create mode 100644 Libraries/Shorten/Files/shorten/include/config.h.in create mode 100644 Libraries/Shorten/Files/shorten/include/decode.h create mode 100644 Libraries/Shorten/Files/shorten/include/shn.h create mode 100644 Libraries/Shorten/Files/shorten/include/shorten.h create mode 100644 Libraries/Shorten/Files/shorten/src/Makefile.am create mode 100644 Libraries/Shorten/Files/shorten/src/array.c create mode 100644 Libraries/Shorten/Files/shorten/src/convert.c create mode 100644 Libraries/Shorten/Files/shorten/src/decode.c create mode 100644 Libraries/Shorten/Files/shorten/src/fixio.c create mode 100644 Libraries/Shorten/Files/shorten/src/id3v2.c create mode 100644 Libraries/Shorten/Files/shorten/src/misc.c create mode 100644 Libraries/Shorten/Files/shorten/src/output.c create mode 100644 Libraries/Shorten/Files/shorten/src/seek.c create mode 100644 Libraries/Shorten/Files/shorten/src/shorten.c create mode 100644 Libraries/Shorten/Files/shorten/src/sulawalaw.c create mode 100644 Libraries/Shorten/Files/shorten/src/vario.c create mode 100644 Libraries/Shorten/Files/shorten/src/wave.c create mode 100644 Libraries/Shorten/Files/shorten/util/Makefile.am create mode 100644 Libraries/Shorten/Files/shorten/util/Sulawalaw.c create mode 100644 Libraries/Shorten/Files/shorten/util/array.c create mode 100644 Libraries/Shorten/Files/shorten/util/exit.c create mode 100644 Libraries/Shorten/Files/shorten/util/mkbshift.c create mode 100644 Libraries/Shorten/Files/shorten/util/mkbshift.h create mode 100644 Libraries/Shorten/Files/src/Makefile.am create mode 100644 Libraries/Shorten/Files/src/libinputshorten.c create mode 100644 Libraries/WavPack/Files/AUTHORS create mode 100644 Libraries/WavPack/Files/ChangeLog create mode 100644 Libraries/WavPack/Files/Makefile.am create mode 100644 Libraries/WavPack/Files/NEWS create mode 100644 Libraries/WavPack/Files/README create mode 100755 Libraries/WavPack/Files/autogen.sh create mode 100644 Libraries/WavPack/Files/bits.c create mode 100755 Libraries/WavPack/Files/compile create mode 100644 Libraries/WavPack/Files/configure.ac create mode 100755 Libraries/WavPack/Files/depcomp create mode 100644 Libraries/WavPack/Files/extra1.c create mode 100644 Libraries/WavPack/Files/extra2.c create mode 100644 Libraries/WavPack/Files/float.c create mode 100644 Libraries/WavPack/Files/format.txt create mode 100755 Libraries/WavPack/Files/install-sh create mode 100644 Libraries/WavPack/Files/license.txt create mode 100644 Libraries/WavPack/Files/md5.c create mode 100644 Libraries/WavPack/Files/md5.h create mode 100644 Libraries/WavPack/Files/metadata.c create mode 100755 Libraries/WavPack/Files/missing create mode 100644 Libraries/WavPack/Files/pack.c create mode 100644 Libraries/WavPack/Files/unpack.c create mode 100644 Libraries/WavPack/Files/unpack3.c create mode 100644 Libraries/WavPack/Files/unpack3.h create mode 100644 Libraries/WavPack/Files/utils.c create mode 100644 Libraries/WavPack/Files/wavpack.c create mode 100644 Libraries/WavPack/Files/wavpack.h create mode 100644 Libraries/WavPack/Files/wavpack.pc.in create mode 100644 Libraries/WavPack/Files/words.c create mode 100644 Libraries/WavPack/Files/wputils.c create mode 100644 Libraries/WavPack/Files/wputils.h create mode 100644 Libraries/WavPack/Files/wvunpack.c diff --git a/Libraries/FAAD2/Files/common/faad/aacinfo.c b/Libraries/FAAD2/Files/common/faad/aacinfo.c new file mode 100644 index 000000000..878273270 --- /dev/null +++ b/Libraries/FAAD2/Files/common/faad/aacinfo.c @@ -0,0 +1,366 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include +#include +//#include "filestream.h" +#include "aacinfo.h" + +#define ADIF_MAX_SIZE 30 /* Should be enough */ +#define ADTS_MAX_SIZE 10 /* Should be enough */ + +static int sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000}; + +static int read_ADIF_header(FILE *file, faadAACInfo *info) +{ + int bitstream; + unsigned char buffer[ADIF_MAX_SIZE]; + int skip_size = 0; + int sf_idx; + + /* Get ADIF header data */ + info->headertype = 1; + + if(fread(buffer, 1, ADIF_MAX_SIZE, file) < 0) + return -1; + + /* copyright string */ + if(buffer[0] & 0x80) + skip_size += 9; /* skip 9 bytes */ + + bitstream = buffer[0 + skip_size] & 0x10; + info->bitrate = ((unsigned int)(buffer[0 + skip_size] & 0x0F)<<19)| + ((unsigned int)buffer[1 + skip_size]<<11)| + ((unsigned int)buffer[2 + skip_size]<<3)| + ((unsigned int)buffer[3 + skip_size] & 0xE0); + + if (bitstream == 0) + { + info->object_type = ((buffer[6 + skip_size]&0x01)<<1)|((buffer[7 + skip_size]&0x80)>>7); + sf_idx = (buffer[7 + skip_size]&0x78)>>3; + } else { + info->object_type = (buffer[4 + skip_size] & 0x18)>>3; + sf_idx = ((buffer[4 + skip_size] & 0x07)<<1)|((buffer[5 + skip_size] & 0x80)>>7); + } + info->sampling_rate = sample_rates[sf_idx]; + + return 0; +} + +static int read_ADTS_header(FILE *file, faadAACInfo *info, + unsigned long **seek_table, int *seek_table_len, + int tagsize, int no_seek_table) +{ + /* Get ADTS header data */ + unsigned char buffer[ADTS_MAX_SIZE]; + int frames, framesinsec=0, t_framelength = 0, frame_length, sr_idx, ID; + int second = 0, pos; + int i; + float frames_per_sec = 0; + unsigned long bytes; + unsigned long *tmp_seek_table = NULL; + + info->headertype = 2; + + /* Read all frames to ensure correct time and bitrate */ + for(frames=0; /* */; frames++, framesinsec++) + { + /* If streaming, only go until we hit 5 seconds worth */ +/* if(file->http) + { + if(frames >= 43 * 5) + { + break; + } + } +*/ + pos = ftell(file);//tell_filestream(file); + + /* 12 bit SYNCWORD */ + bytes = fread(buffer, 1, ADTS_MAX_SIZE, file); + + if(bytes != ADTS_MAX_SIZE) + { + /* Bail out if no syncword found */ + break; + } + + /* check syncword */ + if (!((buffer[0] == 0xFF)&&((buffer[1] & 0xF6) == 0xF0))) + break; + + if(!frames) + { + /* fixed ADTS header is the same for every frame, so we read it only once */ + /* Syncword found, proceed to read in the fixed ADTS header */ + ID = buffer[1] & 0x08; + info->object_type = (buffer[2]&0xC0)>>6; + sr_idx = (buffer[2]&0x3C)>>2; + info->channels = ((buffer[2]&0x01)<<2)|((buffer[3]&0xC0)>>6); + + frames_per_sec = sample_rates[sr_idx] / 1024.f; + } + + /* ...and the variable ADTS header */ + if (ID == 0) { + info->version = 4; + } else { /* MPEG-2 */ + info->version = 2; + } + frame_length = ((((unsigned int)buffer[3] & 0x3)) << 11) + | (((unsigned int)buffer[4]) << 3) | (buffer[5] >> 5); + + t_framelength += frame_length; + +// if(!file->http) + { + if(framesinsec == 43) + framesinsec = 0; + + if(framesinsec == 0 && seek_table_len) + { + tmp_seek_table = (unsigned long *) realloc(tmp_seek_table, (second + 1) * sizeof(unsigned long)); + tmp_seek_table[second] = pos; + } + if(framesinsec == 0) + second++; + } + + /* NOTE: While simply skipping ahead by reading may seem to be more work than seeking, + it is actually much faster, and keeps compatibility with streaming */ + //F THAT + fseek(file, frame_length - ADTS_MAX_SIZE, SEEK_CUR); +/* for(i=0; i < frame_length - ADTS_MAX_SIZE; i++) + { + if(read_byte_filestream(file) < 0) + break; + } +*/ + } + + if(seek_table_len) + { + *seek_table_len = second; + *seek_table = tmp_seek_table; + } + + info->sampling_rate = sample_rates[sr_idx]; + info->bitrate = (int)(((t_framelength / frames) * (info->sampling_rate/1024.0)) +0.5)*8; + +/* if(file->http) + { + // Since we only use 5 seconds of aac data to get a rough bitrate, we must use a different + // method of calculating the overall length + if(filelength_filestream(file)) + { + info->length = (int)((filelength_filestream(file)/(((info->bitrate*8)/1024)*16))*1000); + } + else + { + // Since the server didnt tell us how long the file is, + // we have no way of determining length + info->length = 0; + } + } + else +*/ { + info->length = (int)((float)(frames/frames_per_sec))*1000; + } + + return 0; +} + +int get_AAC_format(FILE *file, faadAACInfo *info, + unsigned long **seek_table, int *seek_table_len, + int no_seek_table) +{ + unsigned long tagsize; + char buffer[10]; + unsigned long file_len; + unsigned char adxx_id[5]; + unsigned long tmp; + + memset(info, 0, sizeof(faadAACInfo)); + + fseek(file, 0, SEEK_END); + file_len = ftell(file); + + /* Skip the tag, if it's there */ + tmp = fread(buffer, 1, 10, file); + + if (StringComp(buffer, "ID3", 3) == 0) + { + unsigned int i; + + /* high bit is not used */ + tagsize = (buffer[6] << 21) | (buffer[7] << 14) | + (buffer[8] << 7) | (buffer[9] << 0); + + fseek(file, tagsize, SEEK_CUR); + + tagsize += 10; + } + else + { + tagsize = 0; + + fseek(file, 0, SEEK_SET); + } + + if(file_len) + file_len -= tagsize; + + tmp = fread(adxx_id, 1, 2, file); + //seek_filestream(file, tagsize, FILE_BEGIN); + + adxx_id[5-1] = 0; + info->length = 0; + + /* Determine the header type of the file, check the first two bytes */ + if(strncasecmp((char *)adxx_id, "AD", 2) == 0) + { + /* We think its an ADIF header, but check the rest just to make sure */ + tmp = fread(adxx_id+2, 1, 2, file); + + if(strncasecmp((char *)adxx_id, "ADIF", 4) == 0) + { + read_ADIF_header(file, info); + } + } + else + { + /* No ADIF, check for ADTS header */ + if ((adxx_id[0] == 0xFF)&&((adxx_id[1] & 0xF6) == 0xF0)) + { + /* ADTS header located */ + /* Since this routine must work for streams, we can't use the seek function to go backwards, thus + we have to use a quick hack as seen below to go back where we need to. */ + + fseek(file, -2, SEEK_CUR); +/* if(file->buffer_offset >= 2) + { + // simple seeking hack, though not really safe, the probability of it causing a problem is low. + file->buffer_offset -= 2; + file->file_offset -= 2; + } +*/ + read_ADTS_header(file, info, seek_table, seek_table_len, tagsize, + no_seek_table); + } + else + { + /* Unknown/headerless AAC file, assume format: */ + info->version = 2; + info->bitrate = 128000; + info->sampling_rate = 44100; + info->channels = 2; + info->headertype = 0; + info->object_type = 1; + } + } + + return 0; +} + +int StringComp(char const *str1, char const *str2, unsigned long len) +{ + signed int c1 = 0, c2 = 0; + + while (len--) { + c1 = *str1++; + c2 = *str2++; + + if (c1 == 0 || c1 != c2) + break; + } + + return c1 - c2; +} + +#ifdef TEST +/* Program to test aacinfo functionality */ + +#include + +void main(int argc, char *argv[]) +{ + faadAACInfo info; + unsigned long *seek_table = NULL; + int seek_table_len = 0; + char *header, *object; + + if (argc < 2) + { + fprintf(stderr, "USAGE: aacinfo aacfile.aac\n"); + return; + } + + get_AAC_format(argv[1], &info, &seek_table, &seek_table_len, 0); + + fprintf(stdout, "MPEG version: %d\n", info.version); + fprintf(stdout, "channels: %d\n", info.channels); + fprintf(stdout, "sampling_rate: %d\n", info.sampling_rate); + fprintf(stdout, "bitrate: %d\n", info.bitrate); + fprintf(stdout, "length: %.3f\n", (float)info.length/1000.0); + + switch (info.object_type) + { + case 0: + object = "MAIN"; + break; + case 1: + object = "LC"; + break; + case 2: + object = "SSR"; + break; + case 3: + object = "LTP"; + break; + } + fprintf(stdout, "object_type: %s\n", object); + + switch (info.headertype) + { + case 0: + header = "RAW"; + break; + case 1: + header = "ADIF"; + break; + case 2: + header = "ADTS"; + break; + } + fprintf(stdout, "headertype: %s\n", header); +} + +#endif \ No newline at end of file diff --git a/Libraries/FAAD2/Files/common/faad/aacinfo.h b/Libraries/FAAD2/Files/common/faad/aacinfo.h new file mode 100644 index 000000000..eb83ef78a --- /dev/null +++ b/Libraries/FAAD2/Files/common/faad/aacinfo.h @@ -0,0 +1,54 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#ifndef AACINFO_H__ +#define AACINFO_H__ + +//#include "filestream.h" +#include + +typedef struct { + int version; + int channels; + int sampling_rate; + int bitrate; + int length; + int object_type; + int headertype; +} faadAACInfo; + +int get_AAC_format(FILE *file, faadAACInfo *info, + unsigned long **seek_table, int *seek_table_len, + int no_seek_table); + +static int read_ADIF_header(FILE *file, faadAACInfo *info); +static int read_ADTS_header(FILE *file, faadAACInfo *info, + unsigned long **seek_table, int *seek_table_len, + int tagsize, int no_seek_table); +int StringComp(char const *str1, char const *str2, unsigned long len); + +#endif diff --git a/Libraries/FAAD2/Files/common/faad/filestream.c b/Libraries/FAAD2/Files/common/faad/filestream.c new file mode 100644 index 000000000..25983276b --- /dev/null +++ b/Libraries/FAAD2/Files/common/faad/filestream.c @@ -0,0 +1,470 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +/* Not very portable yet */ + +#include // Note: Must be *before* windows.h +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include "filestream.h" +#include "aacinfo.h" + +/* TEMPROARY HACK */ +#define CommonExit(A) MessageBox(NULL, A, "FAAD Plugin", MB_OK) + +int winsock_init=0; // 0=winsock not initialized, 1=success +long m_local_buffer_size = 64; +long m_stream_buffer_size = 128; + +FILE_STREAM *open_filestream(char *filename) +{ + FILE_STREAM *fs; + + if(StringComp(filename,"http://", 7) == 0) + { + fs = (FILE_STREAM *)LocalAlloc(LPTR, sizeof(FILE_STREAM) + m_stream_buffer_size * 1024); + + if(fs == NULL) + return NULL; + + fs->data = (unsigned char *)&fs[1]; + + if(http_file_open(filename, fs) < 0) + { + LocalFree(fs); + return NULL; + } + + fs->http = 1; + } + else + { + fs = (FILE_STREAM*)LocalAlloc(LPTR, sizeof(FILE_STREAM) + m_local_buffer_size * 1024); + + if(fs == NULL) + return NULL; + + fs->data = (unsigned char *)&fs[1]; + + fs->stream = CreateFile(filename, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, 0, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0); + if (fs->stream == INVALID_HANDLE_VALUE) + { + LocalFree(fs); + return NULL; + } + + fs->http = 0; + } + + fs->buffer_length = 0; + fs->buffer_offset = 0; + fs->file_offset = 0; + + return fs; +} + +int read_byte_filestream(FILE_STREAM *fs) +{ + if(fs->buffer_offset == fs->buffer_length) + { + fs->buffer_offset = 0; + + if(fs->http) + fs->buffer_length = recv(fs->inetStream, fs->data, m_stream_buffer_size * 1024, 0); + else + ReadFile(fs->stream, fs->data, m_local_buffer_size * 1024, &fs->buffer_length, 0); + + if(fs->buffer_length <= 0) + { + if(fs->http) + { + int x; + x = WSAGetLastError(); + + if(x == 0) + { + /* Equivalent of a successful EOF for HTTP */ + } + } + + fs->buffer_length = 0; + return -1; + } + } + + fs->file_offset++; + + return fs->data[fs->buffer_offset++]; +} + +int read_buffer_filestream(FILE_STREAM *fs, void *data, int length) +{ + int i, tmp; + unsigned char *data2 = (unsigned char *)data; + + for(i=0; i < length; i++) + { + if((tmp = read_byte_filestream(fs)) < 0) + { + if(i) + { + break; + } + else + { + return -1; + } + } + data2[i] = tmp; + } + + return i; +} + +unsigned long filelength_filestream(FILE_STREAM *fs) +{ + unsigned long fsize; + + if (fs->http) + { + fsize = fs->http_file_length; + } + else + { + fsize = GetFileSize(fs->stream, NULL); + } + + return fsize; +} + +void seek_filestream(FILE_STREAM *fs, unsigned long offset, int mode) +{ + if(fs->http) + { + return; + } + + SetFilePointer(fs->stream, offset, NULL, mode); + + if(mode == FILE_CURRENT) + fs->file_offset += offset; + else if(mode == FILE_END) + fs->file_offset = filelength_filestream(fs) + offset; + else + fs->file_offset = offset; + + fs->buffer_length = 0; + fs->buffer_offset = 0; +} + +unsigned long tell_filestream(FILE_STREAM *fs) +{ + return fs->file_offset; +} + +void close_filestream(FILE_STREAM *fs) +{ + if(fs) + { + if (fs->http) + { + if (fs->inetStream) + { + /* The 'proper' way to close a TCP connection */ + if(fs->inetStream) + { + CloseTCP(fs->inetStream); + } + } + } + else + { + if(fs->stream) + CloseHandle(fs->stream); + } + + LocalFree(fs); + fs = NULL; + } +} + +int WinsockInit() +{ + /* Before using winsock, you must load the DLL... */ + WSADATA wsaData; + + /* Load version 2.0 */ + if (WSAStartup( MAKEWORD( 2, 0 ), &wsaData )) + { + /* Disable streaming */ + return -1; + } + + winsock_init = 1; + + return 0; +} + +void WinsockDeInit() +{ + /* Unload the DLL */ + + if(winsock_init) + WSACleanup(); +} + +int FindCRLF(char *str) +{ + int i; + + for(i=0; i != lstrlen(str) && str[i] != '\r'; i++); + + return i; +} + +void CloseTCP(int s) +{ + char tempbuf[1024]; + + /* Set the socket to ignore any new incoming data */ + shutdown(s, 1); + + /* Get any old remaining data */ + while(recv(s, tempbuf, 1024, 0) > 0); + + /* Deallocate the socket */ + closesocket(s); +} + +int resolve_host(char *host, SOCKADDR_IN *sck_addr, unsigned short remote_port) +{ + HOSTENT *hp; + + if (isalpha(host[0])) + { + /* server address is a name */ + hp = gethostbyname(host); + } + else + { + unsigned long addr; + /* Convert nnn.nnn address to a usable one */ + addr = inet_addr(host); + hp = gethostbyaddr((char *)&addr, 4, AF_INET); + } + + if (hp == NULL) + { + char tmp[128]; + wsprintf(tmp, "Error resolving host address [%s]!\n", host); + CommonExit(tmp); + return -1; + } + + ZeroMemory(sck_addr, sizeof(SOCKADDR_IN)); + sck_addr->sin_family = AF_INET; + sck_addr->sin_port = htons(remote_port); + CopyMemory(&sck_addr->sin_addr, hp->h_addr, hp->h_length); + + return 0; +} + +int http_file_open(char *url, FILE_STREAM *fs) +{ + SOCKET sck; + SOCKADDR_IN host; + char server[1024], file[1024], request[1024], *temp = NULL, *tmpfile = NULL; + int i, j, port = 80, bytes_recv, http_code; + + /* No winsock, no streaming */ + if(!winsock_init) + { + return -1; + } + + url += 7; // Skip over http:// + + /* Extract data from the URL */ + for(i=0; url[i] != '/' && url[i] != ':' && url[i] != 0; i++); + + ZeroMemory(server, 1024); + CopyMemory(server, url, i); + + if(url[i] == ':') + { + /* A certain port was specified */ + port = atol(url + (i + 1)); + } + + for(; url[i] != '/' && url[i] != 0; i++); + + ZeroMemory(file, 1024); + + CopyMemory(file, url + i, lstrlen(url)); + + /* END OF URL PARSING */ + + /* Create a TCP/IP socket */ + sck = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if(sck == INVALID_SOCKET) + { + CommonExit("Error creating TCP/IP new socket"); + return -1; + } + + /* Resolve the host address (turn www.blah.com into an IP) */ + if(resolve_host(server, &host, (unsigned short)port)) + { + CommonExit("Error resolving host address"); + CloseTCP(sck); + return -1; + } + + /* Connect to the server */ + if(connect(sck, (SOCKADDR *)&host, sizeof(SOCKADDR)) == SOCKET_ERROR) + { + CommonExit("Error connecting to remote server"); + CloseTCP(sck); + return -1; + } + + tmpfile = calloc(1, (strlen(file) * 3) + 1); + + /* Encode URL */ + for(i=0, j=0; i < (int)strlen(file); i++) + { + if((unsigned char)file[i] <= 31 || (unsigned char)file[i] >= 127) + { + /* encode ASCII-control characters */ + wsprintf(tmpfile + j, "%%%X", (unsigned char)file[i]); + j += 3; + continue; + } + else + { + switch(file[i]) + { + /* encode characters that could confuse some servers */ + case ' ': + case '"': + case '>': + case '<': + case '#': + case '%': + case '{': + case '}': + case '|': + case '\\': + case '^': + case '~': + case '[': + case ']': + case '`': + + wsprintf(tmpfile + j, "%%%X", (unsigned char)file[i]); + j += 3; + continue; + } + } + + tmpfile[j] = file[i]; + j++; + } + + wsprintf(request, "GET %s\r\n\r\n", tmpfile); + + free(tmpfile); + + /* Send the request */ + if(send(sck, request, lstrlen(request), 0) <= 0) + { + /* Error sending data */ + CloseTCP(sck); + return -1; + } + + ZeroMemory(request, 1024); + + /* Send the request */ + if((bytes_recv = recv(sck, request, 1024, 0)) <= 0) + { + /* Error sending data */ + CloseTCP(sck); + return -1; + } + + if(StringComp(request,"HTTP/1.", 7) != 0) + { + /* Invalid header */ + CloseTCP(sck); + return -1; + } + + http_code = atol(request + 9); + + if(http_code < 200 || http_code > 299) + { + /* HTTP error */ + CloseTCP(sck); + return -1; + } + + // Search for a length field + fs->http_file_length = 0; + + /* Limit search to only 20 loops */ + if((temp = strstr(request, "Content-Length: ")) != NULL) + { + /* Has a content-length field, copy into structure */ + fs->http_file_length = atol(temp + 16); + } + + /* Copy the handle data into the structure */ + fs->inetStream = sck; + + /* Copy any excess data beyond the header into the filestream buffers */ + temp = strstr(request, "\r\n\r\n"); + + if(temp) + { + temp += 4; + } + + if(temp - request < bytes_recv) + { + memcpy(fs->data, temp, (temp - request) - bytes_recv); + fs->buffer_length = (temp - request) - bytes_recv; + fs->buffer_offset = 0; + } + + return 0; +} diff --git a/Libraries/FAAD2/Files/common/faad/filestream.h b/Libraries/FAAD2/Files/common/faad/filestream.h new file mode 100644 index 000000000..dea8c3e7e --- /dev/null +++ b/Libraries/FAAD2/Files/common/faad/filestream.h @@ -0,0 +1,57 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#ifndef FILESTREAM_H +#define FILESTREAM_H + +typedef struct { + HANDLE stream; + unsigned short inetStream; + unsigned char *data; + int http; + int buffer_offset; + int buffer_length; + int file_offset; + int http_file_length; +} FILE_STREAM; + +extern long m_local_buffer_size; +extern long m_stream_buffer_size; + +FILE_STREAM *open_filestream(char *filename); +int read_byte_filestream(FILE_STREAM *fs); +int read_buffer_filestream(FILE_STREAM *fs, void *data, int length); +unsigned long filelength_filestream(FILE_STREAM *fs); +void close_filestream(FILE_STREAM *fs); +void seek_filestream(FILE_STREAM *fs, unsigned long offset, int mode); +unsigned long tell_filestream(FILE_STREAM *fs); +int http_file_open(char *url, FILE_STREAM *fs); + +int WinsockInit(); +void WinsockDeInit(); +void CloseTCP(int s); +#endif \ No newline at end of file diff --git a/Libraries/FAAD2/Files/common/faad/getopt.c b/Libraries/FAAD2/Files/common/faad/getopt.c new file mode 100644 index 000000000..185d49b80 --- /dev/null +++ b/Libraries/FAAD2/Files/common/faad/getopt.c @@ -0,0 +1,755 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __STDC__ +# ifndef const +# define const +# endif +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) || !__MacOSX__ + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +#define BAD_OPTION '\0' +int optopt = BAD_OPTION; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#define my_strlen strlen +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#if __STDC__ || defined(PROTO) +extern char *getenv(const char *name); +extern int strcmp (const char *s1, const char *s2); +extern int strncmp(const char *s1, const char *s2, unsigned int n); + +static int my_strlen(const char *s); +static char *my_index (const char *str, int chr); +#else +extern char *getenv (); +#endif + +static int +my_strlen (str) + const char *str; +{ + int n = 0; + while (*str++) + n++; + return n; +} + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. + + To perform the swap, we first reverse the order of all elements. So + all options now come before all non options, but they are in the + wrong order. So we put back the options and non options in original + order by reversing them again. For example: + original input: a b c -x -y + reverse all: -y -x c b a + reverse options: -x -y c b a + reverse non options: -x -y a b c +*/ + +#if __STDC__ || defined(PROTO) +static void exchange (char **argv); +#endif + +static void +exchange (argv) + char **argv; +{ + char *temp, **first, **last; + + /* Reverse all the elements [first_nonopt, optind) */ + first = &argv[first_nonopt]; + last = &argv[optind-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + /* Put back the options in order */ + first = &argv[first_nonopt]; + first_nonopt += (optind - last_nonopt); + last = &argv[first_nonopt - 1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + + /* Put back the non options in order */ + first = &argv[first_nonopt]; + last_nonopt = optind; + last = &argv[last_nonopt-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return BAD_OPTION after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return BAD_OPTION. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound = 0; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == my_strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += my_strlen (nextchar); + optind++; + return BAD_OPTION; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += my_strlen (nextchar); + return BAD_OPTION; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += my_strlen (nextchar); + return optstring[0] == ':' ? ':' : BAD_OPTION; + } + } + nextchar += my_strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return BAD_OPTION; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return BAD_OPTION; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = BAD_OPTION; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case BAD_OPTION: + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/Libraries/FAAD2/Files/common/faad/getopt.h b/Libraries/FAAD2/Files/common/faad/getopt.h new file mode 100644 index 000000000..3fd127714 --- /dev/null +++ b/Libraries/FAAD2/Files/common/faad/getopt.h @@ -0,0 +1,130 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __MacOSX__ + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; +#endif + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +//#if __STDC__ || defined(PROTO) +#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#endif /* not __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +//#else /* not __STDC__ */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +//extern int getopt_long (); +//extern int getopt_long_only (); + +//extern int _getopt_internal (); +//#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/Libraries/FAAD2/Files/common/faad/id3v2tag.c b/Libraries/FAAD2/Files/common/faad/id3v2tag.c new file mode 100644 index 000000000..1ab639820 --- /dev/null +++ b/Libraries/FAAD2/Files/common/faad/id3v2tag.c @@ -0,0 +1,1124 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include + +#include + +HWND m_hwndList; + +LPSTR ID3Frames[] = +{ + "No known frame", + "Audio encryption", + "Attached picture", + "Comments", + "Commercial frame", + "Encryption method registration", + "Equalization", + "Event timing codes", + "General encapsulated object", + "Group identification registration", + "Involved people list", + "Linked information", + "Music CD identifier", + "MPEG location lookup table", + "Ownership frame", + "Private frame", + "Play counter", + "Popularimeter", + "Position synchronisation frame", + "Recommended buffer size", + "Relative volume adjustment", + "Reverb", + "Synchronized lyric", + "Synchronized tempo codes", + "Album title", + "BPM (beats per minute)", + "Composer", + "Genre", //"Content type", + "Copyright message", + "Date", + "Playlist delay", + "Encoded by", + "Lyricist", + "File type", + "Time", + "Content group description", + "Title", + "Subtitle", + "Initial key", + "Language(s)", + "Length", + "Media type", + "Original album title", + "Original filename", + "Original lyricist(s)", + "Original artist(s)", + "Original release year", + "File owner", + "Lead performer(s)", + "Band/orchestra/accompaniment", + "Conductor/performer refinement", + "Interpreted, remixed, or otherwise modified by", + "Part of a set", + "Publisher", + "Track number", + "Recording dates", + "Internet radio station name", + "Internet radio station owner", + "Size", + "ISRC (international standard recording code)", + "Software/Hardware and settings used for encoding", + "User defined text information", + "Year", + "Unique file identifier", + "Terms of use", + "Unsynchronized lyric", + "Commercial information", + "Copyright/Legal information", + "Official audio file webpage", + "Official artist webpage", + "Official audio source webpage", + "Official internet radio station homepage", + "Payment", + "Official publisher webpage", + "User defined URL link", + "Encrypted meta frame (id3v2.2.x)", + "Compressed meta frame (id3v2.2.1)" +}; + +ID3GENRES ID3Genres[]= +{ + 123, "Acapella", + 34, "Acid", + 74, "Acid Jazz", + 73, "Acid Punk", + 99, "Acoustic", + 20, "Alternative", + 40, "AlternRock", + 26, "Ambient", + 90, "Avantgarde", + 116, "Ballad", + 41, "Bass", + 85, "Bebob", + 96, "Big Band", + 89, "Bluegrass", + 0, "Blues", + 107, "Booty Bass", + 65, "Cabaret", + 88, "Celtic", + 104, "Chamber Music", + 102, "Chanson", + 97, "Chorus", + 61, "Christian Rap", + 1, "Classic Rock", + 32, "Classical", + 112, "Club", + 57, "Comedy", + 2, "Country", + 58, "Cult", + 3, "Dance", + 125, "Dance Hall", + 50, "Darkwave", + 254, "Data", + 22, "Death Metal", + 4, "Disco", + 55, "Dream", + 122, "Drum Solo", + 120, "Duet", + 98, "Easy Listening", + 52, "Electronic", + 48, "Ethnic", + 124, "Euro-House", + 25, "Euro-Techno", + 54, "Eurodance", + 84, "Fast Fusion", + 80, "Folk", + 81, "Folk-Rock", + 115, "Folklore", + 119, "Freestyle", + 5, "Funk", + 30, "Fusion", + 36, "Game", + 59, "Gangsta", + 38, "Gospel", + 49, "Gothic", + 91, "Gothic Rock", + 6, "Grunge", + 79, "Hard Rock", + 7, "Hip-Hop", + 35, "House", + 100, "Humour", + 19, "Industrial", + 33, "Instrumental", + 46, "Instrumental Pop", + 47, "Instrumental Rock", + 8, "Jazz", + 29, "Jazz+Funk", + 63, "Jungle", + 86, "Latin", + 71, "Lo-Fi", + 45, "Meditative", + 9, "Metal", + 77, "Musical", + 82, "National Folk", + 64, "Native American", + 10, "New Age", + 66, "New Wave", + 39, "Noise", + 255, "Not Set", + 11, "Oldies", + 103, "Opera", + 12, "Other", + 75, "Polka", + 13, "Pop", + 62, "Pop/Funk", + 53, "Pop-Folk", + 109, "Porn Groove", + 117, "Power Ballad", + 23, "Pranks", + 108, "Primus", + 92, "Progressive Rock", + 67, "Psychadelic", + 93, "Psychedelic Rock", + 43, "Punk", + 121, "Punk Rock", + 14, "R&B", + 15, "Rap", + 68, "Rave", + 16, "Reggae", + 76, "Retro", + 87, "Revival", + 118, "Rhythmic Soul", + 17, "Rock", + 78, "Rock & Roll", + 114, "Samba", + 110, "Satire", + 69, "Showtunes", + 21, "Ska", + 111, "Slow Jam", + 95, "Slow Rock", + 105, "Sonata", + 42, "Soul", + 37, "Sound Clip", + 24, "Soundtrack", + 56, "Southern Rock", + 44, "Space", + 101, "Speech", + 83, "Swing", + 94, "Symphonic Rock", + 106, "Symphony", + 113, "Tango", + 18, "Techno", + 51, "Techno-Industrial", + 60, "Top 40", + 70, "Trailer", + 31, "Trance", + 72, "Tribal", + 27, "Trip-Hop", + 28, "Vocal" +}; + +const int NUMFRAMES = sizeof(ID3Frames)/sizeof(ID3Frames[0]); +const int NUMGENRES = sizeof(ID3Genres)/sizeof(ID3Genres[0]); + + +LPSTR DupString(LPSTR lpsz) +{ + int cb = lstrlen(lpsz) + 1; + LPSTR lpszNew = LocalAlloc(LMEM_FIXED, cb); + if (lpszNew != NULL) + CopyMemory(lpszNew, lpsz, cb); + return lpszNew; +} + +LPSTR GetFrameDesc(ID3_FrameID id) +{ + return DupString(ID3Frames[id]); +} + +LPSTR GetGenre(LPSTR lpsz) +{ + int id = atoi(lpsz + 1); + int i; + + if ((*(lpsz + 1) > '0') && (*(lpsz + 1) < '9')) + { + for (i = 0; i < NUMGENRES; i++) + { + if (id == ID3Genres[i].id) + return DupString(ID3Genres[i].name); + } + } + return DupString(lpsz); +} + +void FillID3List(HWND hwndDlg, HWND hwndList, char *filename) +{ + ID3Tag *tag; + ID3Frame *frame; + ID3Field *field; + ID3_FrameID eFrameID; + char info[1024]; + int numFrames; + int i; + int iItem = 0; + + + if ((tag = ID3Tag_New()) != NULL) + { + ID3Tag_Link(tag, filename); + + numFrames = ID3Tag_NumFrames(tag); + + for (i = 0; i < numFrames; i++) + { + iItem++; + + frame = ID3Tag_GetFrameNum(tag, i); + eFrameID = ID3Frame_GetID(frame); + + switch (eFrameID) + { + case ID3FID_ALBUM: case ID3FID_BPM: + case ID3FID_COMPOSER: case ID3FID_CONTENTTYPE: + case ID3FID_COPYRIGHT: case ID3FID_DATE: + case ID3FID_PLAYLISTDELAY: case ID3FID_ENCODEDBY: + case ID3FID_LYRICIST: case ID3FID_FILETYPE: + case ID3FID_TIME: case ID3FID_CONTENTGROUP: + case ID3FID_TITLE: case ID3FID_SUBTITLE: + case ID3FID_INITIALKEY: case ID3FID_LANGUAGE: + case ID3FID_SONGLEN: case ID3FID_MEDIATYPE: + case ID3FID_ORIGALBUM: case ID3FID_ORIGFILENAME: + case ID3FID_ORIGLYRICIST: case ID3FID_ORIGARTIST: + case ID3FID_ORIGYEAR: case ID3FID_FILEOWNER: + case ID3FID_LEADARTIST: case ID3FID_BAND: + case ID3FID_CONDUCTOR: case ID3FID_MIXARTIST: + case ID3FID_PARTINSET: case ID3FID_PUBLISHER: + case ID3FID_TRACKNUM: case ID3FID_RECORDINGDATES: + case ID3FID_NETRADIOSTATION: case ID3FID_NETRADIOOWNER: + case ID3FID_SIZE: case ID3FID_ISRC: + case ID3FID_ENCODERSETTINGS: case ID3FID_YEAR: + { + LV_ITEM lvi; + ID3ITEM *pItem = LocalAlloc(LPTR, sizeof(ID3ITEM)); + + /* Initialize LV_ITEM members that are common to all items. */ + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; + lvi.state = 0; + lvi.stateMask = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; /* app. maintains text */ + lvi.iImage = 0; + lvi.iItem = iItem; + lvi.iSubItem = 0; + + pItem->frameId = eFrameID; + pItem->aCols[0] = GetFrameDesc(eFrameID); + + field = ID3Frame_GetField(frame, ID3FN_TEXT); + ID3Field_GetASCII(field, info, 1024, 1); + if (eFrameID == ID3FID_CONTENTTYPE) + pItem->aCols[1] = GetGenre(info); + else + pItem->aCols[1] = DupString(info); + + lvi.lParam = (LPARAM)pItem; /* item data */ + + /* Add the item. */ + ListView_InsertItem(hwndList, &lvi); + + break; + } + case ID3FID_USERTEXT: + case ID3FID_COMMENT: /* Can also contain an extra language field (but not used now) */ + case ID3FID_UNSYNCEDLYRICS: /* Can also contain an extra language field (but not used now) */ + { + LV_ITEM lvi; + ID3ITEM *pItem = LocalAlloc(LPTR, sizeof(ID3ITEM)); + + /* Initialize LV_ITEM members that are common to all items. */ + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; + lvi.state = 0; + lvi.stateMask = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; /* app. maintains text */ + lvi.iImage = 0; + lvi.iItem = iItem; + lvi.iSubItem = 0; + + pItem->frameId = eFrameID; + + field = ID3Frame_GetField(frame, ID3FN_DESCRIPTION); + ID3Field_GetASCII(field, info, 1024, 1); + pItem->aCols[0] = DupString(info); + + field = ID3Frame_GetField(frame, ID3FN_TEXT); + ID3Field_GetASCII(field, info, 1024, 1); + pItem->aCols[1] = DupString(info); + + lvi.lParam = (LPARAM)pItem; /* item data */ + + /* Add the item. */ + ListView_InsertItem(hwndList, &lvi); + + break; + } + case ID3FID_WWWAUDIOFILE: case ID3FID_WWWARTIST: + case ID3FID_WWWAUDIOSOURCE: case ID3FID_WWWCOMMERCIALINFO: + case ID3FID_WWWCOPYRIGHT: case ID3FID_WWWPUBLISHER: + case ID3FID_WWWPAYMENT: case ID3FID_WWWRADIOPAGE: + { + LV_ITEM lvi; + ID3ITEM *pItem = LocalAlloc(LPTR, sizeof(ID3ITEM)); + + /* Initialize LV_ITEM members that are common to all items. */ + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; + lvi.state = 0; + lvi.stateMask = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; /* app. maintains text */ + lvi.iImage = 0; + lvi.iItem = iItem; + lvi.iSubItem = 0; + + pItem->frameId = eFrameID; + + pItem->aCols[0] = GetFrameDesc(eFrameID); + + field = ID3Frame_GetField(frame, ID3FN_URL); + ID3Field_GetASCII(field, info, 1024, 1); + pItem->aCols[1] = DupString(info); + + lvi.lParam = (LPARAM)pItem; /* item data */ + + /* Add the item. */ + ListView_InsertItem(hwndList, &lvi); + + break; + } + case ID3FID_WWWUSER: + { + LV_ITEM lvi; + ID3ITEM *pItem = LocalAlloc(LPTR, sizeof(ID3ITEM)); + + /* Initialize LV_ITEM members that are common to all items. */ + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; + lvi.state = 0; + lvi.stateMask = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; /* app. maintains text */ + lvi.iImage = 0; + lvi.iItem = iItem; + lvi.iSubItem = 0; + + pItem->frameId = eFrameID; + + field = ID3Frame_GetField(frame, ID3FN_DESCRIPTION); + ID3Field_GetASCII(field, info, 1024, 1); + pItem->aCols[0] = DupString(info); + + field = ID3Frame_GetField(frame, ID3FN_URL); + ID3Field_GetASCII(field, info, 1024, 1); + pItem->aCols[1] = DupString(info); + + lvi.lParam = (LPARAM)pItem; /* item data */ + + /* Add the item. */ + ListView_InsertItem(hwndList, &lvi); + + break; + } + default: + break; + } + } + ID3Tag_Delete(tag); + } +} + +BOOL CALLBACK AddFrameProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int i, cursel; + + switch (message) { + case WM_INITDIALOG: + EnableWindow(GetDlgItem(hwndDlg, IDC_COL0), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); + + /* Note: FRAMEID is the index in the combo box + 1 */ + for (i = 1; i < NUMFRAMES; i++) + { + SendMessage(GetDlgItem(hwndDlg, IDC_FRAMETYPE), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)ID3Frames[i]); + } + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_FRAMETYPE: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + cursel = SendMessage(GetDlgItem(hwndDlg, IDC_FRAMETYPE), CB_GETCURSEL, 0, 0); + + switch (cursel + 1) + { + case ID3FID_ALBUM: case ID3FID_BPM: + case ID3FID_COMPOSER: case ID3FID_COPYRIGHT: + case ID3FID_DATE: case ID3FID_PLAYLISTDELAY: + case ID3FID_ENCODEDBY: case ID3FID_LYRICIST: + case ID3FID_FILETYPE: case ID3FID_TIME: + case ID3FID_CONTENTGROUP: case ID3FID_TITLE: + case ID3FID_SUBTITLE: case ID3FID_INITIALKEY: + case ID3FID_LANGUAGE: case ID3FID_SONGLEN: + case ID3FID_MEDIATYPE: case ID3FID_ORIGALBUM: + case ID3FID_ORIGFILENAME: case ID3FID_ORIGLYRICIST: + case ID3FID_ORIGARTIST: case ID3FID_ORIGYEAR: + case ID3FID_FILEOWNER: case ID3FID_LEADARTIST: + case ID3FID_BAND: case ID3FID_CONDUCTOR: + case ID3FID_MIXARTIST: case ID3FID_PARTINSET: + case ID3FID_PUBLISHER: case ID3FID_TRACKNUM: + case ID3FID_RECORDINGDATES: case ID3FID_NETRADIOSTATION: + case ID3FID_NETRADIOOWNER: case ID3FID_SIZE: + case ID3FID_ISRC: case ID3FID_ENCODERSETTINGS: + case ID3FID_YEAR: case ID3FID_WWWAUDIOFILE: + case ID3FID_WWWARTIST: case ID3FID_WWWAUDIOSOURCE: + case ID3FID_WWWCOMMERCIALINFO: case ID3FID_WWWCOPYRIGHT: + case ID3FID_WWWPUBLISHER: case ID3FID_WWWPAYMENT: + case ID3FID_WWWRADIOPAGE: case ID3FID_CONTENTTYPE: + { + SetDlgItemText(hwndDlg, IDC_COL0, ID3Frames[cursel+1]); + EnableWindow(GetDlgItem(hwndDlg, IDC_COL0), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE); + break; + } + case ID3FID_USERTEXT: case ID3FID_COMMENT: + case ID3FID_UNSYNCEDLYRICS: case ID3FID_WWWUSER: + { + SetDlgItemText(hwndDlg, IDC_COL0, ID3Frames[cursel+1]); + EnableWindow(GetDlgItem(hwndDlg, IDC_COL0), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE); + break; + } + default: + MessageBox(hwndDlg, "Sorry, this frame type cannot be added (yet).", "Sorry", MB_OK); + EnableWindow(GetDlgItem(hwndDlg, IDC_COL0), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); + break; + } + } + return TRUE; + case IDOK: + { + LV_ITEM lvi; + ID3ITEM *pItem = LocalAlloc(LPTR, sizeof(ID3ITEM)); + char *col0 = LocalAlloc(LPTR, 1024); + char *col1 = LocalAlloc(LPTR, 1024); + + /* Initialize LV_ITEM members that are common to all items. */ + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; + lvi.state = 0; + lvi.stateMask = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; /* app. maintains text */ + lvi.iImage = 0; + lvi.iItem = ListView_GetItemCount(m_hwndList) + 1; + lvi.iSubItem = 0; + + cursel = SendMessage(GetDlgItem(hwndDlg, IDC_FRAMETYPE), CB_GETCURSEL, 0, 0); + pItem->frameId = cursel + 1; + GetDlgItemText(hwndDlg, IDC_COL0, col0, 1024); + GetDlgItemText(hwndDlg, IDC_COL1, col1, 1024); + pItem->aCols[0] = col0; + pItem->aCols[1] = col1; + + lvi.lParam = (LPARAM)pItem; /* item data */ + + /* Add the item. */ + ListView_InsertItem(m_hwndList, &lvi); + ListView_Update(m_hwndList, lvi.iItem); + } + case IDCANCEL: + EndDialog(hwndDlg, wParam); + return TRUE; + } + } + return FALSE; +} + +BOOL List_AddFrame(HWND hwndApp, HWND hwndList) +{ + int result; + + m_hwndList = hwndList; + + result = DialogBox(hInstance_for_id3editor, MAKEINTRESOURCE(IDD_ADDFRAME), + hwndApp, AddFrameProc); + + if (LOWORD(result) == IDOK) + return TRUE; + return FALSE; +} + + +void InsertTextFrame(HWND dlg, HWND list, int control, int item, int frame_id) +{ + LV_ITEM lvi; + ID3ITEM *pItem = LocalAlloc(LPTR, sizeof(ID3ITEM)); + + /* Initialize LV_ITEM members that are common to all items. */ + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; + lvi.state = 0; + lvi.stateMask = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; /* app. maintains text */ + lvi.iImage = 0; + lvi.iItem = item; + lvi.iSubItem = 0; + + pItem->frameId = frame_id; + pItem->aCols[0] = GetFrameDesc(frame_id); + + pItem->aCols[1] = LocalAlloc(LPTR, 1024); + GetDlgItemText(dlg, control, pItem->aCols[1], 1024); + + lvi.lParam = (LPARAM)pItem; /* item data */ + + /* Add the item. */ + ListView_InsertItem(list, &lvi); +} + +void AddFrameFromRAWData(HWND hwndList, int frameId, LPSTR data1, LPSTR data2) +{ + LV_ITEM lvi; + ID3ITEM *pItem = LocalAlloc(LPTR, sizeof(ID3ITEM)); + int nextItem; + + nextItem = ListView_GetItemCount(hwndList); + + /* Initialize LV_ITEM members that are common to all items. */ + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; + lvi.state = 0; + lvi.stateMask = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; /* app. maintains text */ + lvi.iImage = 0; + lvi.iItem = nextItem; + lvi.iSubItem = 0; + + pItem->frameId = frameId; + + pItem->aCols[0] = LocalAlloc(LPTR, 1024); + pItem->aCols[1] = LocalAlloc(LPTR, 1024); + + lstrcpy(pItem->aCols[0], data1); + lstrcpy(pItem->aCols[1], data2); + + lvi.lParam = (LPARAM)pItem; /* item data */ + + /* Add the item. */ + ListView_InsertItem(hwndList, &lvi); +} + +HWND m_hwndDlg; +int changed; + +BOOL CALLBACK AddStandardProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int added = 0; + + switch (message) { + case WM_INITDIALOG: + changed = 0; + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + { + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_TRACK)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_TRACK, ListView_GetItemCount(m_hwndList)+1, ID3FID_TRACKNUM); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_TITLE)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_TITLE, ListView_GetItemCount(m_hwndList)+1, ID3FID_TITLE); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_ARTIST)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_ARTIST, ListView_GetItemCount(m_hwndList)+1, ID3FID_LEADARTIST); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_ALBUM)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_ALBUM, ListView_GetItemCount(m_hwndList)+1, ID3FID_ALBUM); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_YEAR)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_YEAR, ListView_GetItemCount(m_hwndList)+1, ID3FID_YEAR); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_GENRE)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_GENRE, ListView_GetItemCount(m_hwndList)+1, ID3FID_CONTENTTYPE); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_COMMENT)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_COMMENT, ListView_GetItemCount(m_hwndList)+1, ID3FID_COMMENT); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_COMPOSER)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_COMPOSER, ListView_GetItemCount(m_hwndList)+1, ID3FID_COMPOSER); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_ORIGARTIST)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_ORIGARTIST, ListView_GetItemCount(m_hwndList)+1, ID3FID_ORIGARTIST); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_COPYRIGHT)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_COPYRIGHT, ListView_GetItemCount(m_hwndList)+1, ID3FID_COPYRIGHT); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_URL)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_URL, ListView_GetItemCount(m_hwndList)+1, ID3FID_WWWARTIST); + added++; + } + + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_ENCBY)) > 0) { + InsertTextFrame(hwndDlg, m_hwndList, IDC_ENCBY, ListView_GetItemCount(m_hwndList)+1, ID3FID_ENCODEDBY); + added++; + } + + if (added > 0) + changed = 1; + } + case IDCANCEL: + EndDialog(hwndDlg, changed); + return TRUE; + } + } + return FALSE; +} + + +BOOL List_AddStandardFrames(HWND hwndApp, HWND hwndList) +{ + int result; + + m_hwndList = hwndList; + m_hwndDlg = hwndApp; + + result = DialogBox(hInstance_for_id3editor, MAKEINTRESOURCE(IDD_ADDSTANDARD), + hwndApp, AddStandardProc); + + return result?TRUE:FALSE; +} + + +/* List_OnGetDispInfo - processes the LVN_GETDISPINFO */ +/* notification message. */ +/* pnmv - value of lParam (points to an LV_DISPINFO structure) */ +void List_OnGetDispInfo(LV_DISPINFO *pnmv) +{ + /* Provide the item or subitem's text, if requested. */ + if (pnmv->item.mask & LVIF_TEXT) { + ID3ITEM *pItem = (ID3ITEM *) (pnmv->item.lParam); + lstrcpy(pnmv->item.pszText, + pItem->aCols[pnmv->item.iSubItem]); + } +} + +ID3ITEM *pItem; +int editItemIndex; + +BOOL CALLBACK EditTextFrameProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + LV_ITEM lvi; + + switch (message) { + case WM_INITDIALOG: + SetDlgItemText(hwndDlg, IDC_TEXTFRAMENAME, pItem->aCols[0]); + SetDlgItemText(hwndDlg, IDC_EDITTEXTFRAME, pItem->aCols[1]); + + switch (pItem->frameId) + { + case ID3FID_ALBUM: case ID3FID_BPM: + case ID3FID_COMPOSER: case ID3FID_COPYRIGHT: + case ID3FID_DATE: case ID3FID_PLAYLISTDELAY: + case ID3FID_ENCODEDBY: case ID3FID_LYRICIST: + case ID3FID_FILETYPE: case ID3FID_TIME: + case ID3FID_CONTENTGROUP: case ID3FID_TITLE: + case ID3FID_SUBTITLE: case ID3FID_INITIALKEY: + case ID3FID_LANGUAGE: case ID3FID_SONGLEN: + case ID3FID_MEDIATYPE: case ID3FID_ORIGALBUM: + case ID3FID_ORIGFILENAME: case ID3FID_ORIGLYRICIST: + case ID3FID_ORIGARTIST: case ID3FID_ORIGYEAR: + case ID3FID_FILEOWNER: case ID3FID_LEADARTIST: + case ID3FID_BAND: case ID3FID_CONDUCTOR: + case ID3FID_MIXARTIST: case ID3FID_PARTINSET: + case ID3FID_PUBLISHER: case ID3FID_TRACKNUM: + case ID3FID_RECORDINGDATES: case ID3FID_NETRADIOSTATION: + case ID3FID_NETRADIOOWNER: case ID3FID_SIZE: + case ID3FID_ISRC: case ID3FID_ENCODERSETTINGS: + case ID3FID_YEAR: case ID3FID_WWWAUDIOFILE: + case ID3FID_WWWARTIST: case ID3FID_WWWAUDIOSOURCE: + case ID3FID_WWWCOMMERCIALINFO: case ID3FID_WWWCOPYRIGHT: + case ID3FID_WWWPUBLISHER: case ID3FID_WWWPAYMENT: + case ID3FID_WWWRADIOPAGE: case ID3FID_CONTENTTYPE: + { + EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTFRAMENAME), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTEXTFRAME), TRUE); + break; + } + case ID3FID_USERTEXT: case ID3FID_COMMENT: + case ID3FID_UNSYNCEDLYRICS: case ID3FID_WWWUSER: + { + EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTFRAMENAME), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTEXTFRAME), TRUE); + break; + } + default: + EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTFRAMENAME), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTEXTFRAME), FALSE); + break; + } + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + { + GetDlgItemText(hwndDlg, IDC_TEXTFRAMENAME, pItem->aCols[0], 1024); + GetDlgItemText(hwndDlg, IDC_EDITTEXTFRAME, pItem->aCols[1], 1024); + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; + lvi.state = 0; + lvi.stateMask = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; /* app. maintains text */ + lvi.iImage = 0; + lvi.iItem = editItemIndex; + lvi.iSubItem = 0; + lvi.lParam = (LPARAM)pItem; /* item data */ + + /* Add the item. */ + ListView_SetItem(m_hwndList, &lvi); + ListView_Update(m_hwndList, editItemIndex); + } /* Fall through */ + case IDCANCEL: + EndDialog(hwndDlg, wParam); + return TRUE; + } + } + return FALSE; +} + + +/* Double clicking means editing a frame */ +BOOL List_EditData(HWND hwndApp, HWND hwndList) +{ + LV_ITEM lvi; + BOOL result; + + /* First get the selected item */ + int index = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED); + + m_hwndList = hwndList; + + if (index != -1) + { + lvi.mask = LVIF_PARAM; + lvi.iItem = index; + lvi.iSubItem = 0; + + if (ListView_GetItem(hwndList, &lvi) == TRUE) + { + pItem = (ID3ITEM*)lvi.lParam; + editItemIndex = lvi.iItem; + + result = DialogBox(hInstance_for_id3editor, MAKEINTRESOURCE(IDD_EDITTEXTFRAME), + hwndApp, EditTextFrameProc); + if (LOWORD(result) == IDOK) + return TRUE; + } + } + return FALSE; +} + + +/* Delete the selected frame */ +BOOL List_DeleteSelected(HWND hwndApp, HWND hwndList) +{ + int items; + + /* First get the selected item */ + int index = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED); + + if (index != -1) + ListView_DeleteItem(hwndList, index); + + items = ListView_GetItemCount(hwndList); + if (index != -1) return TRUE; + else return FALSE; +} + + +/* Save the ID3 to the file */ +void List_SaveID3(HWND hwndApp, HWND hwndList, char *filename) +{ + LV_ITEM lvi; + ID3ITEM *pItem1; + int i, items; + ID3Tag *tag; + ID3Frame *frame; + ID3Field *field; + + /* Strip the tag first, before completely rewriting it */ + if ((tag = ID3Tag_New()) != NULL) + { + ID3Tag_Link(tag, filename); + ID3Tag_Strip(tag, ID3TT_ALL); + ID3Tag_Clear(tag); + + if (SendMessage(GetDlgItem(hwndApp, IDC_ID3V2TAG), BM_GETCHECK, 0, 0) == BST_UNCHECKED) + { + /* No frames saved */ + ID3Tag_Delete(tag); + EnableWindow(GetDlgItem(hwndApp, IDC_ID3V2TAG), FALSE); + ListView_DeleteAllItems(hwndList); + return; + } + + /* First get the number of items */ + items = ListView_GetItemCount(hwndList); + + if (items > 0) + { + for (i = 0; i < items; i++) + { + lvi.mask = LVIF_PARAM; + lvi.iItem = i; + lvi.iSubItem = 0; + + if (ListView_GetItem(hwndList, &lvi) == TRUE) + { + pItem1 = (ID3ITEM*)lvi.lParam; + + frame = ID3Frame_NewID(pItem1->frameId); + + switch (pItem1->frameId) + { + case ID3FID_ALBUM: case ID3FID_BPM: + case ID3FID_COMPOSER: case ID3FID_CONTENTTYPE: + case ID3FID_COPYRIGHT: case ID3FID_DATE: + case ID3FID_PLAYLISTDELAY: case ID3FID_ENCODEDBY: + case ID3FID_LYRICIST: case ID3FID_FILETYPE: + case ID3FID_TIME: case ID3FID_CONTENTGROUP: + case ID3FID_TITLE: case ID3FID_SUBTITLE: + case ID3FID_INITIALKEY: case ID3FID_LANGUAGE: + case ID3FID_SONGLEN: case ID3FID_MEDIATYPE: + case ID3FID_ORIGALBUM: case ID3FID_ORIGFILENAME: + case ID3FID_ORIGLYRICIST: case ID3FID_ORIGARTIST: + case ID3FID_ORIGYEAR: case ID3FID_FILEOWNER: + case ID3FID_LEADARTIST: case ID3FID_BAND: + case ID3FID_CONDUCTOR: case ID3FID_MIXARTIST: + case ID3FID_PARTINSET: case ID3FID_PUBLISHER: + case ID3FID_TRACKNUM: case ID3FID_RECORDINGDATES: + case ID3FID_NETRADIOSTATION: case ID3FID_NETRADIOOWNER: + case ID3FID_SIZE: case ID3FID_ISRC: + case ID3FID_ENCODERSETTINGS: case ID3FID_YEAR: + { + field = ID3Frame_GetField(frame, ID3FN_TEXT); + ID3Field_SetASCII(field, pItem1->aCols[1]); + ID3Tag_AddFrame(tag, frame); + break; + } + case ID3FID_USERTEXT: + case ID3FID_COMMENT: /* Can also contain an extra language field (but not used now) */ + case ID3FID_UNSYNCEDLYRICS: /* Can also contain an extra language field (but not used now) */ + { + field = ID3Frame_GetField(frame, ID3FN_DESCRIPTION); + ID3Field_SetASCII(field, pItem1->aCols[0]); + field = ID3Frame_GetField(frame, ID3FN_TEXT); + ID3Field_SetASCII(field, pItem1->aCols[1]); + ID3Tag_AddFrame(tag, frame); + break; + } + case ID3FID_WWWAUDIOFILE: case ID3FID_WWWARTIST: + case ID3FID_WWWAUDIOSOURCE: case ID3FID_WWWCOMMERCIALINFO: + case ID3FID_WWWCOPYRIGHT: case ID3FID_WWWPUBLISHER: + case ID3FID_WWWPAYMENT: case ID3FID_WWWRADIOPAGE: + { + field = ID3Frame_GetField(frame, ID3FN_URL); + ID3Field_SetASCII(field, pItem1->aCols[1]); + ID3Tag_AddFrame(tag, frame); + break; + } + case ID3FID_WWWUSER: + { + field = ID3Frame_GetField(frame, ID3FN_DESCRIPTION); + ID3Field_SetASCII(field, pItem1->aCols[0]); + field = ID3Frame_GetField(frame, ID3FN_URL); + ID3Field_SetASCII(field, pItem1->aCols[1]); + ID3Tag_AddFrame(tag, frame); + break; + } + default: + break; + } + } + } + ID3Tag_UpdateByTagType(tag, ID3TT_ID3V2); + } + + ID3Tag_Delete(tag); + } +} + +/* Get the title from the file */ +void GetID3FileTitle(char *filename, char *title, char *format) +{ + ID3Tag *tag; + ID3Frame *frame; + ID3Field *field; + char buffer[255]; + int some_info = 0; + char *in = format; + char *out = title; + char *bound = out + (MAX_PATH - 10 - 1); + + + if ((tag = ID3Tag_New()) != NULL) + { + ID3Tag_Link(tag, filename); + + while (*in && out < bound) + { + switch (*in) { + case '%': + ++in; + break; + + default: + *out++ = *in++; + continue; + } + + /* handle % escape sequence */ + switch (*in++) { + case '0': + if ((frame = ID3Tag_FindFrameWithID(tag, ID3FID_TRACKNUM)) != NULL) { + int size; + field = ID3Frame_GetField(frame, ID3FN_TEXT); + size = ID3Field_GetASCII(field, buffer, 255, 1); + lstrcpy(out, buffer); out += size; + some_info = 1; + } + break; + case '1': + if ((frame = ID3Tag_FindFrameWithID(tag, ID3FID_LEADARTIST)) != NULL) { + int size; + field = ID3Frame_GetField(frame, ID3FN_TEXT); + size = ID3Field_GetASCII(field, buffer, 255, 1); + lstrcpy(out, buffer); out += size; + some_info = 1; + } + break; + case '2': + if ((frame = ID3Tag_FindFrameWithID(tag, ID3FID_TITLE)) != NULL) { + int size; + field = ID3Frame_GetField(frame, ID3FN_TEXT); + size = ID3Field_GetASCII(field, buffer, 255, 1); + lstrcpy(out, buffer); out += size; + some_info = 1; + } + break; + case '3': + if ((frame = ID3Tag_FindFrameWithID(tag, ID3FID_ALBUM)) != NULL) { + int size; + field = ID3Frame_GetField(frame, ID3FN_TEXT); + size = ID3Field_GetASCII(field, buffer, 255, 1); + lstrcpy(out, buffer); out += size; + some_info = 1; + } + break; + case '4': + if ((frame = ID3Tag_FindFrameWithID(tag, ID3FID_YEAR)) != NULL) { + int size; + field = ID3Frame_GetField(frame, ID3FN_TEXT); + size = ID3Field_GetASCII(field, buffer, 255, 1); + lstrcpy(out, buffer); out += size; + some_info = 1; + } + break; + case '5': + if ((frame = ID3Tag_FindFrameWithID(tag, ID3FID_COMMENT)) != NULL) { + int size; + field = ID3Frame_GetField(frame, ID3FN_TEXT); + size = ID3Field_GetASCII(field, buffer, 255, 1); + lstrcpy(out, buffer); out += size; + some_info = 1; + } + break; + case '6': + if ((frame = ID3Tag_FindFrameWithID(tag, ID3FID_CONTENTTYPE)) != NULL) { + int size; char *tmp; + field = ID3Frame_GetField(frame, ID3FN_TEXT); + size = ID3Field_GetASCII(field, buffer, 255, 1); + tmp = GetGenre(buffer); + lstrcpy(out, tmp); out += size; + some_info = 1; + } + break; + case '7': + { + char *p=filename+lstrlen(filename); + int len = 0; + while (*p != '\\' && p >= filename) { p--; len++; } + lstrcpy(out, ++p); out += len; + some_info = 1; + break; + } + } + } + + *out = '\0'; + ID3Tag_Delete(tag); + } + + if (!some_info) + { + char *p=filename+lstrlen(filename); + while (*p != '\\' && p >= filename) p--; + lstrcpy(title,++p); + } +} + diff --git a/Libraries/FAAD2/Files/common/faad/id3v2tag.h b/Libraries/FAAD2/Files/common/faad/id3v2tag.h new file mode 100644 index 000000000..6f687f2d9 --- /dev/null +++ b/Libraries/FAAD2/Files/common/faad/id3v2tag.h @@ -0,0 +1,54 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#ifndef __ID3V2TAG_H__ +#define __ID3V2TAG_H__ + +void GetID3FileTitle(char *filename, char *title, char *format); +void FillID3List(HWND hwndDlg, HWND hwndList, char *filename); +void List_OnGetDispInfo(LV_DISPINFO *pnmv); +BOOL List_EditData(HWND hwndApp, HWND hwndList); +void List_SaveID3(HWND hwndApp, HWND hwndList, char *filename); +BOOL List_DeleteSelected(HWND hwndApp, HWND hwndList); +BOOL List_AddFrame(HWND hwndApp, HWND hwndList); +BOOL List_AddStandardFrames(HWND hwndApp, HWND hwndList); +void AddFrameFromRAWData(HWND hwndList, int frameId, LPSTR data1, LPSTR data2); + +HINSTANCE hInstance_for_id3editor; + +typedef struct ID3GENRES_TAG +{ + BYTE id; + char name[30]; +} ID3GENRES; + +typedef struct id3item_tag { + int frameId; + LPSTR aCols[2]; +} ID3ITEM; + +#endif \ No newline at end of file diff --git a/Libraries/FAAD2/Files/common/mp4ff/drms.c b/Libraries/FAAD2/Files/common/mp4ff/drms.c new file mode 100644 index 000000000..6b7993c72 --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/drms.c @@ -0,0 +1,1321 @@ +/***************************************************************************** + * drms.c: DRMS + ***************************************************************************** + * Copyright (C) 2004 VideoLAN + * $Id$ + * + * Authors: Jon Lech Johansen + * Sam Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include /* malloc(), free() */ + +#ifndef _WIN32 +#include "config.h" +#endif +#include "mp4ffint.h" + +#ifdef ITUNES_DRM + +#ifdef _WIN32 +# include +# include +# include +# define PATH_MAX MAX_PATH +#else +# include +#endif + +#ifdef HAVE_ERRNO_H +# include +#endif + +#ifdef _WIN32 +# include +# include +# include +#endif + +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +/* In Solaris (and perhaps others) PATH_MAX is in limits.h. */ +#ifdef HAVE_LIMITS_H +# include +#endif + +#ifdef HAVE_IOKIT_IOKITLIB_H +# include +# include +# include +#endif + +#ifdef HAVE_SYSFS_LIBSYSFS_H +# include +#endif + +#include "drms.h" +#include "drmstables.h" + +/***************************************************************************** + * aes_s: AES keys structure + ***************************************************************************** + * This structure stores a set of keys usable for encryption and decryption + * with the AES/Rijndael algorithm. + *****************************************************************************/ +struct aes_s +{ + uint32_t pp_enc_keys[ AES_KEY_COUNT + 1 ][ 4 ]; + uint32_t pp_dec_keys[ AES_KEY_COUNT + 1 ][ 4 ]; +}; + +/***************************************************************************** + * md5_s: MD5 message structure + ***************************************************************************** + * This structure stores the static information needed to compute an MD5 + * hash. It has an extra data buffer to allow non-aligned writes. + *****************************************************************************/ +struct md5_s +{ + uint64_t i_bits; /* Total written bits */ + uint32_t p_digest[4]; /* The MD5 digest */ + uint32_t p_data[16]; /* Buffer to cache non-aligned writes */ +}; + +/***************************************************************************** + * shuffle_s: shuffle structure + ***************************************************************************** + * This structure stores the static information needed to shuffle data using + * a custom algorithm. + *****************************************************************************/ +struct shuffle_s +{ + uint32_t p_commands[ 20 ]; + uint32_t p_bordel[ 16 ]; +}; + +/***************************************************************************** + * drms_s: DRMS structure + ***************************************************************************** + * This structure stores the static information needed to decrypt DRMS data. + *****************************************************************************/ +struct drms_s +{ + uint32_t i_user; + uint32_t i_key; + uint8_t p_iviv[ 16 ]; + uint8_t *p_name; + + uint32_t p_key[ 4 ]; + struct aes_s aes; + + char psz_homedir[ PATH_MAX ]; +}; + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void InitAES ( struct aes_s *, uint32_t * ); +static void DecryptAES ( struct aes_s *, uint32_t *, const uint32_t * ); + +static void InitMD5 ( struct md5_s * ); +static void AddMD5 ( struct md5_s *, const uint8_t *, uint32_t ); +static void EndMD5 ( struct md5_s * ); +static void Digest ( struct md5_s *, uint32_t * ); + +static void InitShuffle ( struct shuffle_s *, uint32_t * ); +static void DoShuffle ( struct shuffle_s *, uint32_t *, uint32_t ); + +static int GetSystemKey ( uint32_t *, uint32_t ); +static int WriteUserKey ( void *, uint32_t * ); +static int ReadUserKey ( void *, uint32_t * ); +static int GetUserKey ( void *, uint32_t * ); + +static int GetSCIData ( char *, uint32_t **, uint32_t * ); +static int HashSystemInfo ( uint32_t * ); +static int GetiPodID ( int64_t * ); + +#ifdef WORDS_BIGENDIAN +/***************************************************************************** + * Reverse: reverse byte order + *****************************************************************************/ +static __inline void Reverse( uint32_t *p_buffer, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) + { + p_buffer[ i ] = GetDWLE(&p_buffer[ i ]); + } +} +# define REVERSE( p, n ) Reverse( p, n ) +#else +# define REVERSE( p, n ) +#endif + +/***************************************************************************** + * BlockXOR: XOR two 128 bit blocks + *****************************************************************************/ +static __inline void BlockXOR( uint32_t *p_dest, uint32_t *p_s1, uint32_t *p_s2 ) +{ + int i; + + for( i = 0; i < 4; i++ ) + { + p_dest[ i ] = p_s1[ i ] ^ p_s2[ i ]; + } +} + +/***************************************************************************** + * drms_alloc: allocate a DRMS structure + *****************************************************************************/ +void *drms_alloc( char *psz_homedir ) +{ + struct drms_s *p_drms; + + p_drms = malloc( sizeof(struct drms_s) ); + + if( p_drms == NULL ) + { + return NULL; + } + + memset( p_drms, 0, sizeof(struct drms_s) ); + + strncpy( p_drms->psz_homedir, psz_homedir, PATH_MAX ); + p_drms->psz_homedir[ PATH_MAX - 1 ] = '\0'; + + return (void *)p_drms; +} + +/***************************************************************************** + * drms_free: free a previously allocated DRMS structure + *****************************************************************************/ +void drms_free( void *_p_drms ) +{ + struct drms_s *p_drms = (struct drms_s *)_p_drms; + + if( p_drms->p_name != NULL ) + { + free( (void *)p_drms->p_name ); + } + + free( p_drms ); +} + +/***************************************************************************** + * drms_decrypt: unscramble a chunk of data + *****************************************************************************/ +void drms_decrypt( void *_p_drms, uint32_t *p_buffer, uint32_t i_bytes ) +{ + struct drms_s *p_drms = (struct drms_s *)_p_drms; + uint32_t p_key[ 4 ]; + unsigned int i_blocks; + + /* AES is a block cypher, round down the byte count */ + i_blocks = i_bytes / 16; + i_bytes = i_blocks * 16; + + /* Initialise the key */ + memcpy( p_key, p_drms->p_key, 16 ); + + /* Unscramble */ + while( i_blocks-- ) + { + uint32_t p_tmp[ 4 ]; + + REVERSE( p_buffer, 4 ); + DecryptAES( &p_drms->aes, p_tmp, p_buffer ); + BlockXOR( p_tmp, p_key, p_tmp ); + + /* Use the previous scrambled data as the key for next block */ + memcpy( p_key, p_buffer, 16 ); + + /* Copy unscrambled data back to the buffer */ + memcpy( p_buffer, p_tmp, 16 ); + REVERSE( p_buffer, 4 ); + + p_buffer += 4; + } +} + +/***************************************************************************** + * drms_init: initialise a DRMS structure + *****************************************************************************/ +int drms_init( void *_p_drms, uint32_t i_type, + uint8_t *p_info, uint32_t i_len ) +{ + struct drms_s *p_drms = (struct drms_s *)_p_drms; + int i_ret = 0; + + switch( i_type ) + { + case FOURCC_user: + if( i_len < sizeof(p_drms->i_user) ) + { + i_ret = -1; + break; + } + + p_drms->i_user = U32_AT( p_info ); + break; + + case FOURCC_key: + if( i_len < sizeof(p_drms->i_key) ) + { + i_ret = -1; + break; + } + + p_drms->i_key = U32_AT( p_info ); + break; + + case FOURCC_iviv: + if( i_len < sizeof(p_drms->p_key) ) + { + i_ret = -1; + break; + } + + memcpy( p_drms->p_iviv, p_info, 16 ); + break; + + case FOURCC_name: + p_drms->p_name = strdup( p_info ); + + if( p_drms->p_name == NULL ) + { + i_ret = -1; + } + break; + + case FOURCC_priv: + { + uint32_t p_priv[ 64 ]; + struct md5_s md5; + + if( i_len < 64 ) + { + i_ret = -1; + break; + } + + InitMD5( &md5 ); + AddMD5( &md5, p_drms->p_name, strlen( p_drms->p_name ) ); + AddMD5( &md5, p_drms->p_iviv, 16 ); + EndMD5( &md5 ); + + if( GetUserKey( p_drms, p_drms->p_key ) ) + { + i_ret = -1; + break; + } + + InitAES( &p_drms->aes, p_drms->p_key ); + + memcpy( p_priv, p_info, 64 ); + memcpy( p_drms->p_key, md5.p_digest, 16 ); + drms_decrypt( p_drms, p_priv, 64 ); + REVERSE( p_priv, 64 ); + + if( p_priv[ 0 ] != 0x6e757469 ) /* itun */ + { + i_ret = -1; + break; + } + + InitAES( &p_drms->aes, p_priv + 6 ); + memcpy( p_drms->p_key, p_priv + 12, 16 ); + + free( (void *)p_drms->p_name ); + p_drms->p_name = NULL; + } + break; + } + + return i_ret; +} + +/* The following functions are local */ + +/***************************************************************************** + * InitAES: initialise AES/Rijndael encryption/decryption tables + ***************************************************************************** + * The Advanced Encryption Standard (AES) is described in RFC 3268 + *****************************************************************************/ +static void InitAES( struct aes_s *p_aes, uint32_t *p_key ) +{ + unsigned int i, t; + uint32_t i_key, i_seed; + + memset( p_aes->pp_enc_keys[1], 0, 16 ); + memcpy( p_aes->pp_enc_keys[0], p_key, 16 ); + + /* Generate the key tables */ + i_seed = p_aes->pp_enc_keys[ 0 ][ 3 ]; + + for( i_key = 0; i_key < AES_KEY_COUNT; i_key++ ) + { + uint32_t j; + + i_seed = AES_ROR( i_seed, 8 ); + + j = p_aes_table[ i_key ]; + + j ^= p_aes_encrypt[ (i_seed >> 24) & 0xff ] + ^ AES_ROR( p_aes_encrypt[ (i_seed >> 16) & 0xff ], 8 ) + ^ AES_ROR( p_aes_encrypt[ (i_seed >> 8) & 0xff ], 16 ) + ^ AES_ROR( p_aes_encrypt[ i_seed & 0xff ], 24 ); + + j ^= p_aes->pp_enc_keys[ i_key ][ 0 ]; + p_aes->pp_enc_keys[ i_key + 1 ][ 0 ] = j; + j ^= p_aes->pp_enc_keys[ i_key ][ 1 ]; + p_aes->pp_enc_keys[ i_key + 1 ][ 1 ] = j; + j ^= p_aes->pp_enc_keys[ i_key ][ 2 ]; + p_aes->pp_enc_keys[ i_key + 1 ][ 2 ] = j; + j ^= p_aes->pp_enc_keys[ i_key ][ 3 ]; + p_aes->pp_enc_keys[ i_key + 1 ][ 3 ] = j; + + i_seed = j; + } + + memcpy( p_aes->pp_dec_keys[ 0 ], + p_aes->pp_enc_keys[ 0 ], 16 ); + + for( i = 1; i < AES_KEY_COUNT; i++ ) + { + for( t = 0; t < 4; t++ ) + { + uint32_t j, k, l, m, n; + + j = p_aes->pp_enc_keys[ i ][ t ]; + + k = (((j >> 7) & 0x01010101) * 27) ^ ((j & 0xff7f7f7f) << 1); + l = (((k >> 7) & 0x01010101) * 27) ^ ((k & 0xff7f7f7f) << 1); + m = (((l >> 7) & 0x01010101) * 27) ^ ((l & 0xff7f7f7f) << 1); + + j ^= m; + + n = AES_ROR( l ^ j, 16 ) ^ AES_ROR( k ^ j, 8 ) ^ AES_ROR( j, 24 ); + + p_aes->pp_dec_keys[ i ][ t ] = k ^ l ^ m ^ n; + } + } +} + +/***************************************************************************** + * DecryptAES: decrypt an AES/Rijndael 128 bit block + *****************************************************************************/ +static void DecryptAES( struct aes_s *p_aes, + uint32_t *p_dest, const uint32_t *p_src ) +{ + uint32_t p_wtxt[ 4 ]; /* Working cyphertext */ + uint32_t p_tmp[ 4 ]; + unsigned int i_round, t; + + for( t = 0; t < 4; t++ ) + { + /* FIXME: are there any endianness issues here? */ + p_wtxt[ t ] = p_src[ t ] ^ p_aes->pp_enc_keys[ AES_KEY_COUNT ][ t ]; + } + + /* Rounds 0 - 8 */ + for( i_round = 0; i_round < (AES_KEY_COUNT - 1); i_round++ ) + { + for( t = 0; t < 4; t++ ) + { + p_tmp[ t ] = AES_XOR_ROR( p_aes_itable, p_wtxt ); + } + + for( t = 0; t < 4; t++ ) + { + p_wtxt[ t ] = p_tmp[ t ] + ^ p_aes->pp_dec_keys[ (AES_KEY_COUNT - 1) - i_round ][ t ]; + } + } + + /* Final round (9) */ + for( t = 0; t < 4; t++ ) + { + p_dest[ t ] = AES_XOR_ROR( p_aes_decrypt, p_wtxt ); + p_dest[ t ] ^= p_aes->pp_dec_keys[ 0 ][ t ]; + } +} + +/***************************************************************************** + * InitMD5: initialise an MD5 message + ***************************************************************************** + * The MD5 message-digest algorithm is described in RFC 1321 + *****************************************************************************/ +static void InitMD5( struct md5_s *p_md5 ) +{ + p_md5->p_digest[ 0 ] = 0x67452301; + p_md5->p_digest[ 1 ] = 0xefcdab89; + p_md5->p_digest[ 2 ] = 0x98badcfe; + p_md5->p_digest[ 3 ] = 0x10325476; + + memset( p_md5->p_data, 0, 64 ); + p_md5->i_bits = 0; +} + +/***************************************************************************** + * AddMD5: add i_len bytes to an MD5 message + *****************************************************************************/ +static void AddMD5( struct md5_s *p_md5, const uint8_t *p_src, uint32_t i_len ) +{ + unsigned int i_current; /* Current bytes in the spare buffer */ + unsigned int i_offset = 0; + + i_current = (p_md5->i_bits / 8) & 63; + + p_md5->i_bits += 8 * i_len; + + /* If we can complete our spare buffer to 64 bytes, do it and add the + * resulting buffer to the MD5 message */ + if( i_len >= (64 - i_current) ) + { + memcpy( ((uint8_t *)p_md5->p_data) + i_current, p_src, + (64 - i_current) ); + Digest( p_md5, p_md5->p_data ); + + i_offset += (64 - i_current); + i_len -= (64 - i_current); + i_current = 0; + } + + /* Add as many entire 64 bytes blocks as we can to the MD5 message */ + while( i_len >= 64 ) + { + uint32_t p_tmp[ 16 ]; + memcpy( p_tmp, p_src + i_offset, 64 ); + Digest( p_md5, p_tmp ); + i_offset += 64; + i_len -= 64; + } + + /* Copy our remaining data to the message's spare buffer */ + memcpy( ((uint8_t *)p_md5->p_data) + i_current, p_src + i_offset, i_len ); +} + +/***************************************************************************** + * EndMD5: finish an MD5 message + ***************************************************************************** + * This function adds adequate padding to the end of the message, and appends + * the bit count so that we end at a block boundary. + *****************************************************************************/ +static void EndMD5( struct md5_s *p_md5 ) +{ + unsigned int i_current; + + i_current = (p_md5->i_bits / 8) & 63; + + /* Append 0x80 to our buffer. No boundary check because the temporary + * buffer cannot be full, otherwise AddMD5 would have emptied it. */ + ((uint8_t *)p_md5->p_data)[ i_current++ ] = 0x80; + + /* If less than 8 bytes are available at the end of the block, complete + * this 64 bytes block with zeros and add it to the message. We'll add + * our length at the end of the next block. */ + if( i_current > 56 ) + { + memset( ((uint8_t *)p_md5->p_data) + i_current, 0, (64 - i_current) ); + Digest( p_md5, p_md5->p_data ); + i_current = 0; + } + + /* Fill the unused space in our last block with zeroes and put the + * message length at the end. */ + memset( ((uint8_t *)p_md5->p_data) + i_current, 0, (56 - i_current) ); + p_md5->p_data[ 14 ] = p_md5->i_bits & 0xffffffff; + p_md5->p_data[ 15 ] = (p_md5->i_bits >> 32); + REVERSE( &p_md5->p_data[ 14 ], 2 ); + + Digest( p_md5, p_md5->p_data ); +} + +#define F1( x, y, z ) ((z) ^ ((x) & ((y) ^ (z)))) +#define F2( x, y, z ) F1((z), (x), (y)) +#define F3( x, y, z ) ((x) ^ (y) ^ (z)) +#define F4( x, y, z ) ((y) ^ ((x) | ~(z))) + +#define MD5_DO( f, w, x, y, z, data, s ) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/***************************************************************************** + * Digest: update the MD5 digest with 64 bytes of data + *****************************************************************************/ +static void Digest( struct md5_s *p_md5, uint32_t *p_input ) +{ + uint32_t a, b, c, d; + + REVERSE( p_input, 16 ); + + a = p_md5->p_digest[ 0 ]; + b = p_md5->p_digest[ 1 ]; + c = p_md5->p_digest[ 2 ]; + d = p_md5->p_digest[ 3 ]; + + MD5_DO( F1, a, b, c, d, p_input[ 0 ] + 0xd76aa478, 7 ); + MD5_DO( F1, d, a, b, c, p_input[ 1 ] + 0xe8c7b756, 12 ); + MD5_DO( F1, c, d, a, b, p_input[ 2 ] + 0x242070db, 17 ); + MD5_DO( F1, b, c, d, a, p_input[ 3 ] + 0xc1bdceee, 22 ); + MD5_DO( F1, a, b, c, d, p_input[ 4 ] + 0xf57c0faf, 7 ); + MD5_DO( F1, d, a, b, c, p_input[ 5 ] + 0x4787c62a, 12 ); + MD5_DO( F1, c, d, a, b, p_input[ 6 ] + 0xa8304613, 17 ); + MD5_DO( F1, b, c, d, a, p_input[ 7 ] + 0xfd469501, 22 ); + MD5_DO( F1, a, b, c, d, p_input[ 8 ] + 0x698098d8, 7 ); + MD5_DO( F1, d, a, b, c, p_input[ 9 ] + 0x8b44f7af, 12 ); + MD5_DO( F1, c, d, a, b, p_input[ 10 ] + 0xffff5bb1, 17 ); + MD5_DO( F1, b, c, d, a, p_input[ 11 ] + 0x895cd7be, 22 ); + MD5_DO( F1, a, b, c, d, p_input[ 12 ] + 0x6b901122, 7 ); + MD5_DO( F1, d, a, b, c, p_input[ 13 ] + 0xfd987193, 12 ); + MD5_DO( F1, c, d, a, b, p_input[ 14 ] + 0xa679438e, 17 ); + MD5_DO( F1, b, c, d, a, p_input[ 15 ] + 0x49b40821, 22 ); + + MD5_DO( F2, a, b, c, d, p_input[ 1 ] + 0xf61e2562, 5 ); + MD5_DO( F2, d, a, b, c, p_input[ 6 ] + 0xc040b340, 9 ); + MD5_DO( F2, c, d, a, b, p_input[ 11 ] + 0x265e5a51, 14 ); + MD5_DO( F2, b, c, d, a, p_input[ 0 ] + 0xe9b6c7aa, 20 ); + MD5_DO( F2, a, b, c, d, p_input[ 5 ] + 0xd62f105d, 5 ); + MD5_DO( F2, d, a, b, c, p_input[ 10 ] + 0x02441453, 9 ); + MD5_DO( F2, c, d, a, b, p_input[ 15 ] + 0xd8a1e681, 14 ); + MD5_DO( F2, b, c, d, a, p_input[ 4 ] + 0xe7d3fbc8, 20 ); + MD5_DO( F2, a, b, c, d, p_input[ 9 ] + 0x21e1cde6, 5 ); + MD5_DO( F2, d, a, b, c, p_input[ 14 ] + 0xc33707d6, 9 ); + MD5_DO( F2, c, d, a, b, p_input[ 3 ] + 0xf4d50d87, 14 ); + MD5_DO( F2, b, c, d, a, p_input[ 8 ] + 0x455a14ed, 20 ); + MD5_DO( F2, a, b, c, d, p_input[ 13 ] + 0xa9e3e905, 5 ); + MD5_DO( F2, d, a, b, c, p_input[ 2 ] + 0xfcefa3f8, 9 ); + MD5_DO( F2, c, d, a, b, p_input[ 7 ] + 0x676f02d9, 14 ); + MD5_DO( F2, b, c, d, a, p_input[ 12 ] + 0x8d2a4c8a, 20 ); + + MD5_DO( F3, a, b, c, d, p_input[ 5 ] + 0xfffa3942, 4 ); + MD5_DO( F3, d, a, b, c, p_input[ 8 ] + 0x8771f681, 11 ); + MD5_DO( F3, c, d, a, b, p_input[ 11 ] + 0x6d9d6122, 16 ); + MD5_DO( F3, b, c, d, a, p_input[ 14 ] + 0xfde5380c, 23 ); + MD5_DO( F3, a, b, c, d, p_input[ 1 ] + 0xa4beea44, 4 ); + MD5_DO( F3, d, a, b, c, p_input[ 4 ] + 0x4bdecfa9, 11 ); + MD5_DO( F3, c, d, a, b, p_input[ 7 ] + 0xf6bb4b60, 16 ); + MD5_DO( F3, b, c, d, a, p_input[ 10 ] + 0xbebfbc70, 23 ); + MD5_DO( F3, a, b, c, d, p_input[ 13 ] + 0x289b7ec6, 4 ); + MD5_DO( F3, d, a, b, c, p_input[ 0 ] + 0xeaa127fa, 11 ); + MD5_DO( F3, c, d, a, b, p_input[ 3 ] + 0xd4ef3085, 16 ); + MD5_DO( F3, b, c, d, a, p_input[ 6 ] + 0x04881d05, 23 ); + MD5_DO( F3, a, b, c, d, p_input[ 9 ] + 0xd9d4d039, 4 ); + MD5_DO( F3, d, a, b, c, p_input[ 12 ] + 0xe6db99e5, 11 ); + MD5_DO( F3, c, d, a, b, p_input[ 15 ] + 0x1fa27cf8, 16 ); + MD5_DO( F3, b, c, d, a, p_input[ 2 ] + 0xc4ac5665, 23 ); + + MD5_DO( F4, a, b, c, d, p_input[ 0 ] + 0xf4292244, 6 ); + MD5_DO( F4, d, a, b, c, p_input[ 7 ] + 0x432aff97, 10 ); + MD5_DO( F4, c, d, a, b, p_input[ 14 ] + 0xab9423a7, 15 ); + MD5_DO( F4, b, c, d, a, p_input[ 5 ] + 0xfc93a039, 21 ); + MD5_DO( F4, a, b, c, d, p_input[ 12 ] + 0x655b59c3, 6 ); + MD5_DO( F4, d, a, b, c, p_input[ 3 ] + 0x8f0ccc92, 10 ); + MD5_DO( F4, c, d, a, b, p_input[ 10 ] + 0xffeff47d, 15 ); + MD5_DO( F4, b, c, d, a, p_input[ 1 ] + 0x85845dd1, 21 ); + MD5_DO( F4, a, b, c, d, p_input[ 8 ] + 0x6fa87e4f, 6 ); + MD5_DO( F4, d, a, b, c, p_input[ 15 ] + 0xfe2ce6e0, 10 ); + MD5_DO( F4, c, d, a, b, p_input[ 6 ] + 0xa3014314, 15 ); + MD5_DO( F4, b, c, d, a, p_input[ 13 ] + 0x4e0811a1, 21 ); + MD5_DO( F4, a, b, c, d, p_input[ 4 ] + 0xf7537e82, 6 ); + MD5_DO( F4, d, a, b, c, p_input[ 11 ] + 0xbd3af235, 10 ); + MD5_DO( F4, c, d, a, b, p_input[ 2 ] + 0x2ad7d2bb, 15 ); + MD5_DO( F4, b, c, d, a, p_input[ 9 ] + 0xeb86d391, 21 ); + + p_md5->p_digest[ 0 ] += a; + p_md5->p_digest[ 1 ] += b; + p_md5->p_digest[ 2 ] += c; + p_md5->p_digest[ 3 ] += d; +} + +/***************************************************************************** + * InitShuffle: initialise a shuffle structure + ***************************************************************************** + * This function initialises tables in the p_shuffle structure that will be + * used later by DoShuffle. The only external parameter is p_sys_key. + *****************************************************************************/ +static void InitShuffle( struct shuffle_s *p_shuffle, uint32_t *p_sys_key ) +{ + char p_secret1[] = "Tv!*"; + static char const p_secret2[] = "v8rhvsaAvOKMFfUH%798=[;." + "f8677680a634ba87fnOIf)(*"; + unsigned int i; + + /* Fill p_commands using the key and a secret seed */ + for( i = 0; i < 20; i++ ) + { + struct md5_s md5; + int32_t i_hash; + + InitMD5( &md5 ); + AddMD5( &md5, (uint8_t *)p_sys_key, 16 ); + AddMD5( &md5, (uint8_t *)p_secret1, 4 ); + EndMD5( &md5 ); + + p_secret1[ 3 ]++; + + REVERSE( md5.p_digest, 1 ); + i_hash = ((int32_t)U32_AT(md5.p_digest)) % 1024; + + p_shuffle->p_commands[ i ] = i_hash < 0 ? i_hash * -1 : i_hash; + } + + /* Fill p_bordel with completely meaningless initial values. */ + for( i = 0; i < 4; i++ ) + { + p_shuffle->p_bordel[ 4 * i ] = U32_AT(p_sys_key + i); + memcpy( p_shuffle->p_bordel + 4 * i + 1, p_secret2 + 12 * i, 12 ); + REVERSE( p_shuffle->p_bordel + 4 * i + 1, 3 ); + } +} + +/***************************************************************************** + * DoShuffle: shuffle buffer + ***************************************************************************** + * This is so ugly and uses so many MD5 checksums that it is most certainly + * one-way, though why it needs to be so complicated is beyond me. + *****************************************************************************/ +static void DoShuffle( struct shuffle_s *p_shuffle, + uint32_t *p_buffer, uint32_t i_size ) +{ + struct md5_s md5; + uint32_t p_big_bordel[ 16 ]; + uint32_t *p_bordel = p_shuffle->p_bordel; + unsigned int i; + + /* Using the MD5 hash of a memory block is probably not one-way enough + * for the iTunes people. This function randomises p_bordel depending on + * the values in p_commands to make things even more messy in p_bordel. */ + for( i = 0; i < 20; i++ ) + { + uint8_t i_command, i_index; + + if( !p_shuffle->p_commands[ i ] ) + { + continue; + } + + i_command = (p_shuffle->p_commands[ i ] & 0x300) >> 8; + i_index = p_shuffle->p_commands[ i ] & 0xff; + + switch( i_command ) + { + case 0x3: + p_bordel[ i_index & 0xf ] = p_bordel[ i_index >> 4 ] + + p_bordel[ ((i_index + 0x10) >> 4) & 0xf ]; + break; + case 0x2: + p_bordel[ i_index >> 4 ] ^= p_shuffle_xor[ 0xff - i_index ]; + break; + case 0x1: + p_bordel[ i_index >> 4 ] -= p_shuffle_sub[ 0xff - i_index ]; + break; + default: + p_bordel[ i_index >> 4 ] += p_shuffle_add[ 0xff - i_index ]; + break; + } + } + + /* Convert our newly randomised p_bordel to big endianness and take + * its MD5 hash. */ + InitMD5( &md5 ); + for( i = 0; i < 16; i++ ) + { + p_big_bordel[ i ] = U32_AT(p_bordel + i); + } + AddMD5( &md5, (uint8_t *)p_big_bordel, 64 ); + EndMD5( &md5 ); + + /* XOR our buffer with the computed checksum */ + for( i = 0; i < i_size; i++ ) + { + p_buffer[ i ] ^= md5.p_digest[ i ]; + } +} + +/***************************************************************************** + * GetSystemKey: get the system key + ***************************************************************************** + * Compute the system key from various system information, see HashSystemInfo. + *****************************************************************************/ +static int GetSystemKey( uint32_t *p_sys_key, uint32_t b_ipod ) +{ + static char const p_secret1[ 8 ] = "YuaFlafu"; + static char const p_secret2[ 8 ] = "zPif98ga"; + struct md5_s md5; + int64_t i_ipod_id; + uint32_t p_system_hash[ 4 ]; + + /* Compute the MD5 hash of our system info */ + if( ( !b_ipod && HashSystemInfo( p_system_hash ) ) || + ( b_ipod && GetiPodID( &i_ipod_id ) ) ) + { + return -1; + } + + /* Combine our system info hash with additional secret data. The resulting + * MD5 hash will be our system key. */ + InitMD5( &md5 ); + AddMD5( &md5, p_secret1, 8 ); + + if( !b_ipod ) + { + AddMD5( &md5, (uint8_t *)p_system_hash, 6 ); + AddMD5( &md5, (uint8_t *)p_system_hash, 6 ); + AddMD5( &md5, (uint8_t *)p_system_hash, 6 ); + AddMD5( &md5, p_secret2, 8 ); + } + else + { + i_ipod_id = U64_AT(&i_ipod_id); + AddMD5( &md5, (uint8_t *)&i_ipod_id, sizeof(i_ipod_id) ); + AddMD5( &md5, (uint8_t *)&i_ipod_id, sizeof(i_ipod_id) ); + AddMD5( &md5, (uint8_t *)&i_ipod_id, sizeof(i_ipod_id) ); + } + + EndMD5( &md5 ); + + memcpy( p_sys_key, md5.p_digest, 16 ); + + return 0; +} + +#ifdef _WIN32 +# define DRMS_DIRNAME "drms" +#else +# define DRMS_DIRNAME ".drms" +#endif + +/***************************************************************************** + * WriteUserKey: write the user key to hard disk + ***************************************************************************** + * Write the user key to the hard disk so that it can be reused later or used + * on operating systems other than Win32. + *****************************************************************************/ +static int WriteUserKey( void *_p_drms, uint32_t *p_user_key ) +{ + struct drms_s *p_drms = (struct drms_s *)_p_drms; + FILE *file; + int i_ret = -1; + char psz_path[ PATH_MAX ]; + + sprintf( psz_path, /* PATH_MAX - 1, */ + "%s/" DRMS_DIRNAME, p_drms->psz_homedir ); + +#if defined( HAVE_ERRNO_H ) +# if defined( _WIN32 ) + if( !mkdir( psz_path ) || errno == EEXIST ) +# else + if( !mkdir( psz_path, 0755 ) || errno == EEXIST ) +# endif +#else + if( !mkdir( psz_path ) ) +#endif + { + sprintf( psz_path, /*PATH_MAX - 1,*/ "%s/" DRMS_DIRNAME "/%08X.%03d", + p_drms->psz_homedir, p_drms->i_user, p_drms->i_key ); + + file = fopen( psz_path, "w" ); + if( file != NULL ) + { + i_ret = fwrite( p_user_key, sizeof(uint32_t), + 4, file ) == 4 ? 0 : -1; + fclose( file ); + } + } + + return i_ret; +} + +/***************************************************************************** + * ReadUserKey: read the user key from hard disk + ***************************************************************************** + * Retrieve the user key from the hard disk if available. + *****************************************************************************/ +static int ReadUserKey( void *_p_drms, uint32_t *p_user_key ) +{ + struct drms_s *p_drms = (struct drms_s *)_p_drms; + FILE *file; + int i_ret = -1; + char psz_path[ PATH_MAX ]; + + sprintf( psz_path, /*PATH_MAX - 1,*/ + "%s/" DRMS_DIRNAME "/%08X.%03d", p_drms->psz_homedir, + p_drms->i_user, p_drms->i_key ); + + file = fopen( psz_path, "r" ); + if( file != NULL ) + { + i_ret = fread( p_user_key, sizeof(uint32_t), + 4, file ) == 4 ? 0 : -1; + fclose( file ); + } + + return i_ret; +} + +/***************************************************************************** + * GetUserKey: get the user key + ***************************************************************************** + * Retrieve the user key from the hard disk if available, otherwise generate + * it from the system key. If the key could be successfully generated, write + * it to the hard disk for future use. + *****************************************************************************/ +static int GetUserKey( void *_p_drms, uint32_t *p_user_key ) +{ + static char const p_secret[] = "mUfnpognadfgf873"; + struct drms_s *p_drms = (struct drms_s *)_p_drms; + struct aes_s aes; + struct shuffle_s shuffle; + uint32_t i, y; + uint32_t *p_sci_data; + uint32_t i_user, i_key; + uint32_t p_sys_key[ 4 ]; + uint32_t i_sci_size, i_blocks, i_remaining; + uint32_t *p_sci0, *p_sci1, *p_buffer; + uint32_t p_sci_key[ 4 ]; + char *psz_ipod; + int i_ret = -1; + + if( !ReadUserKey( p_drms, p_user_key ) ) + { + REVERSE( p_user_key, 4 ); + return 0; + } + + psz_ipod = getenv( "IPOD" ); + + if( GetSystemKey( p_sys_key, psz_ipod ? 1 : 0 ) ) + { + return -1; + } + + if( GetSCIData( psz_ipod, &p_sci_data, &i_sci_size ) ) + { + return -1; + } + + /* Phase 1: unscramble the SCI data using the system key and shuffle + * it using DoShuffle(). */ + + /* Skip the first 4 bytes (some sort of header). Decrypt the rest. */ + i_blocks = (i_sci_size - 4) / 16; + i_remaining = (i_sci_size - 4) - (i_blocks * 16); + p_buffer = p_sci_data + 1; + + /* Decrypt and shuffle our data at the same time */ + InitAES( &aes, p_sys_key ); + REVERSE( p_sys_key, 4 ); + InitShuffle( &shuffle, p_sys_key ); + + memcpy( p_sci_key, p_secret, 16 ); + REVERSE( p_sci_key, 4 ); + + while( i_blocks-- ) + { + uint32_t p_tmp[ 4 ]; + + REVERSE( p_buffer, 4 ); + DecryptAES( &aes, p_tmp, p_buffer ); + BlockXOR( p_tmp, p_sci_key, p_tmp ); + + /* Use the previous scrambled data as the key for next block */ + memcpy( p_sci_key, p_buffer, 16 ); + + /* Shuffle the decrypted data using a custom routine */ + DoShuffle( &shuffle, p_tmp, 4 ); + + /* Copy this block back to p_buffer */ + memcpy( p_buffer, p_tmp, 16 ); + + p_buffer += 4; + } + + if( i_remaining >= 4 ) + { + i_remaining /= 4; + REVERSE( p_buffer, i_remaining ); + DoShuffle( &shuffle, p_buffer, i_remaining ); + } + + /* Phase 2: look for the user key in the generated data. I must admit I + * do not understand what is going on here, because it almost + * looks like we are browsing data that makes sense, even though + * the DoShuffle() part made it completely meaningless. */ + + y = 0; + REVERSE( p_sci_data + 5, 1 ); + i = U32_AT( p_sci_data + 5 ); + i_sci_size -= 22 * sizeof(uint32_t); + p_sci1 = p_sci_data + 22; + p_sci0 = NULL; + + while( i_sci_size >= 20 && i > 0 ) + { + if( p_sci0 == NULL ) + { + i_sci_size -= 18 * sizeof(uint32_t); + if( i_sci_size < 20 ) + { + break; + } + + p_sci0 = p_sci1; + REVERSE( p_sci1 + 17, 1 ); + y = U32_AT( p_sci1 + 17 ); + p_sci1 += 18; + } + + if( !y ) + { + i--; + p_sci0 = NULL; + continue; + } + + i_user = U32_AT( p_sci0 ); + i_key = U32_AT( p_sci1 ); + REVERSE( &i_user, 1 ); + REVERSE( &i_key, 1 ); + if( i_user == p_drms->i_user && ( ( i_key == p_drms->i_key ) || + ( !p_drms->i_key && ( p_sci1 == (p_sci0 + 18) ) ) ) ) + { + memcpy( p_user_key, p_sci1 + 1, 16 ); + REVERSE( p_sci1 + 1, 4 ); + WriteUserKey( p_drms, p_sci1 + 1 ); + i_ret = 0; + break; + } + + y--; + p_sci1 += 5; + i_sci_size -= 5 * sizeof(uint32_t); + } + + free( p_sci_data ); + + return i_ret; +} + +/***************************************************************************** + * GetSCIData: get SCI data from "SC Info.sidb" + ***************************************************************************** + * Read SCI data from "\Apple Computer\iTunes\SC Info\SC Info.sidb" + *****************************************************************************/ +static int GetSCIData( char *psz_ipod, uint32_t **pp_sci, + uint32_t *pi_sci_size ) +{ + FILE *file; + char *psz_path = NULL; + char p_tmp[ PATH_MAX ]; + int i_ret = -1; + + if( psz_ipod == NULL ) + { +#ifdef _WIN32 + char *p_filename = "\\Apple Computer\\iTunes\\SC Info\\SC Info.sidb"; + typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD, + LPSTR ); + HINSTANCE shfolder_dll = NULL; + SHGETFOLDERPATH dSHGetFolderPath = NULL; + + if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL ) + { + dSHGetFolderPath = + (SHGETFOLDERPATH)GetProcAddress( shfolder_dll, + _T("SHGetFolderPathA") ); + } + + if( dSHGetFolderPath != NULL && + SUCCEEDED( dSHGetFolderPath( NULL, /*CSIDL_COMMON_APPDATA*/ 0x0023, + NULL, 0, p_tmp ) ) ) + { + strncat( p_tmp, p_filename, min( strlen( p_filename ), + (sizeof(p_tmp)/sizeof(p_tmp[0]) - 1) - + strlen( p_tmp ) ) ); + psz_path = p_tmp; + } + + if( shfolder_dll != NULL ) + { + FreeLibrary( shfolder_dll ); + } +#endif + } + else + { +#define ISCINFO "iSCInfo" + if( strstr( psz_ipod, ISCINFO ) == NULL ) + { + sprintf( p_tmp, /*sizeof(p_tmp)/sizeof(p_tmp[0]) - 1,*/ + "%s/iPod_Control/iTunes/" ISCINFO, psz_ipod ); + psz_path = p_tmp; + } + else + { + psz_path = psz_ipod; + } + } + + if( psz_path == NULL ) + { + return -1; + } + + file = fopen( psz_path, "r" ); + if( file != NULL ) + { + struct stat st; + + if( !fstat( fileno( file ), &st ) ) + { + *pp_sci = malloc( st.st_size ); + if( *pp_sci != NULL ) + { + if( fread( *pp_sci, 1, st.st_size, + file ) == (size_t)st.st_size ) + { + *pi_sci_size = st.st_size; + i_ret = 0; + } + else + { + free( (void *)*pp_sci ); + *pp_sci = NULL; + } + } + } + + fclose( file ); + } + + return i_ret; +} + +/***************************************************************************** + * HashSystemInfo: hash system information + ***************************************************************************** + * This function computes the MD5 hash of the C: hard drive serial number, + * BIOS version, CPU type and Windows version. + *****************************************************************************/ +static int HashSystemInfo( uint32_t *p_system_hash ) +{ + struct md5_s md5; + int i_ret = 0; + +#ifdef _WIN32 + HKEY i_key; + unsigned int i; + DWORD i_size; + DWORD i_serial; + LPBYTE p_reg_buf; + + static LPCTSTR p_reg_keys[ 3 ][ 2 ] = + { + { + _T("HARDWARE\\DESCRIPTION\\System"), + _T("SystemBiosVersion") + }, + + { + _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), + _T("ProcessorNameString") + }, + + { + _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"), + _T("ProductId") + } + }; + + InitMD5( &md5 ); + + AddMD5( &md5, "cache-control", 13 ); + AddMD5( &md5, "Ethernet", 8 ); + + GetVolumeInformation( _T("C:\\"), NULL, 0, &i_serial, + NULL, NULL, NULL, 0 ); + AddMD5( &md5, (uint8_t *)&i_serial, 4 ); + + for( i = 0; i < sizeof(p_reg_keys) / sizeof(p_reg_keys[ 0 ]); i++ ) + { + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, p_reg_keys[ i ][ 0 ], + 0, KEY_READ, &i_key ) != ERROR_SUCCESS ) + { + continue; + } + + if( RegQueryValueEx( i_key, p_reg_keys[ i ][ 1 ], + NULL, NULL, NULL, &i_size ) != ERROR_SUCCESS ) + { + RegCloseKey( i_key ); + continue; + } + + p_reg_buf = malloc( i_size ); + + if( p_reg_buf != NULL ) + { + if( RegQueryValueEx( i_key, p_reg_keys[ i ][ 1 ], + NULL, NULL, p_reg_buf, + &i_size ) == ERROR_SUCCESS ) + { + AddMD5( &md5, (uint8_t *)p_reg_buf, i_size ); + } + + free( p_reg_buf ); + } + + RegCloseKey( i_key ); + } + +#else + InitMD5( &md5 ); + i_ret = -1; +#endif + + EndMD5( &md5 ); + memcpy( p_system_hash, md5.p_digest, 16 ); + + return i_ret; +} + +/***************************************************************************** + * GetiPodID: Get iPod ID + ***************************************************************************** + * This function gets the iPod ID. + *****************************************************************************/ +static int GetiPodID( int64_t *p_ipod_id ) +{ + int i_ret = -1; + +#define PROD_NAME "iPod" +#define VENDOR_NAME "Apple Computer, Inc." + + char *psz_ipod_id = getenv( "IPODID" ); + if( psz_ipod_id != NULL ) + { +#ifndef _WIN32 + *p_ipod_id = strtoll( psz_ipod_id, NULL, 16 ); +#else + *p_ipod_id = strtol( psz_ipod_id, NULL, 16 ); +#endif + return 0; + } + +#ifdef HAVE_IOKIT_IOKITLIB_H + CFTypeRef value; + mach_port_t port; + io_object_t device; + io_iterator_t iterator; + CFMutableDictionaryRef matching_dic; + + if( IOMasterPort( MACH_PORT_NULL, &port ) == KERN_SUCCESS ) + { + if( ( matching_dic = IOServiceMatching( "IOFireWireUnit" ) ) != NULL ) + { + CFDictionarySetValue( matching_dic, + CFSTR("FireWire Vendor Name"), + CFSTR(VENDOR_NAME) ); + CFDictionarySetValue( matching_dic, + CFSTR("FireWire Product Name"), + CFSTR(PROD_NAME) ); + + if( IOServiceGetMatchingServices( port, matching_dic, + &iterator ) == KERN_SUCCESS ) + { + while( ( device = IOIteratorNext( iterator ) ) != NULL ) + { + value = IORegistryEntryCreateCFProperty( device, + CFSTR("GUID"), kCFAllocatorDefault, kNilOptions ); + + if( value != NULL ) + { + if( CFGetTypeID( value ) == CFNumberGetTypeID() ) + { + int64_t i_ipod_id; + CFNumberGetValue( (CFNumberRef)value, + kCFNumberLongLongType, + &i_ipod_id ); + *p_ipod_id = i_ipod_id; + i_ret = 0; + } + + CFRelease( value ); + } + + IOObjectRelease( device ); + + if( !i_ret ) break; + } + + IOObjectRelease( iterator ); + } + } + + mach_port_deallocate( mach_task_self(), port ); + } + +#elif HAVE_SYSFS_LIBSYSFS_H + struct sysfs_bus *bus = NULL; + struct dlist *devlist = NULL; + struct dlist *attributes = NULL; + struct sysfs_device *curdev = NULL; + struct sysfs_attribute *curattr = NULL; + + bus = sysfs_open_bus( "ieee1394" ); + if( bus != NULL ) + { + devlist = sysfs_get_bus_devices( bus ); + if( devlist != NULL ) + { + dlist_for_each_data( devlist, curdev, struct sysfs_device ) + { + attributes = sysfs_get_device_attributes( curdev ); + if( attributes != NULL ) + { + dlist_for_each_data( attributes, curattr, + struct sysfs_attribute ) + { + if( ( strcmp( curattr->name, "model_name" ) == 0 ) && + ( strncmp( curattr->value, PROD_NAME, + sizeof(PROD_NAME) ) == 0 ) ) + { + *p_ipod_id = strtoll( curdev->name, NULL, 16 ); + i_ret = 0; + break; + } + } + } + + if( !i_ret ) break; + } + } + + sysfs_close_bus( bus ); + } +#endif + + return i_ret; +} + +#endif diff --git a/Libraries/FAAD2/Files/common/mp4ff/drms.h b/Libraries/FAAD2/Files/common/mp4ff/drms.h new file mode 100644 index 000000000..0993e7681 --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/drms.h @@ -0,0 +1,30 @@ +/***************************************************************************** + * drms.h : DRMS + ***************************************************************************** + * Copyright (C) 2004 VideoLAN + * $Id$ + * + * Author: Jon Lech Johansen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +extern void *drms_alloc( char *psz_homedir ); +extern void drms_free( void *p_drms ); +extern int drms_init( void *p_drms, uint32_t i_type, + uint8_t *p_info, uint32_t i_len ); +extern void drms_decrypt( void *p_drms, uint32_t *p_buffer, + uint32_t i_len ); + diff --git a/Libraries/FAAD2/Files/common/mp4ff/drmstables.h b/Libraries/FAAD2/Files/common/mp4ff/drmstables.h new file mode 100644 index 000000000..9216d85a3 --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/drmstables.h @@ -0,0 +1,288 @@ +/***************************************************************************** + * drmstables.h : AES/Rijndael block cipher and miscellaneous tables + ***************************************************************************** + * Copyright (C) 2004 VideoLAN + * $Id$ + * + * Author: Jon Lech Johansen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#define AES_ROR( x, n ) (((x) << (32-(n))) | ((x) >> (n))) + +#define AES_XOR_ROR( p_table, p_tmp ) \ + ( p_table[ (p_tmp[ t > 2 ? t - 3 : t + 1 ] >> 24) & 0xFF ] \ + ^ AES_ROR( p_table[ (p_tmp[ t > 1 ? t - 2 : t + 2 ] >> 16) & 0xFF ], 8 ) \ + ^ AES_ROR( p_table[ (p_tmp[ t > 0 ? t - 1 : t + 3 ] >> 8) & 0xFF ], 16 ) \ + ^ AES_ROR( p_table[ p_tmp[ t ] & 0xFF ], 24 ) ) + +#define AES_KEY_COUNT 10 + +static uint32_t const p_aes_table[ AES_KEY_COUNT ] = +{ + 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, + 0x00000040, 0x00000080, 0x0000001b, 0x00000036 +}; + +static uint32_t const p_aes_encrypt[ 256 ] = +{ + 0x63000000, 0x7c000000, 0x77000000, 0x7b000000, 0xf2000000, 0x6b000000, + 0x6f000000, 0xc5000000, 0x30000000, 0x01000000, 0x67000000, 0x2b000000, + 0xfe000000, 0xd7000000, 0xab000000, 0x76000000, 0xca000000, 0x82000000, + 0xc9000000, 0x7d000000, 0xfa000000, 0x59000000, 0x47000000, 0xf0000000, + 0xad000000, 0xd4000000, 0xa2000000, 0xaf000000, 0x9c000000, 0xa4000000, + 0x72000000, 0xc0000000, 0xb7000000, 0xfd000000, 0x93000000, 0x26000000, + 0x36000000, 0x3f000000, 0xf7000000, 0xcc000000, 0x34000000, 0xa5000000, + 0xe5000000, 0xf1000000, 0x71000000, 0xd8000000, 0x31000000, 0x15000000, + 0x04000000, 0xc7000000, 0x23000000, 0xc3000000, 0x18000000, 0x96000000, + 0x05000000, 0x9a000000, 0x07000000, 0x12000000, 0x80000000, 0xe2000000, + 0xeb000000, 0x27000000, 0xb2000000, 0x75000000, 0x09000000, 0x83000000, + 0x2c000000, 0x1a000000, 0x1b000000, 0x6e000000, 0x5a000000, 0xa0000000, + 0x52000000, 0x3b000000, 0xd6000000, 0xb3000000, 0x29000000, 0xe3000000, + 0x2f000000, 0x84000000, 0x53000000, 0xd1000000, 0x00000000, 0xed000000, + 0x20000000, 0xfc000000, 0xb1000000, 0x5b000000, 0x6a000000, 0xcb000000, + 0xbe000000, 0x39000000, 0x4a000000, 0x4c000000, 0x58000000, 0xcf000000, + 0xd0000000, 0xef000000, 0xaa000000, 0xfb000000, 0x43000000, 0x4d000000, + 0x33000000, 0x85000000, 0x45000000, 0xf9000000, 0x02000000, 0x7f000000, + 0x50000000, 0x3c000000, 0x9f000000, 0xa8000000, 0x51000000, 0xa3000000, + 0x40000000, 0x8f000000, 0x92000000, 0x9d000000, 0x38000000, 0xf5000000, + 0xbc000000, 0xb6000000, 0xda000000, 0x21000000, 0x10000000, 0xff000000, + 0xf3000000, 0xd2000000, 0xcd000000, 0x0c000000, 0x13000000, 0xec000000, + 0x5f000000, 0x97000000, 0x44000000, 0x17000000, 0xc4000000, 0xa7000000, + 0x7e000000, 0x3d000000, 0x64000000, 0x5d000000, 0x19000000, 0x73000000, + 0x60000000, 0x81000000, 0x4f000000, 0xdc000000, 0x22000000, 0x2a000000, + 0x90000000, 0x88000000, 0x46000000, 0xee000000, 0xb8000000, 0x14000000, + 0xde000000, 0x5e000000, 0x0b000000, 0xdb000000, 0xe0000000, 0x32000000, + 0x3a000000, 0x0a000000, 0x49000000, 0x06000000, 0x24000000, 0x5c000000, + 0xc2000000, 0xd3000000, 0xac000000, 0x62000000, 0x91000000, 0x95000000, + 0xe4000000, 0x79000000, 0xe7000000, 0xc8000000, 0x37000000, 0x6d000000, + 0x8d000000, 0xd5000000, 0x4e000000, 0xa9000000, 0x6c000000, 0x56000000, + 0xf4000000, 0xea000000, 0x65000000, 0x7a000000, 0xae000000, 0x08000000, + 0xba000000, 0x78000000, 0x25000000, 0x2e000000, 0x1c000000, 0xa6000000, + 0xb4000000, 0xc6000000, 0xe8000000, 0xdd000000, 0x74000000, 0x1f000000, + 0x4b000000, 0xbd000000, 0x8b000000, 0x8a000000, 0x70000000, 0x3e000000, + 0xb5000000, 0x66000000, 0x48000000, 0x03000000, 0xf6000000, 0x0e000000, + 0x61000000, 0x35000000, 0x57000000, 0xb9000000, 0x86000000, 0xc1000000, + 0x1d000000, 0x9e000000, 0xe1000000, 0xf8000000, 0x98000000, 0x11000000, + 0x69000000, 0xd9000000, 0x8e000000, 0x94000000, 0x9b000000, 0x1e000000, + 0x87000000, 0xe9000000, 0xce000000, 0x55000000, 0x28000000, 0xdf000000, + 0x8c000000, 0xa1000000, 0x89000000, 0x0d000000, 0xbf000000, 0xe6000000, + 0x42000000, 0x68000000, 0x41000000, 0x99000000, 0x2d000000, 0x0f000000, + 0xb0000000, 0x54000000, 0xbb000000, 0x16000000 +}; + +static uint32_t const p_aes_itable[ 256 ] = +{ + 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, + 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, + 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, 0xde495ab1, 0x25671bba, + 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, + 0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, + 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, + 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, + 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, + 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, + 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, + 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, + 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05, 0xd11f6234, 0xc48afea6, + 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6, 0x0b39ec83, 0x40aaef60, + 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, + 0x91b58d54, 0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, + 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8, + 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, 0x09838680, 0x3248ed2b, + 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, 0x0f563885, 0x3d1ed5ae, 0x3627392d, + 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, + 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, + 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09, 0xf2adc78b, + 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, + 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, + 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, + 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, + 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, + 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, 0x87c74e49, 0xd9c1d138, + 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, + 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, + 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, + 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, + 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, + 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, + 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, + 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, + 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, + 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, + 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, + 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, + 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, + 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, + 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c, 0xd89ce4b4, 0x6490c156, + 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8 +}; + +static uint32_t const p_aes_decrypt[ 256 ] = +{ + 0x52000000, 0x09000000, 0x6a000000, 0xd5000000, 0x30000000, 0x36000000, + 0xa5000000, 0x38000000, 0xbf000000, 0x40000000, 0xa3000000, 0x9e000000, + 0x81000000, 0xf3000000, 0xd7000000, 0xfb000000, 0x7c000000, 0xe3000000, + 0x39000000, 0x82000000, 0x9b000000, 0x2f000000, 0xff000000, 0x87000000, + 0x34000000, 0x8e000000, 0x43000000, 0x44000000, 0xc4000000, 0xde000000, + 0xe9000000, 0xcb000000, 0x54000000, 0x7b000000, 0x94000000, 0x32000000, + 0xa6000000, 0xc2000000, 0x23000000, 0x3d000000, 0xee000000, 0x4c000000, + 0x95000000, 0x0b000000, 0x42000000, 0xfa000000, 0xc3000000, 0x4e000000, + 0x08000000, 0x2e000000, 0xa1000000, 0x66000000, 0x28000000, 0xd9000000, + 0x24000000, 0xb2000000, 0x76000000, 0x5b000000, 0xa2000000, 0x49000000, + 0x6d000000, 0x8b000000, 0xd1000000, 0x25000000, 0x72000000, 0xf8000000, + 0xf6000000, 0x64000000, 0x86000000, 0x68000000, 0x98000000, 0x16000000, + 0xd4000000, 0xa4000000, 0x5c000000, 0xcc000000, 0x5d000000, 0x65000000, + 0xb6000000, 0x92000000, 0x6c000000, 0x70000000, 0x48000000, 0x50000000, + 0xfd000000, 0xed000000, 0xb9000000, 0xda000000, 0x5e000000, 0x15000000, + 0x46000000, 0x57000000, 0xa7000000, 0x8d000000, 0x9d000000, 0x84000000, + 0x90000000, 0xd8000000, 0xab000000, 0x00000000, 0x8c000000, 0xbc000000, + 0xd3000000, 0x0a000000, 0xf7000000, 0xe4000000, 0x58000000, 0x05000000, + 0xb8000000, 0xb3000000, 0x45000000, 0x06000000, 0xd0000000, 0x2c000000, + 0x1e000000, 0x8f000000, 0xca000000, 0x3f000000, 0x0f000000, 0x02000000, + 0xc1000000, 0xaf000000, 0xbd000000, 0x03000000, 0x01000000, 0x13000000, + 0x8a000000, 0x6b000000, 0x3a000000, 0x91000000, 0x11000000, 0x41000000, + 0x4f000000, 0x67000000, 0xdc000000, 0xea000000, 0x97000000, 0xf2000000, + 0xcf000000, 0xce000000, 0xf0000000, 0xb4000000, 0xe6000000, 0x73000000, + 0x96000000, 0xac000000, 0x74000000, 0x22000000, 0xe7000000, 0xad000000, + 0x35000000, 0x85000000, 0xe2000000, 0xf9000000, 0x37000000, 0xe8000000, + 0x1c000000, 0x75000000, 0xdf000000, 0x6e000000, 0x47000000, 0xf1000000, + 0x1a000000, 0x71000000, 0x1d000000, 0x29000000, 0xc5000000, 0x89000000, + 0x6f000000, 0xb7000000, 0x62000000, 0x0e000000, 0xaa000000, 0x18000000, + 0xbe000000, 0x1b000000, 0xfc000000, 0x56000000, 0x3e000000, 0x4b000000, + 0xc6000000, 0xd2000000, 0x79000000, 0x20000000, 0x9a000000, 0xdb000000, + 0xc0000000, 0xfe000000, 0x78000000, 0xcd000000, 0x5a000000, 0xf4000000, + 0x1f000000, 0xdd000000, 0xa8000000, 0x33000000, 0x88000000, 0x07000000, + 0xc7000000, 0x31000000, 0xb1000000, 0x12000000, 0x10000000, 0x59000000, + 0x27000000, 0x80000000, 0xec000000, 0x5f000000, 0x60000000, 0x51000000, + 0x7f000000, 0xa9000000, 0x19000000, 0xb5000000, 0x4a000000, 0x0d000000, + 0x2d000000, 0xe5000000, 0x7a000000, 0x9f000000, 0x93000000, 0xc9000000, + 0x9c000000, 0xef000000, 0xa0000000, 0xe0000000, 0x3b000000, 0x4d000000, + 0xae000000, 0x2a000000, 0xf5000000, 0xb0000000, 0xc8000000, 0xeb000000, + 0xbb000000, 0x3c000000, 0x83000000, 0x53000000, 0x99000000, 0x61000000, + 0x17000000, 0x2b000000, 0x04000000, 0x7e000000, 0xba000000, 0x77000000, + 0xd6000000, 0x26000000, 0xe1000000, 0x69000000, 0x14000000, 0x63000000, + 0x55000000, 0x21000000, 0x0c000000, 0x7d000000 +}; + +static uint16_t const p_shuffle_xor[ 256 ] = +{ + 0x00d1, 0x0315, 0x1a32, 0x19ec, 0x1bbb, 0x1d6f, 0x14fe, 0x0e9e, + 0x029e, 0x1b8f, 0x0b70, 0x033a, 0x188e, 0x1d18, 0x0bd8, 0x0edb, + 0x0c64, 0x1c2b, 0x149c, 0x047b, 0x1064, 0x1c7c, 0x118d, 0x1355, + 0x0ae5, 0x0f18, 0x016f, 0x17d6, 0x1595, 0x0084, 0x0616, 0x1ccd, + 0x1d94, 0x0618, 0x182c, 0x195b, 0x196d, 0x0394, 0x07db, 0x0287, + 0x1636, 0x0b81, 0x1519, 0x0df9, 0x1ba3, 0x1cc3, 0x0ee2, 0x1434, + 0x1457, 0x0ced, 0x0f7d, 0x0d7b, 0x0b9e, 0x0d13, 0x13d7, 0x18d0, + 0x1259, 0x1977, 0x0606, 0x1e80, 0x05f2, 0x06b8, 0x1f07, 0x1365, + 0x0334, 0x0e30, 0x195f, 0x15f1, 0x058e, 0x0aa8, 0x045a, 0x0465, + 0x0b3e, 0x071e, 0x0a36, 0x105c, 0x01ac, 0x1a1e, 0x04e4, 0x056b, + 0x12bf, 0x0da2, 0x0b41, 0x0eaf, 0x034f, 0x0181, 0x04e2, 0x002b, + 0x12e6, 0x01be, 0x10e8, 0x128f, 0x0eb2, 0x1369, 0x05be, 0x1a59, + 0x117e, 0x047c, 0x1e86, 0x056a, 0x0da7, 0x0d61, 0x03fc, 0x1e6e, + 0x1d0c, 0x1e6d, 0x14bf, 0x0c50, 0x063a, 0x1b47, 0x17ae, 0x1321, + 0x041b, 0x0a24, 0x0d4d, 0x1f2b, 0x1cb6, 0x1bed, 0x1549, 0x03a7, + 0x0254, 0x006c, 0x0c9e, 0x0f73, 0x006c, 0x0008, 0x11f9, 0x0dd5, + 0x0bcf, 0x0af9, 0x1dfe, 0x0341, 0x0e49, 0x0d38, 0x17cb, 0x1513, + 0x0e96, 0x00ed, 0x0556, 0x1b28, 0x100c, 0x19d8, 0x14fa, 0x028c, + 0x1c60, 0x1232, 0x13d3, 0x0d00, 0x1534, 0x192c, 0x14b5, 0x1cf2, + 0x0504, 0x0b5b, 0x1ecf, 0x0423, 0x183b, 0x06b0, 0x169e, 0x1066, + 0x04cb, 0x08a2, 0x1b4a, 0x1254, 0x198d, 0x1044, 0x0236, 0x1bd8, + 0x18a1, 0x03ff, 0x1a0d, 0x0277, 0x0c2d, 0x17c9, 0x007c, 0x116e, + 0x048a, 0x1eaf, 0x0922, 0x0c45, 0x0766, 0x1e5f, 0x1a28, 0x0120, + 0x1c15, 0x034c, 0x0508, 0x0e73, 0x0879, 0x0441, 0x09ae, 0x132f, + 0x14fe, 0x0413, 0x0a9d, 0x1727, 0x01d7, 0x1a2b, 0x0474, 0x18f0, + 0x1f3b, 0x14f5, 0x1071, 0x0895, 0x1071, 0x18ff, 0x18e3, 0x0eb9, + 0x0ba9, 0x0961, 0x1599, 0x019e, 0x1d12, 0x1baa, 0x1e94, 0x1921, + 0x14dc, 0x124e, 0x0a25, 0x03ab, 0x1cc0, 0x1ebb, 0x0b4b, 0x16e5, + 0x11ea, 0x0d78, 0x1bb3, 0x1ba7, 0x1510, 0x1b7b, 0x0c64, 0x1995, + 0x1a58, 0x1651, 0x1964, 0x147a, 0x15f2, 0x11bb, 0x1654, 0x166e, + 0x0ea9, 0x1de1, 0x1443, 0x13c5, 0x00e1, 0x0b2f, 0x0b6f, 0x0a37, + 0x18ac, 0x08e6, 0x06f0, 0x136e, 0x0853, 0x0b2e, 0x0813, 0x10d6 +}; + +static uint16_t const p_shuffle_sub[ 256 ] = +{ + 0x067a, 0x0c7d, 0x0b4f, 0x127d, 0x0bd6, 0x04ac, 0x16e0, 0x1730, + 0x0587, 0x0afb, 0x1ac3, 0x0120, 0x14b5, 0x0f67, 0x11de, 0x0961, + 0x1127, 0x1a68, 0x07f0, 0x17d0, 0x1a6f, 0x1f3b, 0x01ef, 0x0919, + 0x131e, 0x0f90, 0x19e9, 0x18a8, 0x0cb2, 0x1ad0, 0x0c66, 0x0378, + 0x03b0, 0x01be, 0x1866, 0x1159, 0x197c, 0x1105, 0x010b, 0x0353, + 0x1abb, 0x09a6, 0x028a, 0x1bad, 0x1b20, 0x0455, 0x0f57, 0x0588, + 0x1491, 0x0a1d, 0x0f04, 0x0650, 0x191e, 0x1e0e, 0x174b, 0x016b, + 0x051f, 0x0532, 0x00df, 0x1aea, 0x0005, 0x0e1b, 0x0ff6, 0x08d8, + 0x14b4, 0x086a, 0x0c20, 0x0149, 0x1971, 0x0f26, 0x1852, 0x017d, + 0x1228, 0x0352, 0x0a44, 0x1330, 0x18df, 0x1e38, 0x01bc, 0x0bac, + 0x1a48, 0x021f, 0x02f7, 0x0c31, 0x0bc4, 0x1e75, 0x105c, 0x13e3, + 0x0b20, 0x03a1, 0x1af3, 0x1a36, 0x0e34, 0x181f, 0x09bd, 0x122b, + 0x0ee0, 0x163b, 0x0be7, 0x103d, 0x1075, 0x1e9d, 0x02af, 0x0ba2, + 0x1daa, 0x0cf1, 0x04b6, 0x0598, 0x06a1, 0x0d33, 0x1cfe, 0x04ee, + 0x1bad, 0x07c8, 0x1a48, 0x05e6, 0x031f, 0x0e0a, 0x0326, 0x1650, + 0x0526, 0x0b4e, 0x08fc, 0x0e4d, 0x0832, 0x06ea, 0x09bf, 0x0993, + 0x09eb, 0x0f31, 0x071b, 0x14d5, 0x11ca, 0x0722, 0x120d, 0x014c, + 0x1993, 0x0ae4, 0x1ccb, 0x04e9, 0x0aee, 0x1708, 0x0c3d, 0x12f2, + 0x1a19, 0x07c1, 0x05a7, 0x0744, 0x1606, 0x1a9b, 0x042d, 0x1bfc, + 0x1841, 0x0c3c, 0x0ffe, 0x1ab1, 0x1416, 0x18a9, 0x0320, 0x1ec2, + 0x0ae7, 0x11c6, 0x124a, 0x11df, 0x0f81, 0x06cf, 0x0ed9, 0x0253, + 0x1d2b, 0x0349, 0x0805, 0x08b3, 0x1052, 0x12cf, 0x0a44, 0x0ea6, + 0x03bf, 0x1d90, 0x0ef8, 0x0657, 0x156d, 0x0405, 0x10be, 0x091f, + 0x1c82, 0x1725, 0x19ef, 0x0b8c, 0x04d9, 0x02c7, 0x025a, 0x1b89, + 0x0f5c, 0x013d, 0x02f7, 0x12e3, 0x0bc5, 0x1b56, 0x0848, 0x0239, + 0x0fcf, 0x03a4, 0x092d, 0x1354, 0x1d83, 0x01bd, 0x071a, 0x0af1, + 0x0875, 0x0793, 0x1b41, 0x1782, 0x0def, 0x1d20, 0x13be, 0x0095, + 0x1650, 0x19d4, 0x0de3, 0x0980, 0x18f2, 0x0ca3, 0x0098, 0x149a, + 0x0b81, 0x0ad2, 0x1bba, 0x1a02, 0x027b, 0x1906, 0x07f5, 0x1cae, + 0x0c3f, 0x02f6, 0x1298, 0x175e, 0x15b2, 0x13d8, 0x14cc, 0x161a, + 0x0a42, 0x15f3, 0x0870, 0x1c1d, 0x1203, 0x18b1, 0x1738, 0x1954, + 0x1143, 0x1ae8, 0x1d9d, 0x155b, 0x11e8, 0x0ed9, 0x06f7, 0x04ca +}; + +static uint16_t const p_shuffle_add[ 256 ] = +{ + 0x0706, 0x175a, 0x0def, 0x1e72, 0x0297, 0x1b0e, 0x1d5a, 0x15b8, + 0x13e2, 0x1347, 0x10c6, 0x0b4f, 0x0629, 0x0a75, 0x0a9b, 0x0f55, + 0x1a69, 0x09bf, 0x0ba6, 0x1582, 0x1086, 0x1921, 0x01cb, 0x1c6a, + 0x0ff5, 0x00f7, 0x0a67, 0x0a1e, 0x1838, 0x0196, 0x10d6, 0x0c7a, + 0x180e, 0x038d, 0x1add, 0x0684, 0x154a, 0x0ab0, 0x18a4, 0x0d73, + 0x1641, 0x0ec6, 0x09f1, 0x1a62, 0x0414, 0x162a, 0x194e, 0x1ec9, + 0x022f, 0x0296, 0x1104, 0x14fc, 0x096c, 0x1d02, 0x09bd, 0x027c, + 0x080e, 0x1324, 0x128c, 0x0dc1, 0x00b9, 0x17f2, 0x0cbc, 0x0f97, + 0x1b93, 0x1c3c, 0x0415, 0x0395, 0x0c7a, 0x06cc, 0x0d4b, 0x16e2, + 0x04a2, 0x0dab, 0x1228, 0x012b, 0x0896, 0x0012, 0x1cd6, 0x1dac, + 0x080d, 0x0446, 0x047a, 0x00ad, 0x029e, 0x0686, 0x17c3, 0x1466, + 0x0d16, 0x1896, 0x076e, 0x00cd, 0x17dc, 0x1e9f, 0x1a7c, 0x02bb, + 0x0d06, 0x112b, 0x14cb, 0x0a03, 0x1541, 0x1290, 0x0f6d, 0x1503, + 0x084b, 0x0382, 0x1a3f, 0x0371, 0x1977, 0x0b67, 0x0cad, 0x1df8, + 0x1ce3, 0x1306, 0x13f8, 0x1163, 0x1b0b, 0x00bd, 0x0bf0, 0x1a4f, + 0x16f7, 0x0b4f, 0x0cf8, 0x1254, 0x0541, 0x100d, 0x0296, 0x0410, + 0x1a2b, 0x1169, 0x17d9, 0x0819, 0x03d6, 0x0d03, 0x194d, 0x184a, + 0x07ca, 0x1989, 0x0fad, 0x011c, 0x1c71, 0x0ef6, 0x0dc8, 0x0f2f, + 0x0fa5, 0x11be, 0x0f3b, 0x1d52, 0x0de2, 0x016e, 0x1ad1, 0x0c4a, + 0x1bc2, 0x0ac9, 0x1485, 0x1bee, 0x0949, 0x1a79, 0x1894, 0x12bb, + 0x17b6, 0x14f5, 0x16b1, 0x142c, 0x1301, 0x03ef, 0x16ff, 0x0d37, + 0x0d78, 0x01ff, 0x00d6, 0x1053, 0x1a2a, 0x0f61, 0x1352, 0x0c7f, + 0x137f, 0x09c4, 0x1d96, 0x021d, 0x1037, 0x1b19, 0x10ef, 0x14e4, + 0x02a0, 0x0236, 0x0a5d, 0x1519, 0x141c, 0x1399, 0x007e, 0x1e74, + 0x0941, 0x1b3c, 0x0062, 0x0371, 0x09ad, 0x08e8, 0x0a24, 0x0b97, + 0x1ed2, 0x0889, 0x136b, 0x0006, 0x1c4c, 0x0444, 0x06f8, 0x0dfb, + 0x1d0f, 0x198d, 0x0700, 0x0afc, 0x1781, 0x12f3, 0x10da, 0x1f19, + 0x1055, 0x0dc9, 0x1860, 0x012b, 0x05bf, 0x082d, 0x0c17, 0x1941, + 0x0359, 0x1232, 0x104c, 0x0762, 0x0897, 0x1d6c, 0x030f, 0x1a36, + 0x16b0, 0x094d, 0x1782, 0x036f, 0x0eea, 0x06e6, 0x0d00, 0x0187, + 0x17e2, 0x05e5, 0x19fa, 0x1950, 0x146a, 0x0b2a, 0x0512, 0x0ee0, + 0x1e27, 0x112d, 0x1df0, 0x0b13, 0x0378, 0x1dd0, 0x00c1, 0x01e6 +}; + diff --git a/Libraries/FAAD2/Files/common/mp4ff/mp4atom.c b/Libraries/FAAD2/Files/common/mp4ff/mp4atom.c new file mode 100644 index 000000000..0b5b9559f --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/mp4atom.c @@ -0,0 +1,901 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2004 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#include +#ifndef _WIN32 +#include "config.h" +#else +#include +#ifdef ITUNES_DRM +#include +#endif +#include +#endif +#ifdef HAVE_GETPWUID +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#include "mp4ffint.h" + +#include "drms.h" + +/* parse atom header size */ +static int32_t mp4ff_atom_get_size(const int8_t *data) +{ + uint32_t result; + uint32_t a, b, c, d; + + a = (uint8_t)data[0]; + b = (uint8_t)data[1]; + c = (uint8_t)data[2]; + d = (uint8_t)data[3]; + + result = (a<<24) | (b<<16) | (c<<8) | d; + //if (result > 0 && result < 8) result = 8; + + return (int32_t)result; +} + +/* comnapre 2 atom names, returns 1 for equal, 0 for unequal */ +static int32_t mp4ff_atom_compare(const int8_t a1, const int8_t b1, const int8_t c1, const int8_t d1, + const int8_t a2, const int8_t b2, const int8_t c2, const int8_t d2) +{ + if (a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2) + return 1; + else + return 0; +} + +static uint8_t mp4ff_atom_name_to_type(const int8_t a, const int8_t b, + const int8_t c, const int8_t d) +{ + if (a == 'm') + { + if (mp4ff_atom_compare(a,b,c,d, 'm','o','o','v')) + return ATOM_MOOV; + else if (mp4ff_atom_compare(a,b,c,d, 'm','i','n','f')) + return ATOM_MINF; + else if (mp4ff_atom_compare(a,b,c,d, 'm','d','i','a')) + return ATOM_MDIA; + else if (mp4ff_atom_compare(a,b,c,d, 'm','d','a','t')) + return ATOM_MDAT; + else if (mp4ff_atom_compare(a,b,c,d, 'm','d','h','d')) + return ATOM_MDHD; + else if (mp4ff_atom_compare(a,b,c,d, 'm','v','h','d')) + return ATOM_MVHD; + else if (mp4ff_atom_compare(a,b,c,d, 'm','p','4','a')) + return ATOM_MP4A; + else if (mp4ff_atom_compare(a,b,c,d, 'm','p','4','v')) + return ATOM_MP4V; + else if (mp4ff_atom_compare(a,b,c,d, 'm','p','4','s')) + return ATOM_MP4S; + else if (mp4ff_atom_compare(a,b,c,d, 'm','e','t','a')) + return ATOM_META; + } else if (a == 't') { + if (mp4ff_atom_compare(a,b,c,d, 't','r','a','k')) + return ATOM_TRAK; + else if (mp4ff_atom_compare(a,b,c,d, 't','k','h','d')) + return ATOM_TKHD; + else if (mp4ff_atom_compare(a,b,c,d, 't','r','e','f')) + return ATOM_TREF; + else if (mp4ff_atom_compare(a,b,c,d, 't','r','k','n')) + return ATOM_TRACK; + else if (mp4ff_atom_compare(a,b,c,d, 't','m','p','o')) + return ATOM_TEMPO; + } else if (a == 's') { + if (mp4ff_atom_compare(a,b,c,d, 's','t','b','l')) + return ATOM_STBL; + else if (mp4ff_atom_compare(a,b,c,d, 's','m','h','d')) + return ATOM_SMHD; + else if (mp4ff_atom_compare(a,b,c,d, 's','t','s','d')) + return ATOM_STSD; + else if (mp4ff_atom_compare(a,b,c,d, 's','t','t','s')) + return ATOM_STTS; + else if (mp4ff_atom_compare(a,b,c,d, 's','t','c','o')) + return ATOM_STCO; + else if (mp4ff_atom_compare(a,b,c,d, 's','t','s','c')) + return ATOM_STSC; + else if (mp4ff_atom_compare(a,b,c,d, 's','t','s','z')) + return ATOM_STSZ; + else if (mp4ff_atom_compare(a,b,c,d, 's','t','z','2')) + return ATOM_STZ2; + else if (mp4ff_atom_compare(a,b,c,d, 's','k','i','p')) + return ATOM_SKIP; + else if (mp4ff_atom_compare(a,b,c,d, 's','i','n','f')) + return ATOM_SINF; + else if (mp4ff_atom_compare(a,b,c,d, 's','c','h','i')) + return ATOM_SCHI; + } else if (a == '©') { + if (mp4ff_atom_compare(a,b,c,d, '©','n','a','m')) + return ATOM_TITLE; + else if (mp4ff_atom_compare(a,b,c,d, '©','A','R','T')) + return ATOM_ARTIST; + else if (mp4ff_atom_compare(a,b,c,d, '©','w','r','t')) + return ATOM_WRITER; + else if (mp4ff_atom_compare(a,b,c,d, '©','a','l','b')) + return ATOM_ALBUM; + else if (mp4ff_atom_compare(a,b,c,d, '©','d','a','y')) + return ATOM_DATE; + else if (mp4ff_atom_compare(a,b,c,d, '©','t','o','o')) + return ATOM_TOOL; + else if (mp4ff_atom_compare(a,b,c,d, '©','c','m','t')) + return ATOM_COMMENT; + else if (mp4ff_atom_compare(a,b,c,d, '©','g','e','n')) + return ATOM_GENRE1; + } + + if (mp4ff_atom_compare(a,b,c,d, 'e','d','t','s')) + return ATOM_EDTS; + else if (mp4ff_atom_compare(a,b,c,d, 'e','s','d','s')) + return ATOM_ESDS; + else if (mp4ff_atom_compare(a,b,c,d, 'f','t','y','p')) + return ATOM_FTYP; + else if (mp4ff_atom_compare(a,b,c,d, 'f','r','e','e')) + return ATOM_FREE; + else if (mp4ff_atom_compare(a,b,c,d, 'h','m','h','d')) + return ATOM_HMHD; + else if (mp4ff_atom_compare(a,b,c,d, 'v','m','h','d')) + return ATOM_VMHD; + else if (mp4ff_atom_compare(a,b,c,d, 'u','d','t','a')) + return ATOM_UDTA; + else if (mp4ff_atom_compare(a,b,c,d, 'i','l','s','t')) + return ATOM_ILST; + else if (mp4ff_atom_compare(a,b,c,d, 'n','a','m','e')) + return ATOM_NAME; + else if (mp4ff_atom_compare(a,b,c,d, 'd','a','t','a')) + return ATOM_DATA; + else if (mp4ff_atom_compare(a,b,c,d, 'd','i','s','k')) + return ATOM_DISC; + else if (mp4ff_atom_compare(a,b,c,d, 'g','n','r','e')) + return ATOM_GENRE2; + else if (mp4ff_atom_compare(a,b,c,d, 'c','o','v','r')) + return ATOM_COVER; + else if (mp4ff_atom_compare(a,b,c,d, 'c','p','i','l')) + return ATOM_COMPILATION; + else if (mp4ff_atom_compare(a,b,c,d, 'c','t','t','s')) + return ATOM_CTTS; + else if (mp4ff_atom_compare(a,b,c,d, 'd','r','m','s')) + return ATOM_DRMS; + else if (mp4ff_atom_compare(a,b,c,d, 'f','r','m','a')) + return ATOM_FRMA; + else if (mp4ff_atom_compare(a,b,c,d, 'p','r','i','v')) + return ATOM_PRIV; + else if (mp4ff_atom_compare(a,b,c,d, 'i','v','i','v')) + return ATOM_IVIV; + else if (mp4ff_atom_compare(a,b,c,d, 'u','s','e','r')) + return ATOM_USER; + else if (mp4ff_atom_compare(a,b,c,d, 'k','e','y',' ')) + return ATOM_KEY; + else + return ATOM_UNKNOWN; +} + +/* read atom header, return atom size, atom size is with header included */ +uint64_t mp4ff_atom_read_header(mp4ff_t *f, uint8_t *atom_type, uint8_t *header_size) +{ + uint64_t size; + int32_t ret; + int8_t atom_header[8]; + + ret = mp4ff_read_data(f, atom_header, 8); + if (ret != 8) + return 0; + + size = mp4ff_atom_get_size(atom_header); + *header_size = 8; + + /* check for 64 bit atom size */ + if (size == 1) + { + *header_size = 16; + size = mp4ff_read_int64(f); + } + + //printf("%c%c%c%c\n", atom_header[4], atom_header[5], atom_header[6], atom_header[7]); + + *atom_type = mp4ff_atom_name_to_type(atom_header[4], atom_header[5], atom_header[6], atom_header[7]); + + return size; +} + +static int32_t mp4ff_read_stsz(mp4ff_t *f) +{ + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + f->track[f->total_tracks - 1]->stsz_sample_size = mp4ff_read_int32(f); + f->track[f->total_tracks - 1]->stsz_sample_count = mp4ff_read_int32(f); + + if (f->track[f->total_tracks - 1]->stsz_sample_size == 0) + { + int32_t i; + f->track[f->total_tracks - 1]->stsz_table = + (int32_t*)malloc(f->track[f->total_tracks - 1]->stsz_sample_count*sizeof(int32_t)); + + for (i = 0; i < f->track[f->total_tracks - 1]->stsz_sample_count; i++) + { + f->track[f->total_tracks - 1]->stsz_table[i] = mp4ff_read_int32(f); + } + } + + return 0; +} + +static int32_t mp4ff_read_esds(mp4ff_t *f) +{ + uint8_t tag; + uint32_t temp; + + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + + /* get and verify ES_DescrTag */ + tag = mp4ff_read_char(f); + if (tag == 0x03) + { + /* read length */ + if (mp4ff_read_mp4_descr_length(f) < 5 + 15) + { + return 1; + } + /* skip 3 bytes */ + mp4ff_read_int24(f); + } else { + /* skip 2 bytes */ + mp4ff_read_int16(f); + } + + /* get and verify DecoderConfigDescrTab */ + if (mp4ff_read_char(f) != 0x04) + { + return 1; + } + + /* read length */ + temp = mp4ff_read_mp4_descr_length(f); + if (temp < 13) return 1; + + f->track[f->total_tracks - 1]->audioType = mp4ff_read_char(f); + mp4ff_read_int32(f);//0x15000414 ???? + f->track[f->total_tracks - 1]->maxBitrate = mp4ff_read_int32(f); + f->track[f->total_tracks - 1]->avgBitrate = mp4ff_read_int32(f); + + /* get and verify DecSpecificInfoTag */ + if (mp4ff_read_char(f) != 0x05) + { + return 1; + } + + /* read length */ + f->track[f->total_tracks - 1]->decoderConfigLen = mp4ff_read_mp4_descr_length(f); + + if (f->track[f->total_tracks - 1]->decoderConfig) + free(f->track[f->total_tracks - 1]->decoderConfig); + f->track[f->total_tracks - 1]->decoderConfig = malloc(f->track[f->total_tracks - 1]->decoderConfigLen); + if (f->track[f->total_tracks - 1]->decoderConfig) + { + mp4ff_read_data(f, f->track[f->total_tracks - 1]->decoderConfig, f->track[f->total_tracks - 1]->decoderConfigLen); + } else { + f->track[f->total_tracks - 1]->decoderConfigLen = 0; + } + + /* will skip the remainder of the atom */ + return 0; +} + +static int32_t mp4ff_read_mp4a(mp4ff_t *f) +{ + uint64_t size; + int32_t i; + uint8_t atom_type = 0; + uint8_t header_size = 0; + + for (i = 0; i < 6; i++) + { + mp4ff_read_char(f); /* reserved */ + } + /* data_reference_index */ mp4ff_read_int16(f); + + mp4ff_read_int32(f); /* reserved */ + mp4ff_read_int32(f); /* reserved */ + + f->track[f->total_tracks - 1]->channelCount = mp4ff_read_int16(f); + f->track[f->total_tracks - 1]->sampleSize = mp4ff_read_int16(f); + + mp4ff_read_int16(f); + mp4ff_read_int16(f); + + f->track[f->total_tracks - 1]->sampleRate = mp4ff_read_int16(f); + + mp4ff_read_int16(f); + + size = mp4ff_atom_read_header(f, &atom_type, &header_size); + if (atom_type == ATOM_ESDS) + { + mp4ff_read_esds(f); + } + + return 0; +} + +#ifdef ITUNES_DRM +char *GetHomeDir( void ) +{ + char *p_tmp, *p_homedir = NULL; + +#if defined(HAVE_GETPWUID) + struct passwd *p_pw = NULL; +#endif + +#if defined(_WIN32) || defined(UNDER_CE) + typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD, + LPSTR ); +# define CSIDL_FLAG_CREATE 0x8000 +# define CSIDL_APPDATA 0x1A +# define SHGFP_TYPE_CURRENT 0 + + HINSTANCE shfolder_dll; + SHGETFOLDERPATH SHGetFolderPath ; + /* load the shfolder dll to retrieve SHGetFolderPath */ + if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL ) + { + SHGetFolderPath = (void *)GetProcAddress( shfolder_dll, + _T("SHGetFolderPathA") ); + if ( SHGetFolderPath != NULL ) + { + p_homedir = (char *)malloc( MAX_PATH ); + if( !p_homedir ) + { + return NULL; + } + + /* get the "Application Data" folder for the current user */ + if( S_OK == SHGetFolderPath( NULL, + CSIDL_APPDATA | CSIDL_FLAG_CREATE, + NULL, SHGFP_TYPE_CURRENT, + p_homedir ) ) + { + FreeLibrary( shfolder_dll ); + return p_homedir; + } + free( p_homedir ); + } + FreeLibrary( shfolder_dll ); + } +#endif + +#if defined(HAVE_GETPWUID) + if( ( p_pw = getpwuid( getuid() ) ) == NULL ) +#endif + { + if( ( p_tmp = getenv( "HOME" ) ) == NULL ) + { + if( ( p_tmp = getenv( "TMP" ) ) == NULL ) + { + p_tmp = "/tmp"; + } + } + + p_homedir = strdup( p_tmp ); + } +#if defined(HAVE_GETPWUID) + else + { + p_homedir = strdup( p_pw->pw_dir ); + } +#endif + + return p_homedir; +} + +static int32_t mp4ff_read_drms(mp4ff_t *f, uint64_t skip) +{ + uint64_t size; + int32_t i; + uint8_t atom_type = 0; + uint8_t header_size = 0; + + f->track[f->total_tracks - 1]->p_drms = drms_alloc( GetHomeDir() ); + + for (i = 0; i < 6; i++) + { + mp4ff_read_char(f); /* reserved */ + } + /* data_reference_index */ mp4ff_read_int16(f); + + mp4ff_read_int32(f); /* reserved */ + mp4ff_read_int32(f); /* reserved */ + + f->track[f->total_tracks - 1]->channelCount = mp4ff_read_int16(f); + f->track[f->total_tracks - 1]->sampleSize = mp4ff_read_int16(f); + + mp4ff_read_int16(f); + mp4ff_read_int16(f); + + f->track[f->total_tracks - 1]->sampleRate = mp4ff_read_int16(f); + + mp4ff_read_int16(f); + + size = mp4ff_atom_read_header(f, &atom_type, &header_size); + if (atom_type == ATOM_ESDS) + { + mp4ff_read_esds(f); + } + mp4ff_set_position(f, skip+size+28); + + size = mp4ff_atom_read_header(f, &atom_type, &header_size); + if (atom_type == ATOM_SINF) + { + parse_sub_atoms(f, size-header_size,0); + } + + return 0; +} + +static int32_t mp4ff_read_frma(mp4ff_t *f) +{ + uint8_t atom_type; + int8_t type[4]; + + mp4ff_read_data(f, type, 4); + + atom_type = mp4ff_atom_name_to_type(type[0], type[1], type[2], type[3]); + + if (atom_type == ATOM_MP4A) + { + f->track[f->total_tracks - 1]->type = TRACK_AUDIO; + } else if (atom_type == ATOM_MP4V) { + f->track[f->total_tracks - 1]->type = TRACK_VIDEO; + } else if (atom_type == ATOM_MP4S) { + f->track[f->total_tracks - 1]->type = TRACK_SYSTEM; + } else { + f->track[f->total_tracks - 1]->type = TRACK_UNKNOWN; + } + + return 0; +} + +static int32_t mp4ff_read_name(mp4ff_t *f, uint64_t size) +{ + uint8_t *data = malloc(size); + mp4ff_read_data(f, data, size); + + if (f->track[f->total_tracks - 1]->p_drms != NULL) + { + drms_init(f->track[f->total_tracks - 1]->p_drms, + FOURCC_name, data, strlen(data) ); + } + + if (data) + free(data); + + return 0; +} + +static int32_t mp4ff_read_priv(mp4ff_t *f, uint64_t size) +{ + uint8_t *data = malloc(size); + mp4ff_read_data(f, data, size); + + if (f->track[f->total_tracks - 1]->p_drms != 0) + { + drms_init(f->track[f->total_tracks - 1]->p_drms, + FOURCC_priv, data, size ); + } + + if (data) + free(data); + + return 0; +} + +static int32_t mp4ff_read_iviv(mp4ff_t *f, uint64_t size) +{ + uint8_t *data = malloc(size); + mp4ff_read_data(f, data, size); + + if (f->track[f->total_tracks - 1]->p_drms != 0) + { + drms_init(f->track[f->total_tracks - 1]->p_drms, + FOURCC_iviv, data, sizeof(uint32_t) * 4 ); + } + + if (data) + free(data); + + return 0; +} + +static int32_t mp4ff_read_user(mp4ff_t *f, uint64_t size) +{ + uint8_t *data = malloc(size); + mp4ff_read_data(f, data, size); + + if (f->track[f->total_tracks - 1]->p_drms != 0) + { + drms_init(f->track[f->total_tracks - 1]->p_drms, + FOURCC_user, data, size ); + } + + if (data) + free(data); + + return 0; +} + +static int32_t mp4ff_read_key(mp4ff_t *f, uint64_t size) +{ + uint8_t *data = malloc(size); + mp4ff_read_data(f, data, size); + + if (f->track[f->total_tracks - 1]->p_drms != 0) + { + drms_init(f->track[f->total_tracks - 1]->p_drms, + FOURCC_key, data, size ); + } + + if (data) + free(data); + + return 0; +} +#endif + +static int32_t mp4ff_read_stsd(mp4ff_t *f) +{ + int32_t i; + uint8_t header_size = 0; + + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + + f->track[f->total_tracks - 1]->stsd_entry_count = mp4ff_read_int32(f); + + for (i = 0; i < f->track[f->total_tracks - 1]->stsd_entry_count; i++) + { + uint64_t skip = mp4ff_position(f); + uint64_t size; + uint8_t atom_type = 0; + size = mp4ff_atom_read_header(f, &atom_type, &header_size); + skip += size; + + if (atom_type == ATOM_MP4A) + { + f->track[f->total_tracks - 1]->type = TRACK_AUDIO; + mp4ff_read_mp4a(f); + } else if (atom_type == ATOM_MP4V) { + f->track[f->total_tracks - 1]->type = TRACK_VIDEO; + } else if (atom_type == ATOM_MP4S) { + f->track[f->total_tracks - 1]->type = TRACK_SYSTEM; +#ifdef ITUNES_DRM + } else if (atom_type == ATOM_DRMS) { + // track type is read from the "frma" atom + f->track[f->total_tracks - 1]->type = TRACK_UNKNOWN; + mp4ff_read_drms(f, skip-size+header_size); +#endif + } else { + f->track[f->total_tracks - 1]->type = TRACK_UNKNOWN; + } + + mp4ff_set_position(f, skip); + } + + return 0; +} + +static int32_t mp4ff_read_stsc(mp4ff_t *f) +{ + int32_t i; + + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + f->track[f->total_tracks - 1]->stsc_entry_count = mp4ff_read_int32(f); + + f->track[f->total_tracks - 1]->stsc_first_chunk = + (int32_t*)malloc(f->track[f->total_tracks - 1]->stsc_entry_count*sizeof(int32_t)); + f->track[f->total_tracks - 1]->stsc_samples_per_chunk = + (int32_t*)malloc(f->track[f->total_tracks - 1]->stsc_entry_count*sizeof(int32_t)); + f->track[f->total_tracks - 1]->stsc_sample_desc_index = + (int32_t*)malloc(f->track[f->total_tracks - 1]->stsc_entry_count*sizeof(int32_t)); + + for (i = 0; i < f->track[f->total_tracks - 1]->stsc_entry_count; i++) + { + f->track[f->total_tracks - 1]->stsc_first_chunk[i] = mp4ff_read_int32(f); + f->track[f->total_tracks - 1]->stsc_samples_per_chunk[i] = mp4ff_read_int32(f); + f->track[f->total_tracks - 1]->stsc_sample_desc_index[i] = mp4ff_read_int32(f); + } + + return 0; +} + +static int32_t mp4ff_read_stco(mp4ff_t *f) +{ + int32_t i; + + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + f->track[f->total_tracks - 1]->stco_entry_count = mp4ff_read_int32(f); + + f->track[f->total_tracks - 1]->stco_chunk_offset = + (int32_t*)malloc(f->track[f->total_tracks - 1]->stco_entry_count*sizeof(int32_t)); + + for (i = 0; i < f->track[f->total_tracks - 1]->stco_entry_count; i++) + { + f->track[f->total_tracks - 1]->stco_chunk_offset[i] = mp4ff_read_int32(f); + } + + return 0; +} + +static int32_t mp4ff_read_ctts(mp4ff_t *f) +{ + int32_t i; + mp4ff_track_t * p_track = f->track[f->total_tracks - 1]; + + if (p_track->ctts_entry_count) return 0; + + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + p_track->ctts_entry_count = mp4ff_read_int32(f); + + p_track->ctts_sample_count = (int32_t*)malloc(p_track->ctts_entry_count * sizeof(int32_t)); + p_track->ctts_sample_offset = (int32_t*)malloc(p_track->ctts_entry_count * sizeof(int32_t)); + + if (p_track->ctts_sample_count == 0 || p_track->ctts_sample_offset == 0) + { + if (p_track->ctts_sample_count) {free(p_track->ctts_sample_count);p_track->ctts_sample_count=0;} + if (p_track->ctts_sample_offset) {free(p_track->ctts_sample_offset);p_track->ctts_sample_offset=0;} + p_track->ctts_entry_count = 0; + return 0; + } + else + { + for (i = 0; i < f->track[f->total_tracks - 1]->ctts_entry_count; i++) + { + p_track->ctts_sample_count[i] = mp4ff_read_int32(f); + p_track->ctts_sample_offset[i] = mp4ff_read_int32(f); + } + return 1; + } +} + +static int32_t mp4ff_read_stts(mp4ff_t *f) +{ + int32_t i; + mp4ff_track_t * p_track = f->track[f->total_tracks - 1]; + + if (p_track->stts_entry_count) return 0; + + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + p_track->stts_entry_count = mp4ff_read_int32(f); + + p_track->stts_sample_count = (int32_t*)malloc(p_track->stts_entry_count * sizeof(int32_t)); + p_track->stts_sample_delta = (int32_t*)malloc(p_track->stts_entry_count * sizeof(int32_t)); + + if (p_track->stts_sample_count == 0 || p_track->stts_sample_delta == 0) + { + if (p_track->stts_sample_count) {free(p_track->stts_sample_count);p_track->stts_sample_count=0;} + if (p_track->stts_sample_delta) {free(p_track->stts_sample_delta);p_track->stts_sample_delta=0;} + p_track->stts_entry_count = 0; + return 0; + } + else + { + for (i = 0; i < f->track[f->total_tracks - 1]->stts_entry_count; i++) + { + p_track->stts_sample_count[i] = mp4ff_read_int32(f); + p_track->stts_sample_delta[i] = mp4ff_read_int32(f); + } + return 1; + } +} + +static int32_t mp4ff_read_mvhd(mp4ff_t *f) +{ + int32_t i; + + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + /* creation_time */ mp4ff_read_int32(f); + /* modification_time */ mp4ff_read_int32(f); + f->time_scale = mp4ff_read_int32(f); + f->duration = mp4ff_read_int32(f); + /* preferred_rate */ mp4ff_read_int32(f); /*mp4ff_read_fixed32(f);*/ + /* preferred_volume */ mp4ff_read_int16(f); /*mp4ff_read_fixed16(f);*/ + for (i = 0; i < 10; i++) + { + /* reserved */ mp4ff_read_char(f); + } + for (i = 0; i < 9; i++) + { + mp4ff_read_int32(f); /* matrix */ + } + /* preview_time */ mp4ff_read_int32(f); + /* preview_duration */ mp4ff_read_int32(f); + /* poster_time */ mp4ff_read_int32(f); + /* selection_time */ mp4ff_read_int32(f); + /* selection_duration */ mp4ff_read_int32(f); + /* current_time */ mp4ff_read_int32(f); + /* next_track_id */ mp4ff_read_int32(f); + + return 0; +} + +#if 0 +static int32_t mp4ff_read_tkhd(mp4ff_t *f) +{ + uint8_t version; + uint32_t flags; + version = mp4ff_read_char(f); /* version */ + flags = mp4ff_read_int24(f); /* flags */ + if (version==1) + { + mp4ff_read_int64(f);//creation-time + mp4ff_read_int64(f);//modification-time + mp4ff_read_int32(f);//track-id + mp4ff_read_int32(f);//reserved + f->track[f->total_tracks - 1]->duration = mp4ff_read_int64(f);//duration + } + else //version == 0 + { + mp4ff_read_int32(f);//creation-time + mp4ff_read_int32(f);//modification-time + mp4ff_read_int32(f);//track-id + mp4ff_read_int32(f);//reserved + f->track[f->total_tracks - 1]->duration = mp4ff_read_int32(f);//duration + if (f->track[f->total_tracks - 1]->duration == 0xFFFFFFFF) + f->track[f->total_tracks - 1]->duration = 0xFFFFFFFFFFFFFFFF; + + } + mp4ff_read_int32(f);//reserved + mp4ff_read_int32(f);//reserved + mp4ff_read_int16(f);//layer + mp4ff_read_int16(f);//pre-defined + mp4ff_read_int16(f);//volume + mp4ff_read_int16(f);//reserved + + //matrix + mp4ff_read_int32(f); mp4ff_read_int32(f); mp4ff_read_int32(f); + mp4ff_read_int32(f); mp4ff_read_int32(f); mp4ff_read_int32(f); + mp4ff_read_int32(f); mp4ff_read_int32(f); mp4ff_read_int32(f); + mp4ff_read_int32(f);//width + mp4ff_read_int32(f);//height + return 1; +} +#endif + +static int32_t mp4ff_read_mdhd(mp4ff_t *f) +{ + uint32_t version; + + version = mp4ff_read_int32(f); + if (version==1) + { + mp4ff_read_int64(f);//creation-time + mp4ff_read_int64(f);//modification-time + f->track[f->total_tracks - 1]->timeScale = mp4ff_read_int32(f);//timescale + f->track[f->total_tracks - 1]->duration = mp4ff_read_int64(f);//duration + } + else //version == 0 + { + uint32_t temp; + + mp4ff_read_int32(f);//creation-time + mp4ff_read_int32(f);//modification-time + f->track[f->total_tracks - 1]->timeScale = mp4ff_read_int32(f);//timescale + temp = mp4ff_read_int32(f); + f->track[f->total_tracks - 1]->duration = (temp == (uint32_t)(-1)) ? (uint64_t)(-1) : (uint64_t)(temp); + } + mp4ff_read_int16(f); + mp4ff_read_int16(f); + return 1; +} +#ifdef USE_TAGGING +static int32_t mp4ff_read_meta(mp4ff_t *f, const uint64_t size) +{ + uint64_t subsize, sumsize = 0; + uint8_t atom_type; + uint8_t header_size = 0; + + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + + while (sumsize < (size-(header_size+4))) + { + subsize = mp4ff_atom_read_header(f, &atom_type, &header_size); + if (subsize <= header_size+4) + return 1; + if (atom_type == ATOM_ILST) + { + mp4ff_parse_metadata(f, (uint32_t)(subsize-(header_size+4))); + } else { + mp4ff_set_position(f, mp4ff_position(f)+subsize-header_size); + } + sumsize += subsize; + } + + return 0; +} +#endif + +int32_t mp4ff_atom_read(mp4ff_t *f, const int32_t size, const uint8_t atom_type) +{ + uint64_t dest_position = mp4ff_position(f)+size-8; + if (atom_type == ATOM_STSZ) + { + /* sample size box */ + mp4ff_read_stsz(f); + } else if (atom_type == ATOM_STTS) { + /* time to sample box */ + mp4ff_read_stts(f); + } else if (atom_type == ATOM_CTTS) { + /* composition offset box */ + mp4ff_read_ctts(f); + } else if (atom_type == ATOM_STSC) { + /* sample to chunk box */ + mp4ff_read_stsc(f); + } else if (atom_type == ATOM_STCO) { + /* chunk offset box */ + mp4ff_read_stco(f); + } else if (atom_type == ATOM_STSD) { + /* sample description box */ + mp4ff_read_stsd(f); + } else if (atom_type == ATOM_MVHD) { + /* movie header box */ + mp4ff_read_mvhd(f); + } else if (atom_type == ATOM_MDHD) { + /* track header */ + mp4ff_read_mdhd(f); +#ifdef ITUNES_DRM + } else if (atom_type == ATOM_FRMA) { + /* DRM track format */ + mp4ff_read_frma(f); + } else if (atom_type == ATOM_IVIV) { + mp4ff_read_iviv(f, size-8); + } else if (atom_type == ATOM_NAME) { + mp4ff_read_name(f, size-8); + } else if (atom_type == ATOM_PRIV) { + mp4ff_read_priv(f, size-8); + } else if (atom_type == ATOM_USER) { + mp4ff_read_user(f, size-8); + } else if (atom_type == ATOM_KEY) { + mp4ff_read_key(f, size-8); +#endif +#ifdef USE_TAGGING + } else if (atom_type == ATOM_META) { + /* iTunes Metadata box */ + mp4ff_read_meta(f, size); +#endif + } + + mp4ff_set_position(f, dest_position); + + + return 0; +} diff --git a/Libraries/FAAD2/Files/common/mp4ff/mp4ff.c b/Libraries/FAAD2/Files/common/mp4ff/mp4ff.c new file mode 100644 index 000000000..118fe95be --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/mp4ff.c @@ -0,0 +1,474 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2004 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#include +#include +#include "mp4ffint.h" + +#include "drms.h" + +mp4ff_t *mp4ff_open_read(mp4ff_callback_t *f) +{ + mp4ff_t *ff = malloc(sizeof(mp4ff_t)); + + memset(ff, 0, sizeof(mp4ff_t)); + + ff->stream = f; + + parse_atoms(ff,0); + + return ff; +} + +mp4ff_t *mp4ff_open_read_metaonly(mp4ff_callback_t *f) +{ + mp4ff_t *ff = malloc(sizeof(mp4ff_t)); + + memset(ff, 0, sizeof(mp4ff_t)); + + ff->stream = f; + + parse_atoms(ff,1); + + return ff; +} + +void mp4ff_close(mp4ff_t *ff) +{ + int32_t i; + + for (i = 0; i < ff->total_tracks; i++) + { + if (ff->track[i]) + { + if (ff->track[i]->stsz_table) + free(ff->track[i]->stsz_table); + if (ff->track[i]->stts_sample_count) + free(ff->track[i]->stts_sample_count); + if (ff->track[i]->stts_sample_delta) + free(ff->track[i]->stts_sample_delta); + if (ff->track[i]->stsc_first_chunk) + free(ff->track[i]->stsc_first_chunk); + if (ff->track[i]->stsc_samples_per_chunk) + free(ff->track[i]->stsc_samples_per_chunk); + if (ff->track[i]->stsc_sample_desc_index) + free(ff->track[i]->stsc_sample_desc_index); + if (ff->track[i]->stco_chunk_offset) + free(ff->track[i]->stco_chunk_offset); + if (ff->track[i]->decoderConfig) + free(ff->track[i]->decoderConfig); + if (ff->track[i]->ctts_sample_count) + free(ff->track[i]->ctts_sample_count); + if (ff->track[i]->ctts_sample_offset) + free(ff->track[i]->ctts_sample_offset); +#ifdef ITUNES_DRM + if (ff->track[i]->p_drms) + drms_free(ff->track[i]->p_drms); +#endif + free(ff->track[i]); + } + } + +#ifdef USE_TAGGING + mp4ff_tag_delete(&(ff->tags)); +#endif + + if (ff) free(ff); +} + +static void mp4ff_track_add(mp4ff_t *f) +{ + f->total_tracks++; + + f->track[f->total_tracks - 1] = malloc(sizeof(mp4ff_track_t)); + + memset(f->track[f->total_tracks - 1], 0, sizeof(mp4ff_track_t)); +} + +static int need_parse_when_meta_only(uint8_t atom_type) +{ + switch(atom_type) + { + case ATOM_EDTS: +// case ATOM_MDIA: +// case ATOM_MINF: + case ATOM_DRMS: + case ATOM_SINF: + case ATOM_SCHI: +// case ATOM_STBL: +// case ATOM_STSD: + case ATOM_STTS: + case ATOM_STSZ: + case ATOM_STZ2: + case ATOM_STCO: + case ATOM_STSC: +// case ATOM_CTTS: + case ATOM_FRMA: + case ATOM_IVIV: + case ATOM_PRIV: + return 0; + default: + return 1; + } +} + +/* parse atoms that are sub atoms of other atoms */ +int32_t parse_sub_atoms(mp4ff_t *f, const uint64_t total_size,int meta_only) +{ + uint64_t size; + uint8_t atom_type = 0; + uint64_t counted_size = 0; + uint8_t header_size = 0; + + while (counted_size < total_size) + { + size = mp4ff_atom_read_header(f, &atom_type, &header_size); + counted_size += size; + + /* check for end of file */ + if (size == 0) + break; + + /* we're starting to read a new track, update index, + * so that all data and tables get written in the right place + */ + if (atom_type == ATOM_TRAK) + { + mp4ff_track_add(f); + } + + /* parse subatoms */ + if (meta_only && !need_parse_when_meta_only(atom_type)) + { + mp4ff_set_position(f, mp4ff_position(f)+size-header_size); + } else if (atom_type < SUBATOMIC) + { + parse_sub_atoms(f, size-header_size,meta_only); + } else { + mp4ff_atom_read(f, (uint32_t)size, atom_type); + } + } + + return 0; +} + +/* parse root atoms */ +int32_t parse_atoms(mp4ff_t *f,int meta_only) +{ + uint64_t size; + uint8_t atom_type = 0; + uint8_t header_size = 0; + + f->file_size = 0; + + while ((size = mp4ff_atom_read_header(f, &atom_type, &header_size)) != 0) + { + f->file_size += size; + f->last_atom = atom_type; + + if (atom_type == ATOM_MDAT && f->moov_read) + { + /* moov atom is before mdat, we can stop reading when mdat is encountered */ + /* file position will stay at beginning of mdat data */ +// break; + } + + if (atom_type == ATOM_MOOV && size > header_size) + { + f->moov_read = 1; + f->moov_offset = mp4ff_position(f)-header_size; + f->moov_size = size; + } + + /* parse subatoms */ + if (meta_only && !need_parse_when_meta_only(atom_type)) + { + mp4ff_set_position(f, mp4ff_position(f)+size-header_size); + } else if (atom_type < SUBATOMIC) + { + parse_sub_atoms(f, size-header_size,meta_only); + } else { + /* skip this atom */ + mp4ff_set_position(f, mp4ff_position(f)+size-header_size); + } + } + + return 0; +} + +int32_t mp4ff_get_decoder_config(const mp4ff_t *f, const int32_t track, + uint8_t** ppBuf, uint32_t* pBufSize) +{ + if (track >= f->total_tracks) + { + *ppBuf = NULL; + *pBufSize = 0; + return 1; + } + + if (f->track[track]->decoderConfig == NULL || f->track[track]->decoderConfigLen == 0) + { + *ppBuf = NULL; + *pBufSize = 0; + } else { + *ppBuf = malloc(f->track[track]->decoderConfigLen); + if (*ppBuf == NULL) + { + *pBufSize = 0; + return 1; + } + memcpy(*ppBuf, f->track[track]->decoderConfig, f->track[track]->decoderConfigLen); + *pBufSize = f->track[track]->decoderConfigLen; + } + + return 0; +} + +int32_t mp4ff_get_track_type(const mp4ff_t *f, const int track) +{ + return f->track[track]->type; +} + +int32_t mp4ff_total_tracks(const mp4ff_t *f) +{ + return f->total_tracks; +} + +int32_t mp4ff_time_scale(const mp4ff_t *f, const int32_t track) +{ + return f->track[track]->timeScale; +} + +uint32_t mp4ff_get_avg_bitrate(const mp4ff_t *f, const int32_t track) +{ + return f->track[track]->avgBitrate; +} + +uint32_t mp4ff_get_max_bitrate(const mp4ff_t *f, const int32_t track) +{ + return f->track[track]->maxBitrate; +} + +int64_t mp4ff_get_track_duration(const mp4ff_t *f, const int32_t track) +{ + return f->track[track]->duration; +} + +int64_t mp4ff_get_track_duration_use_offsets(const mp4ff_t *f, const int32_t track) +{ + int64_t duration = mp4ff_get_track_duration(f,track); + if (duration!=-1) + { + int64_t offset = mp4ff_get_sample_offset(f,track,0); + if (offset > duration) duration = 0; + else duration -= offset; + } + return duration; +} + + +int32_t mp4ff_num_samples(const mp4ff_t *f, const int32_t track) +{ + int32_t i; + int32_t total = 0; + + for (i = 0; i < f->track[track]->stts_entry_count; i++) + { + total += f->track[track]->stts_sample_count[i]; + } + return total; +} + + + + +uint32_t mp4ff_get_sample_rate(const mp4ff_t *f, const int32_t track) +{ + return f->track[track]->sampleRate; +} + +uint32_t mp4ff_get_channel_count(const mp4ff_t * f,const int32_t track) +{ + return f->track[track]->channelCount; +} + +uint32_t mp4ff_get_audio_type(const mp4ff_t * f,const int32_t track) +{ + return f->track[track]->audioType; +} + +int32_t mp4ff_get_sample_duration_use_offsets(const mp4ff_t *f, const int32_t track, const int32_t sample) +{ + int32_t d,o; + d = mp4ff_get_sample_duration(f,track,sample); + if (d!=-1) + { + o = mp4ff_get_sample_offset(f,track,sample); + if (o>d) d = 0; + else d -= o; + } + return d; +} + +int32_t mp4ff_get_sample_duration(const mp4ff_t *f, const int32_t track, const int32_t sample) +{ + int32_t i, co = 0; + + for (i = 0; i < f->track[track]->stts_entry_count; i++) + { + int32_t delta = f->track[track]->stts_sample_count[i]; + if (sample < co + delta) + return f->track[track]->stts_sample_delta[i]; + co += delta; + } + return (int32_t)(-1); +} + +int64_t mp4ff_get_sample_position(const mp4ff_t *f, const int32_t track, const int32_t sample) +{ + int32_t i, co = 0; + int64_t acc = 0; + + for (i = 0; i < f->track[track]->stts_entry_count; i++) + { + int32_t delta = f->track[track]->stts_sample_count[i]; + if (sample < co + delta) + { + acc += f->track[track]->stts_sample_delta[i] * (sample - co); + return acc; + } + else + { + acc += f->track[track]->stts_sample_delta[i] * delta; + } + co += delta; + } + return (int64_t)(-1); +} + +int32_t mp4ff_get_sample_offset(const mp4ff_t *f, const int32_t track, const int32_t sample) +{ + int32_t i, co = 0; + + for (i = 0; i < f->track[track]->ctts_entry_count; i++) + { + int32_t delta = f->track[track]->ctts_sample_count[i]; + if (sample < co + delta) + return f->track[track]->ctts_sample_offset[i]; + co += delta; + } + return 0; +} + +int32_t mp4ff_find_sample(const mp4ff_t *f, const int32_t track, const int64_t offset,int32_t * toskip) +{ + int32_t i, co = 0; + int64_t offset_total = 0; + mp4ff_track_t * p_track = f->track[track]; + + for (i = 0; i < p_track->stts_entry_count; i++) + { + int32_t sample_count = p_track->stts_sample_count[i]; + int32_t sample_delta = p_track->stts_sample_delta[i]; + int64_t offset_delta = (int64_t)sample_delta * (int64_t)sample_count; + if (offset < offset_total + offset_delta) + { + int64_t offset_fromstts = offset - offset_total; + if (toskip) *toskip = (int32_t)(offset_fromstts % sample_delta); + return co + (int32_t)(offset_fromstts / sample_delta); + } + else + { + offset_total += offset_delta; + } + co += sample_count; + } + return (int32_t)(-1); +} + +int32_t mp4ff_find_sample_use_offsets(const mp4ff_t *f, const int32_t track, const int64_t offset,int32_t * toskip) +{ + return mp4ff_find_sample(f,track,offset + mp4ff_get_sample_offset(f,track,0),toskip); +} + +int32_t mp4ff_read_sample(mp4ff_t *f, const int32_t track, const int32_t sample, + uint8_t **audio_buffer, uint32_t *bytes) +{ + int32_t result = 0; + + *bytes = mp4ff_audio_frame_size(f, track, sample); + + if (*bytes==0) return 0; + + *audio_buffer = (uint8_t*)malloc(*bytes); + + mp4ff_set_sample_position(f, track, sample); + + result = mp4ff_read_data(f, *audio_buffer, *bytes); + + if (!result) + { + free(*audio_buffer); + *audio_buffer = 0; + return 0; + } + +#ifdef ITUNES_DRM + if (f->track[track]->p_drms != NULL) + { + drms_decrypt(f->track[track]->p_drms, (uint32_t*)*audio_buffer, *bytes); + } +#endif + + return *bytes; +} + + +int32_t mp4ff_read_sample_v2(mp4ff_t *f, const int track, const int sample,unsigned char *buffer) +{ + int32_t result = 0; + int32_t size = mp4ff_audio_frame_size(f,track,sample); + if (size<=0) return 0; + mp4ff_set_sample_position(f, track, sample); + result = mp4ff_read_data(f,buffer,size); + +#ifdef ITUNES_DRM + if (f->track[track]->p_drms != NULL) + { + drms_decrypt(f->track[track]->p_drms, (uint32_t*)buffer, size); + } +#endif + + return result; +} + +int32_t mp4ff_read_sample_getsize(mp4ff_t *f, const int track, const int sample) +{ + int32_t temp = mp4ff_audio_frame_size(f, track, sample); + if (temp<0) temp = 0; + return temp; +} diff --git a/Libraries/FAAD2/Files/common/mp4ff/mp4ff.h b/Libraries/FAAD2/Files/common/mp4ff/mp4ff.h new file mode 100644 index 000000000..c8d754d88 --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/mp4ff.h @@ -0,0 +1,131 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2004 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#ifndef MP4FF_H +#define MP4FF_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "mp4ff_int_types.h" + +/* file callback structure */ +typedef struct +{ + uint32_t (*read)(void *user_data, void *buffer, uint32_t length); + uint32_t (*write)(void *udata, void *buffer, uint32_t length); + uint32_t (*seek)(void *user_data, uint64_t position); + uint32_t (*truncate)(void *user_data); + void *user_data; +} mp4ff_callback_t; + +/* mp4 main file structure */ +typedef void* mp4ff_t; + + +/* API */ + +mp4ff_t *mp4ff_open_read(mp4ff_callback_t *f); +mp4ff_t *mp4ff_open_read_metaonly(mp4ff_callback_t *f); +void mp4ff_close(mp4ff_t *f); +int32_t mp4ff_get_sample_duration(const mp4ff_t *f, const int32_t track, const int32_t sample); +int32_t mp4ff_get_sample_duration_use_offsets(const mp4ff_t *f, const int32_t track, const int32_t sample); +int64_t mp4ff_get_sample_position(const mp4ff_t *f, const int32_t track, const int32_t sample); +int32_t mp4ff_get_sample_offset(const mp4ff_t *f, const int32_t track, const int32_t sample); +int32_t mp4ff_find_sample(const mp4ff_t *f, const int32_t track, const int64_t offset,int32_t * toskip); +int32_t mp4ff_find_sample_use_offsets(const mp4ff_t *f, const int32_t track, const int64_t offset,int32_t * toskip); + +int32_t mp4ff_read_sample(mp4ff_t *f, const int track, const int sample, + unsigned char **audio_buffer, unsigned int *bytes); + +int32_t mp4ff_read_sample_v2(mp4ff_t *f, const int track, const int sample,unsigned char *buffer);//returns 0 on error, number of bytes read on success, use mp4ff_read_sample_getsize() to check buffer size needed +int32_t mp4ff_read_sample_getsize(mp4ff_t *f, const int track, const int sample);//returns 0 on error, buffer size needed for mp4ff_read_sample_v2() on success + + + +int32_t mp4ff_get_decoder_config(const mp4ff_t *f, const int track, + unsigned char** ppBuf, unsigned int* pBufSize); +int32_t mp4ff_get_track_type(const mp4ff_t *f, const int track); +int32_t mp4ff_total_tracks(const mp4ff_t *f); +int32_t mp4ff_num_samples(const mp4ff_t *f, const int track); +int32_t mp4ff_time_scale(const mp4ff_t *f, const int track); + +uint32_t mp4ff_get_avg_bitrate(const mp4ff_t *f, const int32_t track); +uint32_t mp4ff_get_max_bitrate(const mp4ff_t *f, const int32_t track); +int64_t mp4ff_get_track_duration(const mp4ff_t *f, const int32_t track); //returns (-1) if unknown +int64_t mp4ff_get_track_duration_use_offsets(const mp4ff_t *f, const int32_t track); //returns (-1) if unknown +uint32_t mp4ff_get_sample_rate(const mp4ff_t *f, const int32_t track); +uint32_t mp4ff_get_channel_count(const mp4ff_t * f,const int32_t track); +uint32_t mp4ff_get_audio_type(const mp4ff_t * f,const int32_t track); + + +/* metadata */ +int mp4ff_meta_get_num_items(const mp4ff_t *f); +int mp4ff_meta_get_by_index(const mp4ff_t *f, unsigned int index, + char **item, char **value); +int mp4ff_meta_get_title(const mp4ff_t *f, char **value); +int mp4ff_meta_get_artist(const mp4ff_t *f, char **value); +int mp4ff_meta_get_writer(const mp4ff_t *f, char **value); +int mp4ff_meta_get_album(const mp4ff_t *f, char **value); +int mp4ff_meta_get_date(const mp4ff_t *f, char **value); +int mp4ff_meta_get_tool(const mp4ff_t *f, char **value); +int mp4ff_meta_get_comment(const mp4ff_t *f, char **value); +int mp4ff_meta_get_genre(const mp4ff_t *f, char **value); +int mp4ff_meta_get_track(const mp4ff_t *f, char **value); +int mp4ff_meta_get_disc(const mp4ff_t *f, char **value); +int mp4ff_meta_get_totaltracks(const mp4ff_t *f, char **value); +int mp4ff_meta_get_totaldiscs(const mp4ff_t *f, char **value); +int mp4ff_meta_get_compilation(const mp4ff_t *f, char **value); +int mp4ff_meta_get_tempo(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_coverart(const mp4ff_t *f, char **value); +#ifdef USE_TAGGING + +/* metadata tag structure */ +typedef struct +{ + char *item; + char *value; +} mp4ff_tag_t; + +/* metadata list structure */ +typedef struct +{ + mp4ff_tag_t *tags; + uint32_t count; +} mp4ff_metadata_t; + +int32_t mp4ff_meta_update(mp4ff_callback_t *f,const mp4ff_metadata_t * data); + +#endif + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/Libraries/FAAD2/Files/common/mp4ff/mp4ff_int_types.h b/Libraries/FAAD2/Files/common/mp4ff/mp4ff_int_types.h new file mode 100644 index 000000000..0ed4790d8 --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/mp4ff_int_types.h @@ -0,0 +1,23 @@ +#ifndef _MP4FF_INT_TYPES_H_ +#define _MP4FF_INT_TYPES_H_ + +#ifdef _WIN32 + +typedef char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef long int32_t; +typedef unsigned long uint32_t; + +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#else + +#include + +#endif + + +#endif \ No newline at end of file diff --git a/Libraries/FAAD2/Files/common/mp4ff/mp4ffint.h b/Libraries/FAAD2/Files/common/mp4ff/mp4ffint.h new file mode 100644 index 000000000..51c52c45c --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/mp4ffint.h @@ -0,0 +1,373 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2004 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#ifndef MP4FF_INTERNAL_H +#define MP4FF_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "mp4ff_int_types.h" + + +#if defined(_WIN32) && !defined(_WIN32_WCE) +#define ITUNES_DRM + +static __inline uint32_t GetDWLE( void const * _p ) +{ + uint8_t * p = (uint8_t *)_p; + return ( ((uint32_t)p[3] << 24) | ((uint32_t)p[2] << 16) + | ((uint32_t)p[1] << 8) | p[0] ); +} +static __inline uint32_t U32_AT( void const * _p ) +{ + uint8_t * p = (uint8_t *)_p; + return ( ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) + | ((uint32_t)p[2] << 8) | p[3] ); +} +static __inline uint64_t U64_AT( void const * _p ) +{ + uint8_t * p = (uint8_t *)_p; + return ( ((uint64_t)p[0] << 56) | ((uint64_t)p[1] << 48) + | ((uint64_t)p[2] << 40) | ((uint64_t)p[3] << 32) + | ((uint64_t)p[4] << 24) | ((uint64_t)p[5] << 16) + | ((uint64_t)p[6] << 8) | p[7] ); +} + +#ifdef WORDS_BIGENDIAN +# define VLC_FOURCC( a, b, c, d ) \ + ( ((uint32_t)d) | ( ((uint32_t)c) << 8 ) \ + | ( ((uint32_t)b) << 16 ) | ( ((uint32_t)a) << 24 ) ) +# define VLC_TWOCC( a, b ) \ + ( (uint16_t)(b) | ( (uint16_t)(a) << 8 ) ) + +#else +# define VLC_FOURCC( a, b, c, d ) \ + ( ((uint32_t)a) | ( ((uint32_t)b) << 8 ) \ + | ( ((uint32_t)c) << 16 ) | ( ((uint32_t)d) << 24 ) ) +# define VLC_TWOCC( a, b ) \ + ( (uint16_t)(a) | ( (uint16_t)(b) << 8 ) ) +#endif + +#define FOURCC_user VLC_FOURCC( 'u', 's', 'e', 'r' ) +#define FOURCC_key VLC_FOURCC( 'k', 'e', 'y', ' ' ) +#define FOURCC_iviv VLC_FOURCC( 'i', 'v', 'i', 'v' ) +#define FOURCC_name VLC_FOURCC( 'n', 'a', 'm', 'e' ) +#define FOURCC_priv VLC_FOURCC( 'p', 'r', 'i', 'v' ) + +#endif + +#define MAX_TRACKS 1024 +#define TRACK_UNKNOWN 0 +#define TRACK_AUDIO 1 +#define TRACK_VIDEO 2 +#define TRACK_SYSTEM 3 + + +#define SUBATOMIC 128 + +/* atoms without subatoms */ +#define ATOM_FTYP 129 +#define ATOM_MDAT 130 +#define ATOM_MVHD 131 +#define ATOM_TKHD 132 +#define ATOM_TREF 133 +#define ATOM_MDHD 134 +#define ATOM_VMHD 135 +#define ATOM_SMHD 136 +#define ATOM_HMHD 137 +#define ATOM_STSD 138 +#define ATOM_STTS 139 +#define ATOM_STSZ 140 +#define ATOM_STZ2 141 +#define ATOM_STCO 142 +#define ATOM_STSC 143 +#define ATOM_MP4A 144 +#define ATOM_MP4V 145 +#define ATOM_MP4S 146 +#define ATOM_ESDS 147 +#define ATOM_META 148 /* iTunes Metadata box */ +#define ATOM_NAME 149 /* iTunes Metadata name box */ +#define ATOM_DATA 150 /* iTunes Metadata data box */ +#define ATOM_CTTS 151 +#define ATOM_FRMA 152 +#define ATOM_IVIV 153 +#define ATOM_PRIV 154 +#define ATOM_USER 155 +#define ATOM_KEY 156 + +#define ATOM_UNKNOWN 255 +#define ATOM_FREE ATOM_UNKNOWN +#define ATOM_SKIP ATOM_UNKNOWN + +/* atoms with subatoms */ +#define ATOM_MOOV 1 +#define ATOM_TRAK 2 +#define ATOM_EDTS 3 +#define ATOM_MDIA 4 +#define ATOM_MINF 5 +#define ATOM_STBL 6 +#define ATOM_UDTA 7 +#define ATOM_ILST 8 /* iTunes Metadata list */ +#define ATOM_TITLE 9 +#define ATOM_ARTIST 10 +#define ATOM_WRITER 11 +#define ATOM_ALBUM 12 +#define ATOM_DATE 13 +#define ATOM_TOOL 14 +#define ATOM_COMMENT 15 +#define ATOM_GENRE1 16 +#define ATOM_TRACK 17 +#define ATOM_DISC 18 +#define ATOM_COMPILATION 19 +#define ATOM_GENRE2 20 +#define ATOM_TEMPO 21 +#define ATOM_COVER 22 +#define ATOM_DRMS 23 +#define ATOM_SINF 24 +#define ATOM_SCHI 25 + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#if !(defined(_WIN32) || defined(_WIN32_WCE)) +#define stricmp strcasecmp +#endif + +/* file callback structure */ +typedef struct +{ + uint32_t (*read)(void *user_data, void *buffer, uint32_t length); + uint32_t (*write)(void *udata, void *buffer, uint32_t length); + uint32_t (*seek)(void *user_data, uint64_t position); + uint32_t (*truncate)(void *user_data); + void *user_data; +} mp4ff_callback_t; + + +/* metadata tag structure */ +typedef struct +{ + char *item; + char *value; +} mp4ff_tag_t; + +/* metadata list structure */ +typedef struct +{ + mp4ff_tag_t *tags; + uint32_t count; +} mp4ff_metadata_t; + + +typedef struct +{ + int32_t type; + int32_t channelCount; + int32_t sampleSize; + uint16_t sampleRate; + int32_t audioType; + + /* stsd */ + int32_t stsd_entry_count; + + /* stsz */ + int32_t stsz_sample_size; + int32_t stsz_sample_count; + int32_t *stsz_table; + + /* stts */ + int32_t stts_entry_count; + int32_t *stts_sample_count; + int32_t *stts_sample_delta; + + /* stsc */ + int32_t stsc_entry_count; + int32_t *stsc_first_chunk; + int32_t *stsc_samples_per_chunk; + int32_t *stsc_sample_desc_index; + + /* stsc */ + int32_t stco_entry_count; + int32_t *stco_chunk_offset; + + /* ctts */ + int32_t ctts_entry_count; + int32_t *ctts_sample_count; + int32_t *ctts_sample_offset; + + /* esde */ + uint8_t *decoderConfig; + int32_t decoderConfigLen; + + uint32_t maxBitrate; + uint32_t avgBitrate; + + uint32_t timeScale; + uint64_t duration; + +#ifdef ITUNES_DRM + /* drms */ + void *p_drms; +#endif + +} mp4ff_track_t; + +/* mp4 main file structure */ +typedef struct +{ + /* stream to read from */ + mp4ff_callback_t *stream; + int64_t current_position; + + int32_t moov_read; + uint64_t moov_offset; + uint64_t moov_size; + uint8_t last_atom; + uint64_t file_size; + + /* mvhd */ + int32_t time_scale; + int32_t duration; + + /* incremental track index while reading the file */ + int32_t total_tracks; + + /* track data */ + mp4ff_track_t *track[MAX_TRACKS]; + + /* metadata */ + mp4ff_metadata_t tags; +} mp4ff_t; + + + + +/* mp4util.c */ +int32_t mp4ff_read_data(mp4ff_t *f, int8_t *data, uint32_t size); +int32_t mp4ff_write_data(mp4ff_t *f, int8_t *data, uint32_t size); +uint64_t mp4ff_read_int64(mp4ff_t *f); +uint32_t mp4ff_read_int32(mp4ff_t *f); +uint32_t mp4ff_read_int24(mp4ff_t *f); +uint16_t mp4ff_read_int16(mp4ff_t *f); +uint8_t mp4ff_read_char(mp4ff_t *f); +int32_t mp4ff_write_int32(mp4ff_t *f,const uint32_t data); +uint32_t mp4ff_read_mp4_descr_length(mp4ff_t *f); +int64_t mp4ff_position(const mp4ff_t *f); +int32_t mp4ff_set_position(mp4ff_t *f, const int64_t position); +int32_t mp4ff_truncate(mp4ff_t * f); +char * mp4ff_read_string(mp4ff_t * f,uint32_t length); + +/* mp4atom.c */ +static int32_t mp4ff_atom_get_size(const int8_t *data); +static int32_t mp4ff_atom_compare(const int8_t a1, const int8_t b1, const int8_t c1, const int8_t d1, + const int8_t a2, const int8_t b2, const int8_t c2, const int8_t d2); +static uint8_t mp4ff_atom_name_to_type(const int8_t a, const int8_t b, const int8_t c, const int8_t d); +uint64_t mp4ff_atom_read_header(mp4ff_t *f, uint8_t *atom_type, uint8_t *header_size); +static int32_t mp4ff_read_stsz(mp4ff_t *f); +static int32_t mp4ff_read_esds(mp4ff_t *f); +static int32_t mp4ff_read_mp4a(mp4ff_t *f); +static int32_t mp4ff_read_stsd(mp4ff_t *f); +static int32_t mp4ff_read_stsc(mp4ff_t *f); +static int32_t mp4ff_read_stco(mp4ff_t *f); +static int32_t mp4ff_read_stts(mp4ff_t *f); +#ifdef USE_TAGGING +static int32_t mp4ff_read_meta(mp4ff_t *f, const uint64_t size); +#endif +int32_t mp4ff_atom_read(mp4ff_t *f, const int32_t size, const uint8_t atom_type); + +/* mp4sample.c */ +static int32_t mp4ff_chunk_of_sample(const mp4ff_t *f, const int32_t track, const int32_t sample, + int32_t *chunk_sample, int32_t *chunk); +static int32_t mp4ff_chunk_to_offset(const mp4ff_t *f, const int32_t track, const int32_t chunk); +static int32_t mp4ff_sample_range_size(const mp4ff_t *f, const int32_t track, + const int32_t chunk_sample, const int32_t sample); +static int32_t mp4ff_sample_to_offset(const mp4ff_t *f, const int32_t track, const int32_t sample); +int32_t mp4ff_audio_frame_size(const mp4ff_t *f, const int32_t track, const int32_t sample); +int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample); + +#ifdef USE_TAGGING +/* mp4meta.c */ +static int32_t mp4ff_tag_add_field(mp4ff_metadata_t *tags, const char *item, const char *value); +static int32_t mp4ff_tag_set_field(mp4ff_metadata_t *tags, const char *item, const char *value); +static int32_t mp4ff_set_metadata_name(mp4ff_t *f, const uint8_t atom_type, char **name); +static int32_t mp4ff_parse_tag(mp4ff_t *f, const uint8_t parent_atom_type, const int32_t size); +static int32_t mp4ff_meta_find_by_name(const mp4ff_t *f, const char *item, char **value); +int32_t mp4ff_parse_metadata(mp4ff_t *f, const int32_t size); +int32_t mp4ff_tag_delete(mp4ff_metadata_t *tags); +int32_t mp4ff_meta_get_num_items(const mp4ff_t *f); +int32_t mp4ff_meta_get_by_index(const mp4ff_t *f, uint32_t index, + char **item, char **value); +int32_t mp4ff_meta_get_title(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_artist(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_writer(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_album(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_date(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_tool(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_comment(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_genre(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_track(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_disc(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_compilation(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_tempo(const mp4ff_t *f, char **value); +int32_t mp4ff_meta_get_coverart(const mp4ff_t *f, char **value); +#endif + +/* mp4ff.c */ +mp4ff_t *mp4ff_open_read(mp4ff_callback_t *f); +#ifdef USE_TAGGING +mp4ff_t *mp4ff_open_edit(mp4ff_callback_t *f); +#endif +void mp4ff_close(mp4ff_t *ff); +void mp4ff_track_add(mp4ff_t *f); +int32_t parse_sub_atoms(mp4ff_t *f, const uint64_t total_size,int meta_only); +int32_t parse_atoms(mp4ff_t *f,int meta_only); + +int32_t mp4ff_get_sample_duration(const mp4ff_t *f, const int32_t track, const int32_t sample); +int64_t mp4ff_get_sample_position(const mp4ff_t *f, const int32_t track, const int32_t sample); +int32_t mp4ff_get_sample_offset(const mp4ff_t *f, const int32_t track, const int32_t sample); +int32_t mp4ff_find_sample(const mp4ff_t *f, const int32_t track, const int64_t offset,int32_t * toskip); + +int32_t mp4ff_read_sample(mp4ff_t *f, const int32_t track, const int32_t sample, + uint8_t **audio_buffer, uint32_t *bytes); +int32_t mp4ff_get_decoder_config(const mp4ff_t *f, const int32_t track, + uint8_t** ppBuf, uint32_t* pBufSize); +int32_t mp4ff_total_tracks(const mp4ff_t *f); +int32_t mp4ff_time_scale(const mp4ff_t *f, const int32_t track); +int32_t mp4ff_num_samples(const mp4ff_t *f, const int32_t track); + +uint32_t mp4ff_meta_genre_to_index(const char * genrestr);//returns 1-based index, 0 if not found +const char * mp4ff_meta_index_to_genre(uint32_t idx);//returns pointer to static string + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/Libraries/FAAD2/Files/common/mp4ff/mp4meta.c b/Libraries/FAAD2/Files/common/mp4ff/mp4meta.c new file mode 100644 index 000000000..432f46ed5 --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/mp4meta.c @@ -0,0 +1,426 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2004 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#ifdef USE_TAGGING + +#include +#include +#include +#include "mp4ffint.h" + + + +static int32_t mp4ff_tag_add_field(mp4ff_metadata_t *tags, const char *item, const char *value) +{ + void *backup = (void *)tags->tags; + + if (!item || (item && !*item) || !value) return 0; + + tags->tags = (mp4ff_tag_t*)realloc(tags->tags, (tags->count+1) * sizeof(mp4ff_tag_t)); + if (!tags->tags) + { + if (backup) free(backup); + return 0; + } else { + tags->tags[tags->count].item = strdup(item); + tags->tags[tags->count].value = strdup(value); + + if (!tags->tags[tags->count].item || !tags->tags[tags->count].value) + { + if (!tags->tags[tags->count].item) free (tags->tags[tags->count].item); + if (!tags->tags[tags->count].value) free (tags->tags[tags->count].value); + tags->tags[tags->count].item = NULL; + tags->tags[tags->count].value = NULL; + return 0; + } + + tags->count++; + return 1; + } +} + +static int32_t mp4ff_tag_set_field(mp4ff_metadata_t *tags, const char *item, const char *value) +{ + unsigned int i; + + if (!item || (item && !*item) || !value) return 0; + + for (i = 0; i < tags->count; i++) + { + if (!stricmp(tags->tags[i].item, item)) + { + free(tags->tags[i].value); + tags->tags[i].value = strdup(value); + return 1; + } + } + + return mp4ff_tag_add_field(tags, item, value); +} + +int32_t mp4ff_tag_delete(mp4ff_metadata_t *tags) +{ + uint32_t i; + + for (i = 0; i < tags->count; i++) + { + if (tags->tags[i].item) free(tags->tags[i].item); + if (tags->tags[i].value) free(tags->tags[i].value); + } + + if (tags->tags) free(tags->tags); + + tags->tags = NULL; + tags->count = 0; + + return 0; +} + +static const char* ID3v1GenreList[] = { + "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", + "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", + "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", + "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", + "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", + "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", + "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", + "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", + "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", + "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", + "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", + "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", + "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", + "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing", + "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", + "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", + "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", + "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", + "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", + "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", + "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", + "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror", + "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat", + "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C", + "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", + "SynthPop", +}; + +uint32_t mp4ff_meta_genre_to_index(const char * genrestr) +{ + unsigned n; + for(n=0;n0 && idx<=sizeof(ID3v1GenreList)/sizeof(ID3v1GenreList[0])) + { + return ID3v1GenreList[idx-1]; + } + else + { + return 0; + } +} + + +static int32_t TrackToString(char** str, const uint16_t track, const uint16_t totalTracks) +{ + char temp[32]; + sprintf(temp, "%.5u of %.5u", track, totalTracks); + *str = strdup(temp); + return 0; +} + +static int32_t mp4ff_set_metadata_name(mp4ff_t *f, const uint8_t atom_type, char **name) +{ + static char *tag_names[] = { + "unknown", "title", "artist", "writer", "album", + "date", "tool", "comment", "genre", "track", + "disc", "compilation", "genre", "tempo", "cover" + }; + uint8_t tag_idx = 0; + + switch (atom_type) + { + case ATOM_TITLE: tag_idx = 1; break; + case ATOM_ARTIST: tag_idx = 2; break; + case ATOM_WRITER: tag_idx = 3; break; + case ATOM_ALBUM: tag_idx = 4; break; + case ATOM_DATE: tag_idx = 5; break; + case ATOM_TOOL: tag_idx = 6; break; + case ATOM_COMMENT: tag_idx = 7; break; + case ATOM_GENRE1: tag_idx = 8; break; + case ATOM_TRACK: tag_idx = 9; break; + case ATOM_DISC: tag_idx = 10; break; + case ATOM_COMPILATION: tag_idx = 11; break; + case ATOM_GENRE2: tag_idx = 12; break; + case ATOM_TEMPO: tag_idx = 13; break; + case ATOM_COVER: tag_idx = 14; break; + default: tag_idx = 0; break; + } + + *name = strdup(tag_names[tag_idx]); + + return 0; +} + +static int32_t mp4ff_parse_tag(mp4ff_t *f, const uint8_t parent_atom_type, const int32_t size) +{ + uint8_t atom_type; + uint8_t header_size = 0; + uint64_t subsize, sumsize = 0; + char * name = NULL; + char * data = NULL; + uint32_t done = 0; + + + while (sumsize < size) + { + uint64_t destpos; + subsize = mp4ff_atom_read_header(f, &atom_type, &header_size); + destpos = mp4ff_position(f)+subsize-header_size; + if (!done) + { + if (atom_type == ATOM_DATA) + { + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + mp4ff_read_int32(f); /* reserved */ + + /* some need special attention */ + if (parent_atom_type == ATOM_GENRE2 || parent_atom_type == ATOM_TEMPO) + { + if (subsize - header_size >= 8 + 2) + { + uint16_t val = mp4ff_read_int16(f); + + if (parent_atom_type == ATOM_TEMPO) + { + char temp[16]; + sprintf(temp, "%.5u BPM", val); + mp4ff_tag_add_field(&(f->tags), "tempo", temp); + } + else + { + const char * temp = mp4ff_meta_index_to_genre(val); + if (temp) + { + mp4ff_tag_add_field(&(f->tags), "genre", temp); + } + } + done = 1; + } + } else if (parent_atom_type == ATOM_TRACK || parent_atom_type == ATOM_DISC) { + if (!done && subsize - header_size >= 8 + 8) + { + uint16_t index,total; + char temp[32]; + mp4ff_read_int16(f); + index = mp4ff_read_int16(f); + total = mp4ff_read_int16(f); + mp4ff_read_int16(f); + + sprintf(temp,"%d",index); + mp4ff_tag_add_field(&(f->tags), parent_atom_type == ATOM_TRACK ? "track" : "disc", temp); + if (total>0) + { + sprintf(temp,"%d",total); + mp4ff_tag_add_field(&(f->tags), parent_atom_type == ATOM_TRACK ? "totaltracks" : "totaldiscs", temp); + } + done = 1; + } + } else + { + if (data) {free(data);data = NULL;} + data = mp4ff_read_string(f,(uint32_t)(subsize-(header_size+8))); + } + } else if (atom_type == ATOM_NAME) { + if (!done) + { + mp4ff_read_char(f); /* version */ + mp4ff_read_int24(f); /* flags */ + if (name) free(name); + name = mp4ff_read_string(f,(uint32_t)(subsize-(header_size+4))); + } + } + mp4ff_set_position(f, destpos); + sumsize += subsize; + } + } + + if (data) + { + if (!done) + { + if (name == NULL) mp4ff_set_metadata_name(f, parent_atom_type, &name); + if (name) mp4ff_tag_add_field(&(f->tags), name, data); + } + + free(data); + } + if (name) free(name); + return 1; +} + +int32_t mp4ff_parse_metadata(mp4ff_t *f, const int32_t size) +{ + uint64_t subsize, sumsize = 0; + uint8_t atom_type; + uint8_t header_size = 0; + + while (sumsize < size) + { + subsize = mp4ff_atom_read_header(f, &atom_type, &header_size); + mp4ff_parse_tag(f, atom_type, (uint32_t)(subsize-header_size)); + sumsize += subsize; + } + + return 0; +} + +/* find a metadata item by name */ +/* returns 0 if item found, 1 if no such item */ +static int32_t mp4ff_meta_find_by_name(const mp4ff_t *f, const char *item, char **value) +{ + uint32_t i; + + for (i = 0; i < f->tags.count; i++) + { + if (!stricmp(f->tags.tags[i].item, item)) + { + *value = strdup(f->tags.tags[i].value); + return 1; + } + } + + *value = NULL; + + /* not found */ + return 0; +} + +int32_t mp4ff_meta_get_num_items(const mp4ff_t *f) +{ + return f->tags.count; +} + +int32_t mp4ff_meta_get_by_index(const mp4ff_t *f, uint32_t index, + char **item, char **value) +{ + if (index >= f->tags.count) + { + *item = NULL; + *value = NULL; + return 0; + } else { + *item = strdup(f->tags.tags[index].item); + *value = strdup(f->tags.tags[index].value); + return 1; + } +} + +int32_t mp4ff_meta_get_title(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "title", value); +} + +int32_t mp4ff_meta_get_artist(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "artist", value); +} + +int32_t mp4ff_meta_get_writer(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "writer", value); +} + +int32_t mp4ff_meta_get_album(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "album", value); +} + +int32_t mp4ff_meta_get_date(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "date", value); +} + +int32_t mp4ff_meta_get_tool(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "tool", value); +} + +int32_t mp4ff_meta_get_comment(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "comment", value); +} + +int32_t mp4ff_meta_get_genre(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "genre", value); +} + +int32_t mp4ff_meta_get_track(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "track", value); +} + +int32_t mp4ff_meta_get_totaltracks(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "totaltracks", value); +} + +int32_t mp4ff_meta_get_disc(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "disc", value); +} + +int32_t mp4ff_meta_get_totaldiscs(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "totaldiscs", value); +} + +int32_t mp4ff_meta_get_compilation(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "compilation", value); +} + +int32_t mp4ff_meta_get_tempo(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "tempo", value); +} + +int32_t mp4ff_meta_get_coverart(const mp4ff_t *f, char **value) +{ + return mp4ff_meta_find_by_name(f, "cover", value); +} + +#endif \ No newline at end of file diff --git a/Libraries/FAAD2/Files/common/mp4ff/mp4sample.c b/Libraries/FAAD2/Files/common/mp4ff/mp4sample.c new file mode 100644 index 000000000..fb2484239 --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/mp4sample.c @@ -0,0 +1,152 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2004 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#include +#include "mp4ffint.h" + + +static int32_t mp4ff_chunk_of_sample(const mp4ff_t *f, const int32_t track, const int32_t sample, + int32_t *chunk_sample, int32_t *chunk) +{ + int32_t total_entries = 0; + int32_t chunk2entry; + int32_t chunk1, chunk2, chunk1samples, range_samples, total = 0; + + if (f->track[track] == NULL) + { + return -1; + } + + total_entries = f->track[track]->stsc_entry_count; + + chunk1 = 1; + chunk1samples = 0; + chunk2entry = 0; + + do + { + chunk2 = f->track[track]->stsc_first_chunk[chunk2entry]; + *chunk = chunk2 - chunk1; + range_samples = *chunk * chunk1samples; + + if (sample < total + range_samples) break; + + chunk1samples = f->track[track]->stsc_samples_per_chunk[chunk2entry]; + chunk1 = chunk2; + + if(chunk2entry < total_entries) + { + chunk2entry++; + total += range_samples; + } + } while (chunk2entry < total_entries); + + if (chunk1samples) + *chunk = (sample - total) / chunk1samples + chunk1; + else + *chunk = 1; + + *chunk_sample = total + (*chunk - chunk1) * chunk1samples; + + return 0; +} + +static int32_t mp4ff_chunk_to_offset(const mp4ff_t *f, const int32_t track, const int32_t chunk) +{ + const mp4ff_track_t * p_track = f->track[track]; + + if (p_track->stco_entry_count && (chunk > p_track->stco_entry_count)) + { + return p_track->stco_chunk_offset[p_track->stco_entry_count - 1]; + } else if (p_track->stco_entry_count) { + return p_track->stco_chunk_offset[chunk - 1]; + } else { + return 8; + } + + return 0; +} + +static int32_t mp4ff_sample_range_size(const mp4ff_t *f, const int32_t track, + const int32_t chunk_sample, const int32_t sample) +{ + int32_t i, total; + const mp4ff_track_t * p_track = f->track[track]; + + if (p_track->stsz_sample_size) + { + return (sample - chunk_sample) * p_track->stsz_sample_size; + } + else + { + if (sample>=p_track->stsz_sample_count) return 0;//error + + for(i = chunk_sample, total = 0; i < sample; i++) + { + total += p_track->stsz_table[i]; + } + } + + return total; +} + +static int32_t mp4ff_sample_to_offset(const mp4ff_t *f, const int32_t track, const int32_t sample) +{ + int32_t chunk, chunk_sample, chunk_offset1, chunk_offset2; + + mp4ff_chunk_of_sample(f, track, sample, &chunk_sample, &chunk); + + chunk_offset1 = mp4ff_chunk_to_offset(f, track, chunk); + chunk_offset2 = chunk_offset1 + mp4ff_sample_range_size(f, track, chunk_sample, sample); + + return chunk_offset2; +} + +int32_t mp4ff_audio_frame_size(const mp4ff_t *f, const int32_t track, const int32_t sample) +{ + int32_t bytes; + const mp4ff_track_t * p_track = f->track[track]; + + if (p_track->stsz_sample_size) + { + bytes = p_track->stsz_sample_size; + } else { + bytes = p_track->stsz_table[sample]; + } + + return bytes; +} + +int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample) +{ + int32_t offset; + + offset = mp4ff_sample_to_offset(f, track, sample); + mp4ff_set_position(f, offset); + + return 0; +} diff --git a/Libraries/FAAD2/Files/common/mp4ff/mp4tagupdate.c b/Libraries/FAAD2/Files/common/mp4ff/mp4tagupdate.c new file mode 100644 index 000000000..941a953ab --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/mp4tagupdate.c @@ -0,0 +1,645 @@ +#include +#include +#include "mp4ffint.h" + +#ifdef USE_TAGGING + +static uint32_t fix_byte_order_32(uint32_t src) +{ + uint32_t result; + uint32_t a, b, c, d; + int8_t data[4]; + + memcpy(data,&src,sizeof(src)); + a = (uint8_t)data[0]; + b = (uint8_t)data[1]; + c = (uint8_t)data[2]; + d = (uint8_t)data[3]; + + result = (a<<24) | (b<<16) | (c<<8) | d; + return (uint32_t)result; +} + +static uint16_t fix_byte_order_16(uint16_t src) +{ + uint16_t result; + uint16_t a, b; + int8_t data[2]; + + memcpy(data,&src,sizeof(src)); + a = (uint8_t)data[0]; + b = (uint8_t)data[1]; + + result = (a<<8) | b; + return (uint16_t)result; +} + + +typedef struct +{ + void * data; + unsigned written; + unsigned allocated; + unsigned error; +} membuffer; + +unsigned membuffer_write(membuffer * buf,const void * ptr,unsigned bytes) +{ + unsigned dest_size = buf->written + bytes; + + if (buf->error) return 0; + if (dest_size > buf->allocated) + { + do + { + buf->allocated <<= 1; + } while(dest_size > buf->allocated); + + { + void * newptr = realloc(buf->data,buf->allocated); + if (newptr==0) + { + free(buf->data); + buf->data = 0; + buf->error = 1; + return 0; + } + buf->data = newptr; + } + } + + if (ptr) memcpy((char*)buf->data + buf->written,ptr,bytes); + buf->written += bytes; + return bytes; +} + +#define membuffer_write_data membuffer_write + +unsigned membuffer_write_int32(membuffer * buf,uint32_t data) +{ + uint8_t temp[4] = {(uint8_t)(data>>24),(uint8_t)(data>>16),(uint8_t)(data>>8),(uint8_t)data}; + return membuffer_write_data(buf,temp,4); +} + +unsigned membuffer_write_int24(membuffer * buf,uint32_t data) +{ + uint8_t temp[3] = {(uint8_t)(data>>16),(uint8_t)(data>>8),(uint8_t)data}; + return membuffer_write_data(buf,temp,3); +} + +unsigned membuffer_write_int16(membuffer * buf,uint16_t data) +{ + uint8_t temp[2] = {(uint8_t)(data>>8),(uint8_t)data}; + return membuffer_write_data(buf,temp,2); +} + +unsigned membuffer_write_atom_name(membuffer * buf,const char * data) +{ + return membuffer_write_data(buf,data,4)==4 ? 1 : 0; +} + +void membuffer_write_atom(membuffer * buf,const char * name,unsigned size,const void * data) +{ + membuffer_write_int32(buf,size + 8); + membuffer_write_atom_name(buf,name); + membuffer_write_data(buf,data,size); +} + +unsigned membuffer_write_string(membuffer * buf,const char * data) +{ + return membuffer_write_data(buf,data,strlen(data)); +} + +unsigned membuffer_write_int8(membuffer * buf,uint8_t data) +{ + return membuffer_write_data(buf,&data,1); +} + +void * membuffer_get_ptr(const membuffer * buf) +{ + return buf->data; +} + +unsigned membuffer_get_size(const membuffer * buf) +{ + return buf->written; +} + +unsigned membuffer_error(const membuffer * buf) +{ + return buf->error; +} + +void membuffer_set_error(membuffer * buf) {buf->error = 1;} + +unsigned membuffer_transfer_from_file(membuffer * buf,mp4ff_t * src,unsigned bytes) +{ + unsigned oldsize; + void * bufptr; + + oldsize = membuffer_get_size(buf); + if (membuffer_write_data(buf,0,bytes) != bytes) return 0; + + bufptr = membuffer_get_ptr(buf); + if (bufptr==0) return 0; + + if ((unsigned)mp4ff_read_data(src,(char*)bufptr + oldsize,bytes)!=bytes) + { + membuffer_set_error(buf); + return 0; + } + + return bytes; +} + + +membuffer * membuffer_create() +{ + const unsigned initial_size = 256; + + membuffer * buf = (membuffer *) malloc(sizeof(membuffer)); + buf->data = malloc(initial_size); + buf->written = 0; + buf->allocated = initial_size; + buf->error = buf->data == 0 ? 1 : 0; + + return buf; +} + +void membuffer_free(membuffer * buf) +{ + if (buf->data) free(buf->data); + free(buf); +} + +void * membuffer_detach(membuffer * buf) +{ + void * ret; + + if (buf->error) return 0; + + ret = realloc(buf->data,buf->written); + + if (ret == 0) free(buf->data); + + buf->data = 0; + buf->error = 1; + + return ret; +} + +#if 0 +/* metadata tag structure */ +typedef struct +{ + char *item; + char *value; +} mp4ff_tag_t; + +/* metadata list structure */ +typedef struct +{ + mp4ff_tag_t *tags; + uint32_t count; +} mp4ff_metadata_t; +#endif + +typedef struct +{ + const char * atom; + const char * name; +} stdmeta_entry; + +static stdmeta_entry stdmetas[] = +{ + {"©nam","title"}, + {"©ART","artist"}, + {"©wrt","writer"}, + {"©alb","album"}, + {"©day","date"}, + {"©too","tool"}, + {"©cmt","comment"}, +// {"©gen","genre"}, + {"cpil","compilation"}, +// {"trkn","track"}, +// {"disk","disc"}, +// {"gnre","genre"}, + {"covr","cover"}, +}; + + +static const char* find_standard_meta(const char * name) //returns atom name if found, 0 if not +{ + unsigned n; + for(n=0;ncount); + memset(mask,0,data->count); + + { + const char * tracknumber_ptr = 0, * totaltracks_ptr = 0; + const char * discnumber_ptr = 0, * totaldiscs_ptr = 0; + const char * genre_ptr = 0, * tempo_ptr = 0; + for(metaptr = 0; metaptr < data->count; metaptr++) + { + mp4ff_tag_t * tag = &data->tags[metaptr]; + if (!stricmp(tag->item,"tracknumber") || !stricmp(tag->item,"track")) + { + if (tracknumber_ptr==0) tracknumber_ptr = tag->value; + mask[metaptr] = 1; + } + else if (!stricmp(tag->item,"totaltracks")) + { + if (totaltracks_ptr==0) totaltracks_ptr = tag->value; + mask[metaptr] = 1; + } + else if (!stricmp(tag->item,"discnumber") || !stricmp(tag->item,"disc")) + { + if (discnumber_ptr==0) discnumber_ptr = tag->value; + mask[metaptr] = 1; + } + else if (!stricmp(tag->item,"totaldiscs")) + { + if (totaldiscs_ptr==0) totaldiscs_ptr = tag->value; + mask[metaptr] = 1; + } + else if (!stricmp(tag->item,"genre")) + { + if (genre_ptr==0) genre_ptr = tag->value; + mask[metaptr] = 1; + } + else if (!stricmp(tag->item,"tempo")) + { + if (tempo_ptr==0) tempo_ptr = tag->value; + mask[metaptr] = 1; + } + + } + + if (tracknumber_ptr) membuffer_write_track_tag(buf,"trkn",myatoi(tracknumber_ptr),myatoi(totaltracks_ptr)); + if (discnumber_ptr) membuffer_write_track_tag(buf,"disk",myatoi(discnumber_ptr),myatoi(totaldiscs_ptr)); + if (tempo_ptr) membuffer_write_int16_tag(buf,"tmpo",(uint16_t)myatoi(tempo_ptr)); + + if (genre_ptr) + { + uint32_t index = mp4ff_meta_genre_to_index(genre_ptr); + if (index==0) + membuffer_write_std_tag(buf,"©gen",genre_ptr); + else + membuffer_write_int16_tag(buf,"gnre",(uint16_t)index); + } + } + + for(metaptr = 0; metaptr < data->count; metaptr++) + { + if (!mask[metaptr]) + { + mp4ff_tag_t * tag = &data->tags[metaptr]; + const char * std_meta_atom = find_standard_meta(tag->item); + if (std_meta_atom) + { + membuffer_write_std_tag(buf,std_meta_atom,tag->value); + } + else + { + membuffer_write_custom_tag(buf,tag->item,tag->value); + } + } + } + + free(mask); + + if (membuffer_error(buf)) + { + membuffer_free(buf); + return 0; + } + + *out_size = membuffer_get_size(buf); + *out_buffer = membuffer_detach(buf); + membuffer_free(buf); + + return 1; +} + +static uint32_t find_atom(mp4ff_t * f,uint64_t base,uint32_t size,const char * name) +{ + uint32_t remaining = size; + uint64_t atom_offset = base; + for(;;) + { + char atom_name[4]; + uint32_t atom_size; + + mp4ff_set_position(f,atom_offset); + + if (remaining < 8) break; + atom_size = mp4ff_read_int32(f); + if (atom_size > remaining || atom_size < 8) break; + mp4ff_read_data(f,atom_name,4); + + if (!memcmp(atom_name,name,4)) + { + mp4ff_set_position(f,atom_offset); + return 1; + } + + remaining -= atom_size; + atom_offset += atom_size; + } + return 0; +} + +static uint32_t find_atom_v2(mp4ff_t * f,uint64_t base,uint32_t size,const char * name,uint32_t extraheaders,const char * name_inside) +{ + uint64_t first_base = (uint64_t)(-1); + while(find_atom(f,base,size,name))//try to find atom with atom in it + { + uint64_t mybase = mp4ff_position(f); + uint32_t mysize = mp4ff_read_int32(f); + + if (first_base == (uint64_t)(-1)) first_base = mybase; + + if (mysize < 8 + extraheaders) break; + + if (find_atom(f,mybase+(8+extraheaders),mysize-(8+extraheaders),name_inside)) + { + mp4ff_set_position(f,mybase); + return 2; + } + base += mysize; + if (size<=mysize) {size=0;break;} + size -= mysize; + } + + if (first_base != (uint64_t)(-1))//wanted atom inside not found + { + mp4ff_set_position(f,first_base); + return 1; + } + else return 0; +} + +static uint32_t create_meta(const mp4ff_metadata_t * data,void ** out_buffer,uint32_t * out_size) +{ + membuffer * buf; + uint32_t ilst_size; + void * ilst_buffer; + + if (!create_ilst(data,&ilst_buffer,&ilst_size)) return 0; + + buf = membuffer_create(); + + membuffer_write_int32(buf,0); + membuffer_write_atom(buf,"ilst",ilst_size,ilst_buffer); + free(ilst_buffer); + + *out_size = membuffer_get_size(buf); + *out_buffer = membuffer_detach(buf); + membuffer_free(buf); + return 1; +} + +static uint32_t create_udta(const mp4ff_metadata_t * data,void ** out_buffer,uint32_t * out_size) +{ + membuffer * buf; + uint32_t meta_size; + void * meta_buffer; + + if (!create_meta(data,&meta_buffer,&meta_size)) return 0; + + buf = membuffer_create(); + + membuffer_write_atom(buf,"meta",meta_size,meta_buffer); + + free(meta_buffer); + + *out_size = membuffer_get_size(buf); + *out_buffer = membuffer_detach(buf); + membuffer_free(buf); + return 1; +} + +static uint32_t modify_moov(mp4ff_t * f,const mp4ff_metadata_t * data,void ** out_buffer,uint32_t * out_size) +{ + uint64_t total_base = f->moov_offset + 8; + uint32_t total_size = (uint32_t)(f->moov_size - 8); + + uint64_t udta_offset,meta_offset,ilst_offset; + uint32_t udta_size, meta_size, ilst_size; + + uint32_t new_ilst_size; + void * new_ilst_buffer; + + uint8_t * p_out; + int32_t size_delta; + + + if (!find_atom_v2(f,total_base,total_size,"udta",0,"meta")) + { + membuffer * buf; + void * new_udta_buffer; + uint32_t new_udta_size; + if (!create_udta(data,&new_udta_buffer,&new_udta_size)) return 0; + + buf = membuffer_create(); + mp4ff_set_position(f,total_base); + membuffer_transfer_from_file(buf,f,total_size); + + membuffer_write_atom(buf,"udta",new_udta_size,new_udta_buffer); + + free(new_udta_buffer); + + *out_size = membuffer_get_size(buf); + *out_buffer = membuffer_detach(buf); + membuffer_free(buf); + return 1; + } + else + { + udta_offset = mp4ff_position(f); + udta_size = mp4ff_read_int32(f); + if (find_atom_v2(f,udta_offset+8,udta_size-8,"meta",4,"ilst")<2) + { + membuffer * buf; + void * new_meta_buffer; + uint32_t new_meta_size; + if (!create_meta(data,&new_meta_buffer,&new_meta_size)) return 0; + + buf = membuffer_create(); + mp4ff_set_position(f,total_base); + membuffer_transfer_from_file(buf,f,(uint32_t)(udta_offset - total_base)); + + membuffer_write_int32(buf,udta_size + 8 + new_meta_size); + membuffer_write_atom_name(buf,"udta"); + membuffer_transfer_from_file(buf,f,udta_size); + + membuffer_write_atom(buf,"meta",new_meta_size,new_meta_buffer); + free(new_meta_buffer); + + *out_size = membuffer_get_size(buf); + *out_buffer = membuffer_detach(buf); + membuffer_free(buf); + return 1; + } + meta_offset = mp4ff_position(f); + meta_size = mp4ff_read_int32(f); + if (!find_atom(f,meta_offset+12,meta_size-12,"ilst")) return 0;//shouldn't happen, find_atom_v2 above takes care of it + ilst_offset = mp4ff_position(f); + ilst_size = mp4ff_read_int32(f); + + if (!create_ilst(data,&new_ilst_buffer,&new_ilst_size)) return 0; + + size_delta = new_ilst_size - (ilst_size - 8); + + *out_size = total_size + size_delta; + *out_buffer = malloc(*out_size); + if (*out_buffer == 0) + { + free(new_ilst_buffer); + return 0; + } + + p_out = (uint8_t*)*out_buffer; + + mp4ff_set_position(f,total_base); + mp4ff_read_data(f,p_out,(uint32_t)(udta_offset - total_base )); p_out += (uint32_t)(udta_offset - total_base ); + *(uint32_t*)p_out = fix_byte_order_32(mp4ff_read_int32(f) + size_delta); p_out += 4; + mp4ff_read_data(f,p_out,4); p_out += 4; + mp4ff_read_data(f,p_out,(uint32_t)(meta_offset - udta_offset - 8)); p_out += (uint32_t)(meta_offset - udta_offset - 8); + *(uint32_t*)p_out = fix_byte_order_32(mp4ff_read_int32(f) + size_delta); p_out += 4; + mp4ff_read_data(f,p_out,4); p_out += 4; + mp4ff_read_data(f,p_out,(uint32_t)(ilst_offset - meta_offset - 8)); p_out += (uint32_t)(ilst_offset - meta_offset - 8); + *(uint32_t*)p_out = fix_byte_order_32(mp4ff_read_int32(f) + size_delta); p_out += 4; + mp4ff_read_data(f,p_out,4); p_out += 4; + + memcpy(p_out,new_ilst_buffer,new_ilst_size); + p_out += new_ilst_size; + + mp4ff_set_position(f,ilst_offset + ilst_size); + mp4ff_read_data(f,p_out,(uint32_t)(total_size - (ilst_offset - total_base) - ilst_size)); + + free(new_ilst_buffer); + } + return 1; + +} + +int32_t mp4ff_meta_update(mp4ff_callback_t *f,const mp4ff_metadata_t * data) +{ + void * new_moov_data; + uint32_t new_moov_size; + + mp4ff_t *ff = malloc(sizeof(mp4ff_t)); + + memset(ff, 0, sizeof(mp4ff_t)); + ff->stream = f; + mp4ff_set_position(ff,0); + + parse_atoms(ff,1); + + + if (!modify_moov(ff,data,&new_moov_data,&new_moov_size)) + { + mp4ff_close(ff); + return 0; + } + + /* copy moov atom to end of the file */ + if (ff->last_atom != ATOM_MOOV) + { + char *free_data = "free"; + + /* rename old moov to free */ + mp4ff_set_position(ff, ff->moov_offset + 4); + mp4ff_write_data(ff, free_data, 4); + + mp4ff_set_position(ff, ff->file_size); + mp4ff_write_int32(ff,new_moov_size + 8); + mp4ff_write_data(ff,"moov",4); + mp4ff_write_data(ff, new_moov_data, new_moov_size); + } + else + { + mp4ff_set_position(ff, ff->moov_offset); + mp4ff_write_int32(ff,new_moov_size + 8); + mp4ff_write_data(ff,"moov",4); + mp4ff_write_data(ff, new_moov_data, new_moov_size); + } + + mp4ff_truncate(ff); + + mp4ff_close(ff); + return 1; +} +#endif diff --git a/Libraries/FAAD2/Files/common/mp4ff/mp4util.c b/Libraries/FAAD2/Files/common/mp4ff/mp4util.c new file mode 100644 index 000000000..c4983a142 --- /dev/null +++ b/Libraries/FAAD2/Files/common/mp4ff/mp4util.c @@ -0,0 +1,188 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2004 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id$ +**/ + +#include "mp4ffint.h" +#include + +int32_t mp4ff_read_data(mp4ff_t *f, int8_t *data, uint32_t size) +{ + int32_t result = 1; + + result = f->stream->read(f->stream->user_data, data, size); + + f->current_position += size; + + return result; +} + +int32_t mp4ff_truncate(mp4ff_t * f) +{ + return f->stream->truncate(f->stream->user_data); +} + +int32_t mp4ff_write_data(mp4ff_t *f, int8_t *data, uint32_t size) +{ + int32_t result = 1; + + result = f->stream->write(f->stream->user_data, data, size); + + f->current_position += size; + + return result; +} + +int32_t mp4ff_write_int32(mp4ff_t *f,const uint32_t data) +{ + uint32_t result; + uint32_t a, b, c, d; + int8_t temp[4]; + + *(uint32_t*)temp = data; + a = (uint8_t)temp[0]; + b = (uint8_t)temp[1]; + c = (uint8_t)temp[2]; + d = (uint8_t)temp[3]; + + result = (a<<24) | (b<<16) | (c<<8) | d; + + return mp4ff_write_data(f,(uint8_t*)&result,sizeof(result)); +} + +int32_t mp4ff_set_position(mp4ff_t *f, const int64_t position) +{ + f->stream->seek(f->stream->user_data, position); + f->current_position = position; + + return 0; +} + +int64_t mp4ff_position(const mp4ff_t *f) +{ + return f->current_position; +} + +uint64_t mp4ff_read_int64(mp4ff_t *f) +{ + uint8_t data[8]; + uint64_t result = 0; + int8_t i; + + mp4ff_read_data(f, data, 8); + + for (i = 0; i < 8; i++) + { + result |= ((uint64_t)data[i]) << ((7 - i) * 8); + } + + return result; +} + +uint32_t mp4ff_read_int32(mp4ff_t *f) +{ + uint32_t result; + uint32_t a, b, c, d; + int8_t data[4]; + + mp4ff_read_data(f, data, 4); + a = (uint8_t)data[0]; + b = (uint8_t)data[1]; + c = (uint8_t)data[2]; + d = (uint8_t)data[3]; + + result = (a<<24) | (b<<16) | (c<<8) | d; + return (uint32_t)result; +} + +uint32_t mp4ff_read_int24(mp4ff_t *f) +{ + uint32_t result; + uint32_t a, b, c; + int8_t data[4]; + + mp4ff_read_data(f, data, 3); + a = (uint8_t)data[0]; + b = (uint8_t)data[1]; + c = (uint8_t)data[2]; + + result = (a<<16) | (b<<8) | c; + return (uint32_t)result; +} + +uint16_t mp4ff_read_int16(mp4ff_t *f) +{ + uint32_t result; + uint32_t a, b; + int8_t data[2]; + + mp4ff_read_data(f, data, 2); + a = (uint8_t)data[0]; + b = (uint8_t)data[1]; + + result = (a<<8) | b; + return (uint16_t)result; +} + +char * mp4ff_read_string(mp4ff_t * f,uint32_t length) +{ + char * str = (char*)malloc(length + 1); + if (str!=0) + { + if ((uint32_t)mp4ff_read_data(f,str,length)!=length) + { + free(str); + str = 0; + } + else + { + str[length] = 0; + } + } + return str; +} + +uint8_t mp4ff_read_char(mp4ff_t *f) +{ + uint8_t output; + mp4ff_read_data(f, &output, 1); + return output; +} + +uint32_t mp4ff_read_mp4_descr_length(mp4ff_t *f) +{ + uint8_t b; + uint8_t numBytes = 0; + uint32_t length = 0; + + do + { + b = mp4ff_read_char(f); + numBytes++; + length = (length << 7) | (b & 0x7F); + } while ((b & 0x80) && numBytes < 4); + + return length; +} diff --git a/Libraries/Shorten/Files/AUTHORS b/Libraries/Shorten/Files/AUTHORS new file mode 100644 index 000000000..b0fafb179 --- /dev/null +++ b/Libraries/Shorten/Files/AUTHORS @@ -0,0 +1,11 @@ +Marcus Heese +marc.icq@gmx.de +icq : 126202773 + +and very special thanks to... + +Frederic Fondriest +icq : 17293220 +irc : /server irc.eu.freenode.net /join #lamip + +...for a linux audio player with a very good API! :) diff --git a/Libraries/Shorten/Files/COPYING b/Libraries/Shorten/Files/COPYING new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/Libraries/Shorten/Files/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Libraries/Shorten/Files/ChangeLog b/Libraries/Shorten/Files/ChangeLog new file mode 100644 index 000000000..daf054b21 --- /dev/null +++ b/Libraries/Shorten/Files/ChangeLog @@ -0,0 +1,6 @@ +lamip-inputSHORTEN_20050405: + - fixed a few minor things. Some variables have to be declared static! + +lamip-inputSHORTEN_20050324: + - well.... it is the initial release! + diff --git a/Libraries/Shorten/Files/Makefile.am b/Libraries/Shorten/Files/Makefile.am new file mode 100644 index 000000000..54303aeeb --- /dev/null +++ b/Libraries/Shorten/Files/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = shorten src diff --git a/Libraries/Shorten/Files/NEWS b/Libraries/Shorten/Files/NEWS new file mode 100644 index 000000000..355f41d9c --- /dev/null +++ b/Libraries/Shorten/Files/NEWS @@ -0,0 +1,2 @@ +well... who the hell takes a look in all these damn files?!? + diff --git a/Libraries/Shorten/Files/README b/Libraries/Shorten/Files/README new file mode 100644 index 000000000..22fbef77b --- /dev/null +++ b/Libraries/Shorten/Files/README @@ -0,0 +1,45 @@ +LAMIP-0.0.3 SHORTEN decoder +--------------------------- + +This is a shorten decoder for lamip. SHORTEN has been a widely used lossless +codec. Since version 3 shorten has seeking support through seek tables (.skt). +They can also be merged into the shorten file (.shn). The code for this plugin +derives from the xmms shorten plugin version 2.4.0 written by Jason Jordan. +Look at that homepage: + +http://etree.org/shnutils/shorten/ + +I have redesigned the xmms-shn plugin for a libshorten decoding library. You +should be able to install libshorten as a standalone-lib and decode shorten +files through that library + +Config options +-------------- +- error_output_method: can be STDERR and DEVNULL... I think the names are +self explanatory + +- swap_bytes: If you have a big-endian machine, switch it on! + +- verbose: It is for some verbose debugging messages in the integrated logging +facility of libshorten + +- seek_tables_path: An absolute path to a directory which contains shorten seek- +tables + +- relative_seek_tables_path: A relative path to directories which contain +shorten seek-tables + + +INSTALL +------- +run : +- autoreconf -vifs +- ./configure --enable-debug +- make + +and as root : +- make install or make install-strip + +---------- +marc.icq@gmx.de + diff --git a/Libraries/Shorten/Files/TODO b/Libraries/Shorten/Files/TODO new file mode 100644 index 000000000..f285bed92 --- /dev/null +++ b/Libraries/Shorten/Files/TODO @@ -0,0 +1,7 @@ +for the future: +--------------- + +- implement id3v2 tag reading, perhaps... :) +- link against a released libshorten (static development version used in this plugin by now) +- documentation for libshorten + diff --git a/Libraries/Shorten/Files/configure.ac b/Libraries/Shorten/Files/configure.ac new file mode 100644 index 000000000..07c2b74ae --- /dev/null +++ b/Libraries/Shorten/Files/configure.ac @@ -0,0 +1,78 @@ +dnl configure.in for lamip-0.0.3 shorten input plugin + +AC_INIT(libinputshorten, 0.0.3) +AC_CONFIG_SRCDIR(src/libinputshorten.c) + +AM_INIT_AUTOMAKE(inputSHORTEN, 0.0.3) +AM_CONFIG_HEADER(shorten/include/config.h) + +dnl AC_INIT(src/libinputshorten.c) +dnl AM_INIT_AUTOMAKE(libinputshorten, 0.0.3) + +AM_DISABLE_STATIC + +dnl save CFLAGS since AC_PROG_CC insert "-g -O2" if CFLAGS is empty +cflags_save="$CFLAGS" +AC_PROG_CC +AC_PROG_CXX +AC_PROG_AWK +AC_PROG_LN_S +AC_PROG_INSTALL +AC_LIBTOOL_DLOPEN +AM_PROG_LIBTOOL +AC_HEADER_STDC + +AC_CHECK_LIB(m, main) + +AC_HEADER_STDC +AC_CHECK_HEADERS(stdarg.h inttypes.h dirent.h) + +AC_CHECK_SIZEOF(unsigned long) + +AC_FUNC_SETVBUF_REVERSED +AC_TYPE_SIGNAL +AC_CHECK_FUNCS(opendir readdir closedir strerror vsnprintf) + +AC_CHECK_PROGS(LAMIP, lamip, + AC_MSG_ERROR(*** lamip not found)) + +CFLAGS="$cflags_save -Wall `lamip --cflags`" +CPPFLAGS="$CPPFLAGS $CFLAGS" +AC_SUBST(CFLAGS) +cflags_save="$CFLAGS" + +AC_CHECK_HEADER(lamip.h,, + AC_MSG_ERROR(*** LAMIP headers not found check your CFLAGS)) + +CFLAGS="$cflags_save -I. -I.. -Ishorten/include" +cflags_save="$CFLAGS" +AC_SUBST(CFLAGS) + +AC_ARG_ENABLE(debug, +[ --enable-debug (for developers only... and people with probs!) ], +[enable_debug=$enableval], +[enable_debug="no"]) + +if test "x$enable_debug" = xyes; then + CPPFLAGS="$CPPFLAGS -DDEBUG" + CFLAGS="$CFLAGS -g" + AC_MSG_RESULT([*** Debugging is enabled... ]) +fi + +AC_OUTPUT([Makefile shorten/Makefile shorten/util/Makefile shorten/src/Makefile src/Makefile]) + +echo "" +echo "*** LAMIP inputSHORTEN (Shorten Codec) plugin succefully configured ***" +echo "" +echo "This plugin allows you to play *.shn files..." +echo "" +echo "install-dir : `lamip --plugin-dir`" +echo "" +echo "CFLAGS : $CFLAGS" +echo "LDFLAGS : $LDFLAGS" +echo "CPPFLAGS : $CPPFLAGS" +echo "" +if test "x$enable_debug" = xyes; then + echo "- debugging messages are enabled!" + echo "" +fi diff --git a/Libraries/Shorten/Files/shorten/Makefile.am b/Libraries/Shorten/Files/shorten/Makefile.am new file mode 100644 index 000000000..83e769f9d --- /dev/null +++ b/Libraries/Shorten/Files/shorten/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = util src diff --git a/Libraries/Shorten/Files/shorten/doc/LICENSE.shorten b/Libraries/Shorten/Files/shorten/doc/LICENSE.shorten new file mode 100644 index 000000000..d5066c7a6 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/doc/LICENSE.shorten @@ -0,0 +1,20 @@ +SHORTEN SOFTWARE LICENSE + +This software is being provided to you, the LICENSEE, by Tony Robinson +and SoftSound under the following license. By obtaining, using and/or +copying this software, you agree that you have read, understood, and +will comply with these terms and conditions: + +This software may not be sold or incorporated into any product which is +sold without prior permission from SoftSound. When no charge is made, +this software may be copied and distributed freely. + +Permission is granted to use this software for decoding and +non-commercial encoding (e.g. private or research use). Please email +shorten@softsound.com for commercial encoding terms. + +DISCLAIMER + +This software carries no warranty, expressed or implied. The user +assumes all risks, known or unknown, direct or indirect, which involve +this software in any way. diff --git a/Libraries/Shorten/Files/shorten/doc/xmms-shn/AUTHORS b/Libraries/Shorten/Files/shorten/doc/xmms-shn/AUTHORS new file mode 100644 index 000000000..bc5844377 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/doc/xmms-shn/AUTHORS @@ -0,0 +1 @@ +Jason Jordan diff --git a/Libraries/Shorten/Files/shorten/doc/xmms-shn/CREDITS b/Libraries/Shorten/Files/shorten/doc/xmms-shn/CREDITS new file mode 100644 index 000000000..c0806da05 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/doc/xmms-shn/CREDITS @@ -0,0 +1,39 @@ +Thanks goes to the XMMS development team, as version 0.x through 1.x of this +plugin were originally based on the Input/wav/wav.[ch] files from the XMMS +distribution. + +Thanks also goes to Caleb Epstein for testing and reporting bugs, and for +providing .deb and .rpm packages. + +Wayne Stielau provided specs and code that became the basis of the 2.x version +of this plugin. Without his help, this version would have taken much longer to +write. + +Thanks goes to scottv for reporting a crash-inducing bug, as well as providing +me a way to download the file that caused it for analysis. + +Thanks goes to Daniel Robbins for reporting the truncated-scope bug. + +Thanks goes to Paul Mather for reporting the m:ss.ff rounding bug in shntool +(which also affected xmms-shn), as well as providing a detailed analysis of the +problem which expedited its fix. + +Thanks goes to Ari Pollak for reporting a crash that led to the discovery of a +race condition in the .shn unloading code. + +Thanks to Alex Prestin for sending a byte-swap patch that fixed a problem where +all files would play back as static on his Ultra 10 and Ultra 30, as well as +providing a patch to load text files in the file information box, which became +the basis of the current implementation. + +Thanks to Rhett Monteg Hollander for reporting a bug that prevented xmms-shn +from playing files encoded with alternate options, e.g. "-b1280 -p16 -m4". + +Thanks to Peter Kunath for sending a one-line patch to fix a bug in shorten that +caused incorrect seek tables to be generated under certain conditions, as well +as for providing a way for xmms-shn to automatically detect this bug so that it +can disable seeking in affected files. + +Finally, thanks goes to SoftSound for creating a great waveform encoder, +shorten, and especially for providing the source code to it. Without the +source, there would be no seek-enabled versions of shorten, and no xmms-shn 2.x. diff --git a/Libraries/Shorten/Files/shorten/doc/xmms-shn/NEWS b/Libraries/Shorten/Files/shorten/doc/xmms-shn/NEWS new file mode 100644 index 000000000..6c2f9bb57 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/doc/xmms-shn/NEWS @@ -0,0 +1 @@ +No news is good news. diff --git a/Libraries/Shorten/Files/shorten/doc/xmms-shn/README b/Libraries/Shorten/Files/shorten/doc/xmms-shn/README new file mode 100644 index 000000000..1e70e1721 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/doc/xmms-shn/README @@ -0,0 +1,346 @@ +xmms-shn version 2.4.x + + +-------- +Overview +-------- + +xmms-shn provides playback support for shorten (.shn) files in XMMS. Real-time +seeking support is provided for .shn files that have accompanying seek tables +generated by shorten 3.x. Otherwise, seeking is not supported. + +See the "Shorten 3.x overview" section below for more information about this new +seek-enabled version of shorten. + + +------------ +Availability +------------ + +The latest version of this plugin can always be found at the sites below: + + http://www.etree.org/shnutils/ + http://shnutils.freeshell.org/ + +Please see the ChangeLog file for changes to this plugin since its creation. + + +------------ +Dependencies +------------ + +As of version 2.0, xmms-shn no longer depends on an external shorten executable +to work, since the core shorten algorithm has been incorporated directly into +xmms-shn. + +You should have XMMS 1.0.0 or newer, and GTK & GLIB 1.2.2 or newer. The +configure script will usually find these if you installed them from source. +However, if you installed any of the above via .rpm's, then you may need to tell +the configure script where to find them. To see what options are available, +type: + +% ./configure --help + +The applicable options are the following: + + --with-xmms-prefix=PFX Prefix where XMMS is installed (optional) + --with-xmms-exec-prefix=PFX Exec prefix where XMMS is installed (optional) + --with-glib-prefix=PFX Prefix where GLIB is installed (optional) + --with-glib-exec-prefix=PFX Exec prefix where GLIB is installed (optional) + --disable-glibtest Do not try to compile and run a test GLIB program + --with-gtk-prefix=PFX Prefix where GTK is installed (optional) + --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional) + --disable-gtktest Do not try to compile and run a test GTK program + + +----------------------------------- +Building and installing this plugin +----------------------------------- + +For instructions on how to build and install this plugin, please consult the +INSTALL file. It is usually as simple as the following: + +% ./configure +% make +% su -c "make install" +Password: (give your root password here) +% + + +--------------------- +Configuration options +--------------------- + +This section details the options that can be found in the plugin's configuration +window in XMMS, under Preferences -> Audio I/O Plugins -> SHN Player 2.4.x -> +Configure. + + +Error Display tab: +================== + + Error display options: + ---------------------- + + This option determines where any error messages go - to a popup window, + to standard error, or to the bit bucket. Pretty self-explanatory. + + +Seek Tables tab: +================ + + Alternate seek table file locations: + ------------------------------------ + + These options allow you to specify alternate directores to search for .skt + (seek table) files. These directories will be searched after all other attempts + to locate a seek table for a given file have failed. + + The "Relative seek table path" option allows you to specify a subdirectory + relative to the base directory of a given file, where seek tables may reside. + This is useful if you create seek table files for a group of .shn files, and + store them in a commonly-named subdirectory of that group. + + The "Absolute seek table path" option allows you to specify an absolute directory + where seek tables may reside. This is useful if you create seek table files for + multiple groups of .shn files and store them in the same directory. + + When searching for seek tables belonging to a file (e.g. '/mnt/shn/filename.shn'), + xmms-shn will do the following, in this order, until it either finds a seek table + or runs out of places to check for one: + + 1. look for a seek table appended to '/mnt/shn/filename.shn' (whether at the end, + or hidden behind an ID3v1 tag) + + 2. look for a seek table in '/mnt/shn/filename.skt' + + 3. look for a seek table in '/mnt/shn/relative_dir/filename.skt', where + 'relative_dir' is the directory specified in the "Relative seek table path" + option + + 4. look for a seek table in '/absolute_dir/filename.skt', where 'absolute_dir' + is the directory specified in the "Absolute seek table path" option + + +Miscellaneous tab: +================== + + Miscellaneous options: + ---------------------- + + The "Swap audio bytes on output" option is useful in situations where every file + you play back is static. This is known to help on the following platforms: + + + Sun Ultra 10 and Ultra 30 both running Solaris 8 + + SGI Octane/Irix 6.5 + + + The "Display debug info to stderr" option specifies whether to display debugging + messages to stderr. This is potentially useful for remote debugging, and also + just to see what's going on. Debug lines are always shown with a prefix of + "xmms-shn [debug]:". + + Note that if "Display debug info to stderr" is enabled, and the error display + option is set to /dev/null, then all non-debug messages that normally would be + suppressed are displayed to stderr with a prefix of "xmms-shn [error]:". + + The "Load text files in file information box" option specifies whether text files + found in the same or parent directory as the given file should be loaded into the + file information box. Each file found will be loaded in a separate tab. The tabs + will be labeled "Text file n", where n starts at 1 and increases with each new + text file. + + The "Text file extensions" option lets you specify a comma-separated list of case- + sensitive file extensions that xmms-shn will recognize as text files. The default + list is "txt,nfo". This option is only useful if "Load text files in file + information box" is enabled. + + Note: + + Text file support is useful for quickly determining information about the given + file and/or the show it belongs to, assuming such information is contained within + the text files. + + Text file location assumes some combination of the following two directory + structures: all text files and .shn files in the same directory, or text files + in a directory with all .shn files in subdirectories one level deep. This + pretty much covers all directory structures seen on live music servers and file- + sharing programs. + + +-------------------- +File information box +-------------------- + +This section describes the output shown in the file information window, which +can be viewed by choosing 'File Inf.' from the 'Misc. Opt' button in the +playlist editor, or selecting 'View File Info' from XMMS's main popup menu. + +Properties tab: +=============== + + Filename: Shows the full path to the .shn file being examined. + + Length: Shows the length of the WAVE data in the file, in m:ss.nnn format. + If the WAVE data is CD-quality (see below for definition), then the length is + shown in m:ss.ff format, where ff is a number from 00 to 74 that best + approximates the number of frames (2352-byte blocks) remaining after m:ss. + + Note on rounding: If the WAVE data is CD-quality, then its length is + rounded to the nearest frame. Otherwise, it is rounded + to the nearest millisecond. + + Seekable: Shows whether a seek table was found for the given file. + + Seek table revision: Shows the revision number of the seek tables, if + present. Note that it is possible for this field to contain a numeric value, + even when the file is not seekable - for example, xmms-shn might detect that + certain seek tables are buggy, and disable seeking in those files. + + Compression ratio: Shows the compression ratio for the given file. This is + simply the file's actual size divided by its total size, as calculated from + the chunk size contained in the WAVE header. Note that if the given file is + truncated (or has junk appended to it), then the compression ratio will be + skewed accordingly. + + CD-quality properties: + ---------------------- + + CD-quality: Shows whether the given file is CD-quality. A file is considered + to be CD-quality if its header indicates 2 channels, 16 bits/sample, 44100 + samples/second, and 176400 average bytes/second. + + Cut on a sector boundary: If the given file is CD-quality, then this shows + whether the length of its WAVE data is a multiple of 2352 bytes (1/75 of a + second). Otherwise, "n/a" is displayed. + + Sector misalignemt: If the file is CD-quality, then the sector misalignment + is simply the remainder when the data size is divided by 2352; i.e. it is the + number of bytes by which the audio data exceeds the previous sector boundary. + + Long enough to be burned: If the given file is CD-quality, then this shows + whether the length of its WAVE data is long enough to be burned (705600 bytes, + or 3 seconds in length). Otherwise, "n/a" is displayed. + + WAVE properties: + ---------------- + + Non-canonical header: Shows whether the WAVE header is canonical. A header + is considered canonical if it is 44 bytes long (this is the smallest possible + size that a WAVE header can be). + + Extra RIFF chunks: Shows whether there are any extra RIFF chunks following + the data chunk in the WAVE stream. + + Possible problems: + ------------------ + + File contains ID3v2 tag: Shows whether the file contains an ID3v2 tag, and + if so, how many bytes it occupies. This is not a problem for xmms-shn (it + was able to read the file after all), but could pose a problem for programs + that are not able to process files with ID3v2 tags. + + +Details tab: +============ + + This tab primarily shows the raw information taken from the WAVE header of the + given file. Each entry is self-explanatory. + + +Text file n tab(s): +=================== + + These tabs, if any, show the contents of all text files found in the same + directory or parent directory as the given file. The associated file name for + each tab is contained in the frame surrounding the text, and the full path to + the file being displayed is shown just above the text. You can control which + files are considered to be text files with the "Text file extensions" option in + the configuration window. Make sure to select the "Load text files in file + information box" option if you want to see these tabs. + + +-------------------- +Shorten 3.x overview +-------------------- + +Wayne Stielau has extended shorten 2.3 to support the creation of seek tables. +Seek tables are simply descriptions of the internal state of the shorten +algorithm at specific points in the decompression. This allows a modified +shorten decoder to restore the decoder state of the shorten algorithm for a +point at (or very close to) the desired seek point. You can get the latest +version at any of the URLs below: + + http://www.etree.org/shnutils/shorten/ + http://shnutils.freeshell.org/shorten/ + +Seek tables may be appended to the .shn itself, or stored in a separate file +(usually with a suffix of '.skt'). xmms-shn supports both of these options. + +The current implementation creates a seek table entry once every 25600 samples. +For 'CD-quality' WAVE data (44100 samples/sec), this provides a granularity of 1 +seek table entry per 25600/44100 ~= 0.58 seconds, more than what is needed by +either XMMS or WinAmp. + +At 80 bytes per seek table entry, you can expect the seek table to add about +0.1% to the size of the existing shortened file, for 'CD-quality' WAVE data. +So the seek table generated for 1 GB of already-shortened 'CD-quality' WAVE data +will fit on a floppy! Quite a deal, if you ask me. :-) + +Best of all, shorten 3.x is fully backward compatible with version 2.3, meaning +that any files created by shorten 3.x can be processed by version 2.3 with no +problems. + +The command line options for dealing with seek tables in shorten 3.x are: + + -e erase seek table appended to input file * + -i inquire as to whether a seek table is appended to input file * + -k append seek table information to existing shorten file + -s generate seek table information in separate file [input file].skt + -S[name] generate seek table information in separate file given by [name] + -v 3 format version number (2: no seek info; 3: default) * + + * only available in versions 3.2 and up + +By default, any file shortened with version 3.x will automatically have seek +tables appended to it. In later versions (3.2 and up), you can disable this +with the -v2 switch. + +** NOTE ON SEEKING IN FILES ENCODED WITH NON-DEFAULT OPTIONS ** + +Seek tables generated by shorten for files that were encoded with non-default +options do not seem to work correctly in most cases. Normal playback works +fine, but any attempt to seek within such a file may cause the audio to become +garbled. This appears to be due to an unforseen limitation in the design of +seek table generation in shorten - for example, as stated above, seek tables +were intended to be generated once every 25600 samples, but this is not true if +an alternate block size is given with the -b option. Encoding options that seem +to be able to cause this problem when set to non-default values are the -b, -m +and -p options. Since there is no reliable way to identify such files, the best +xmms-shn can do is attempt to seek based on the values in the seek tables. If +the sound becomes garbled, then you have likely run across one of these files, +and are out of luck. + + +---------- +Known bugs +---------- + +1. Not really a bug in xmms-shn, but under certain conditions, seeking may not + work correctly. The primary symptom of this is garbled audio after a seek is + performed. See the section above titled "NOTE ON SEEKING IN FILES ENCODED + WITH NON-DEFAULT OPTIONS" for more information. + + +I test this plugin aggressively until I can no longer make it crash. That does +not mean that there are no bugs. If you can crash it, I want to hear about it - +please report it to me at the address below. The xmms-shn webpage (see the +Availability section above) will always contain an up-to-date list of reported +bugs, so be sure to check that page before you send me a bug report. + +Also, feel free to send me any questions, comments, feature requests, patches... +I always enjoy getting feedback. + +Enjoy! + +Jason Jordan diff --git a/Libraries/Shorten/Files/shorten/include/bitshift.h b/Libraries/Shorten/Files/shorten/include/bitshift.h new file mode 100644 index 000000000..77e91b914 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/include/bitshift.h @@ -0,0 +1,33 @@ +char ulaw_maxshift[256] = {12,8,7,9,7,8,7,10,7,8,7,9,7,8,7,11,6,7,6,8,6,7,6,9,6,7,6,8,6,7,6,10,5,6,5,7,5,6,5,8,5,6,5,7,5,6,5,9,5,4,6,4,5,4,7,4,5,4,6,4,5,4,8,4,3,5,3,4,3,6,3,4,3,5,3,4,3,7,3,4,2,3,2,5,2,3,2,4,2,3,2,6,2,3,2,4,1,2,1,3,1,2,1,5,1,2,1,3,1,2,1,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,0,7,8,7,9,7,8,7,10,7,8,7,9,7,8,7,11,6,7,6,8,6,7,6,9,6,7,6,8,6,7,6,10,5,6,5,7,5,6,5,8,5,6,5,7,5,6,5,9,5,4,6,4,5,4,7,4,5,4,6,4,5,4,8,4,3,5,3,4,3,6,3,4,3,5,3,4,3,7,3,4,2,3,2,5,2,3,2,4,2,3,2,6,2,3,2,4,1,2,1,3,1,2,1,5,1,2,1,3,1,2,1,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,12}; + +schar ulaw_inward[13][256] = { +{-127,-126,-125,-124,-123,-122,-121,-120,-119,-118,-117,-116,-115,-114,-113,-112,-111,-110,-109,-108,-107,-106,-105,-104,-103,-102,-101,-100,-99,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,-128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}, +{-119,-118,-117,-116,-115,-114,-113,-112,-111,-110,-109,-108,-107,-106,-105,-104,-103,-102,-101,-100,-99,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-128,-7,-127,-6,-126,-5,-125,-4,-124,-3,-123,-2,-122,-1,-121,-120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,127,7,126,6,125,5,124,4,123,3,122,2,121,1,120,0}, +{-107,-106,-105,-104,-103,-102,-101,-100,-99,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-128,-11,-127,-10,-126,-9,-125,-8,-124,-7,-123,-6,-122,-5,-121,-4,-120,-119,-118,-3,-117,-116,-115,-2,-114,-113,-112,-1,-111,-110,-109,-108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,127,11,126,10,125,9,124,8,123,7,122,6,121,5,120,4,119,118,117,3,116,115,114,2,113,112,111,1,110,109,108,0}, +{-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-128,-13,-127,-12,-126,-11,-125,-10,-124,-9,-123,-8,-122,-7,-121,-6,-120,-119,-118,-5,-117,-116,-115,-4,-114,-113,-112,-3,-111,-110,-109,-2,-108,-107,-106,-105,-104,-103,-102,-1,-101,-100,-99,-98,-97,-96,-95,-94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,127,13,126,12,125,11,124,10,123,9,122,8,121,7,120,6,119,118,117,5,116,115,114,4,113,112,111,3,110,109,108,2,107,106,105,104,103,102,101,1,100,99,98,97,96,95,94,0}, +{-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-128,-14,-127,-13,-126,-12,-125,-11,-124,-10,-123,-9,-122,-8,-121,-7,-120,-119,-118,-6,-117,-116,-115,-5,-114,-113,-112,-4,-111,-110,-109,-3,-108,-107,-106,-105,-104,-103,-102,-2,-101,-100,-99,-98,-97,-96,-95,-1,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,127,14,126,13,125,12,124,11,123,10,122,9,121,8,120,7,119,118,117,6,116,115,114,5,113,112,111,4,110,109,108,3,107,106,105,104,103,102,101,2,100,99,98,97,96,95,94,1,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,0}, +{-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-128,-14,-127,-13,-126,-12,-125,-11,-124,-10,-123,-9,-122,-8,-121,-120,-7,-119,-118,-117,-6,-116,-115,-114,-5,-113,-112,-111,-4,-110,-109,-108,-107,-106,-3,-105,-104,-103,-102,-101,-100,-99,-2,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-1,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,127,14,126,13,125,12,124,11,123,10,122,9,121,8,120,119,7,118,117,116,6,115,114,113,5,112,111,110,4,109,108,107,106,105,3,104,103,102,101,100,99,98,2,97,96,95,94,93,92,91,90,89,88,87,1,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,0}, +{-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-128,-15,-127,-14,-126,-13,-125,-12,-124,-11,-123,-10,-122,-9,-121,-8,-120,-119,-7,-118,-117,-116,-6,-115,-114,-113,-5,-112,-111,-110,-4,-109,-108,-107,-106,-105,-104,-3,-103,-102,-101,-100,-99,-98,-97,-2,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-1,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,127,15,126,14,125,13,124,12,123,11,122,10,121,9,120,8,119,118,7,117,116,115,6,114,113,112,5,111,110,109,4,108,107,106,105,104,103,3,102,101,100,99,98,97,96,2,95,94,93,92,91,90,89,88,87,86,85,84,83,1,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,0}, +{-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-128,-15,-127,-14,-126,-13,-125,-12,-124,-11,-123,-10,-122,-9,-121,-8,-120,-119,-118,-7,-117,-116,-115,-6,-114,-113,-112,-5,-111,-110,-109,-4,-108,-107,-106,-105,-104,-103,-3,-102,-101,-100,-99,-98,-97,-96,-2,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-1,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,127,15,126,14,125,13,124,12,123,11,122,10,121,9,120,8,119,118,117,7,116,115,114,6,113,112,111,5,110,109,108,4,107,106,105,104,103,102,3,101,100,99,98,97,96,95,2,94,93,92,91,90,89,88,87,86,85,84,83,82,81,1,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,0}, +{-16,-15,-128,-14,-127,-13,-126,-12,-125,-11,-124,-10,-123,-9,-122,-8,-121,-120,-119,-7,-118,-117,-116,-6,-115,-114,-113,-5,-112,-111,-110,-4,-109,-108,-107,-106,-105,-104,-103,-3,-102,-101,-100,-99,-98,-97,-96,-2,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-1,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,127,15,126,14,125,13,124,12,123,11,122,10,121,9,120,8,119,118,117,7,116,115,114,6,113,112,111,5,110,109,108,4,107,106,105,104,103,102,101,3,100,99,98,97,96,95,94,2,93,92,91,90,89,88,87,86,85,84,83,82,81,80,1,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,0}, +{-8,-128,-127,-7,-126,-125,-124,-6,-123,-122,-121,-5,-120,-119,-118,-4,-117,-116,-115,-114,-113,-112,-111,-3,-110,-109,-108,-107,-106,-105,-104,-2,-103,-102,-101,-100,-99,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-1,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,127,126,125,7,124,123,122,6,121,120,119,5,118,117,116,4,115,114,113,112,111,110,109,3,108,107,106,105,104,103,102,2,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,1,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,0}, +{-4,-128,-127,-126,-125,-124,-123,-3,-122,-121,-120,-119,-118,-117,-116,-2,-115,-114,-113,-112,-111,-110,-109,-108,-107,-106,-105,-104,-103,-102,-101,-1,-100,-99,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,127,126,125,124,123,122,121,3,120,119,118,117,116,115,114,2,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,1,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,0}, +{-2,-128,-127,-126,-125,-124,-123,-122,-121,-120,-119,-118,-117,-116,-115,-1,-114,-113,-112,-111,-110,-109,-108,-107,-106,-105,-104,-103,-102,-101,-100,-99,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,1,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,0}, +{-1,-128,-127,-126,-125,-124,-123,-122,-121,-120,-119,-118,-117,-116,-115,-114,-113,-112,-111,-110,-109,-108,-107,-106,-105,-104,-103,-102,-101,-100,-99,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78,-77,-76,-75,-74,-73,-72,-71,-70,-69,-68,-67,-66,-65,-64,-63,-62,-61,-60,-59,-58,-57,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0} +}; + +uchar ulaw_outward[13][256] = { +{127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128}, +{112,114,116,118,120,122,124,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,113,115,117,119,121,123,125,255,253,251,249,247,245,243,241,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,252,250,248,246,244,242,240}, +{96,98,100,102,104,106,108,110,112,113,114,116,117,118,120,121,122,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,97,99,101,103,105,107,109,111,115,119,123,255,251,247,243,239,237,235,233,231,229,227,225,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,250,249,248,246,245,244,242,241,240,238,236,234,232,230,228,226,224}, +{80,82,84,86,88,90,92,94,96,97,98,100,101,102,104,105,106,108,109,110,112,113,114,115,116,117,118,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,81,83,85,87,89,91,93,95,99,103,107,111,119,255,247,239,235,231,227,223,221,219,217,215,213,211,209,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,246,245,244,243,242,241,240,238,237,236,234,233,232,230,229,228,226,225,224,222,220,218,216,214,212,210,208}, +{64,66,68,70,72,74,76,78,80,81,82,84,85,86,88,89,90,92,93,94,96,97,98,99,100,101,102,104,105,106,107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,65,67,69,71,73,75,77,79,83,87,91,95,103,111,255,239,231,223,219,215,211,207,205,203,201,199,197,195,193,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,238,237,236,235,234,233,232,230,229,228,227,226,225,224,222,221,220,218,217,216,214,213,212,210,209,208,206,204,202,200,198,196,194,192}, +{49,51,53,55,57,59,61,63,64,66,67,68,70,71,72,74,75,76,78,79,80,81,82,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,100,101,102,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,52,54,56,58,60,62,65,69,73,77,83,91,103,255,231,219,211,205,201,197,193,190,188,186,184,182,180,178,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,230,229,228,227,226,225,224,223,222,221,220,218,217,216,215,214,213,212,210,209,208,207,206,204,203,202,200,199,198,196,195,194,192,191,189,187,185,183,181,179,177}, +{32,34,36,38,40,42,44,46,48,49,51,52,53,55,56,57,59,60,61,63,64,65,66,67,68,70,71,72,73,74,75,76,78,79,80,81,82,83,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,35,37,39,41,43,45,47,50,54,58,62,69,77,91,255,219,205,197,190,186,182,178,175,173,171,169,167,165,163,161,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,218,217,216,215,214,213,212,211,210,209,208,207,206,204,203,202,201,200,199,198,196,195,194,193,192,191,189,188,187,185,184,183,181,180,179,177,176,174,172,170,168,166,164,162,160}, +{16,18,20,22,24,26,28,30,32,33,34,36,37,38,40,41,42,44,45,46,48,49,50,51,52,53,55,56,57,58,59,60,61,63,64,65,66,67,68,69,70,71,72,73,74,75,76,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,19,21,23,25,27,29,31,35,39,43,47,54,62,77,255,205,190,182,175,171,167,163,159,157,155,153,151,149,147,145,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,204,203,202,201,200,199,198,197,196,195,194,193,192,191,189,188,187,186,185,184,183,181,180,179,178,177,176,174,173,172,170,169,168,166,165,164,162,161,160,158,156,154,152,150,148,146,144}, +{2,4,6,8,10,12,14,16,17,18,20,21,22,24,25,26,28,29,30,32,33,34,35,36,37,38,40,41,42,43,44,45,46,48,49,50,51,52,53,54,55,56,57,58,59,60,61,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,3,5,7,9,11,13,15,19,23,27,31,39,47,62,255,190,175,167,159,155,151,147,143,141,139,137,135,133,131,129,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,189,188,187,186,185,184,183,182,181,180,179,178,177,176,174,173,172,171,170,169,168,166,165,164,163,162,161,160,158,157,156,154,153,152,150,149,148,146,145,144,142,140,138,136,134,132,130,128}, +{1,2,4,5,6,8,9,10,12,13,14,16,17,18,19,20,21,22,24,25,26,27,28,29,30,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,3,7,11,15,23,31,47,255,175,159,151,143,139,135,131,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,158,157,156,155,154,153,152,150,149,148,147,146,145,144,142,141,140,138,137,136,134,133,132,130,129,128}, +{1,2,3,4,5,6,8,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,7,15,31,255,159,143,135,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,142,141,140,139,138,137,136,134,133,132,131,130,129,128}, +{1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,15,255,143,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128}, +{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128} +}; diff --git a/Libraries/Shorten/Files/shorten/include/config.h b/Libraries/Shorten/Files/shorten/include/config.h new file mode 100644 index 000000000..82a91ca84 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/include/config.h @@ -0,0 +1,96 @@ +/* shorten/include/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `closedir' function. */ +#define HAVE_CLOSEDIR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `opendir' function. */ +#define HAVE_OPENDIR 1 + +/* Define to 1 if you have the `readdir' function. */ +#define HAVE_READDIR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Name of package */ +#define PACKAGE "libshorten" + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if the C compiler supports function prototypes. */ +#undef PROTOTYPES + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define to 1 if the `setvbuf' function takes the buffering type as its + second argument and the buffer pointer as the third, as on System V before + release 3. */ +#undef SETVBUF_REVERSED + +/* The size of a `unsigned long', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_LONG + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "unknown-cog" + +/* Define like PROTOTYPES; this can be used by system headers. */ +#undef __PROTOTYPES diff --git a/Libraries/Shorten/Files/shorten/include/config.h.in b/Libraries/Shorten/Files/shorten/include/config.h.in new file mode 100644 index 000000000..de761ce1b --- /dev/null +++ b/Libraries/Shorten/Files/shorten/include/config.h.in @@ -0,0 +1,96 @@ +/* shorten/include/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `closedir' function. */ +#undef HAVE_CLOSEDIR + +/* Define to 1 if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `m' library (-lm). */ +#undef HAVE_LIBM + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `opendir' function. */ +#undef HAVE_OPENDIR + +/* Define to 1 if you have the `readdir' function. */ +#undef HAVE_READDIR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if the C compiler supports function prototypes. */ +#undef PROTOTYPES + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define to 1 if the `setvbuf' function takes the buffering type as its + second argument and the buffer pointer as the third, as on System V before + release 3. */ +#undef SETVBUF_REVERSED + +/* The size of a `unsigned long', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_LONG + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define like PROTOTYPES; this can be used by system headers. */ +#undef __PROTOTYPES diff --git a/Libraries/Shorten/Files/shorten/include/decode.h b/Libraries/Shorten/Files/shorten/include/decode.h new file mode 100644 index 000000000..fca7aa4cc --- /dev/null +++ b/Libraries/Shorten/Files/shorten/include/decode.h @@ -0,0 +1,45 @@ +#ifndef _DECODE_H +#define _DECODE_H + +#include "shorten.h" +#include "shn.h" + +#define NUM_DEFAULT_BUFFER_BLOCKS 512L + +#ifndef _SHN_CONFIG +#define _SHN_CONFIG + +/* First fill out a shn_config struct... */ +typedef struct _shn_config +{ + int error_output_method; + char *seek_tables_path; + char *relative_seek_tables_path; + int verbose; + int swap_bytes; +} shn_config; +#endif + +/* ... then you can load a file, normally you have to use the functions in this order */ +shn_file *shn_load(char *filename, shn_config config); /* Loads the file in filename and uses config... returns a shn_file context */ +int shn_init_decoder(shn_file *this_shn); /* inits the decoder for this_shn necessary to do shn_read() */ + +/* shn_get_buffer_block_size() returns the minimal size that read_buffer should have * + * blocks should be around 512 * + * You have to allocate a buffer with the size returned by shn_get_buffer_block_size() yourself */ +int shn_get_buffer_block_size(shn_file *this_shn, int blocks); +unsigned int shn_get_song_length(shn_file *this_shn); /* returns song length in milliseconds */ +unsigned int shn_get_samplerate(shn_file *this_shn); /* returns the number of samples per second */ +unsigned int shn_get_channels(shn_file *this_shn); /* returns the number of channels of the audio file */ +unsigned int shn_get_bitspersample(shn_file *this_shn); /* returns the number of bits per sample */ + +/* Play with the shorten file */ +int shn_read(shn_file *this_shn, uchar *read_buffer, int bytes_to_read); /* bytes_to_read should be the size returned by shn_get_buffer_block_size */ +int shn_seekable(shn_file *this_shn); /* Returns 1 if file is seekables (has seek tables) otherwise 0 */ +int shn_seek(shn_file *this_shn, unsigned int time); /* Seek to position "time" in seconds */ + +/* Unload everything */ +int shn_cleanup_decoder(shn_file *this_shn); /* Frees some buffers */ +void shn_unload(shn_file *this_shn); /* Unloads the file */ + +#endif diff --git a/Libraries/Shorten/Files/shorten/include/shn.h b/Libraries/Shorten/Files/shorten/include/shn.h new file mode 100644 index 000000000..f9e2b4c0c --- /dev/null +++ b/Libraries/Shorten/Files/shorten/include/shn.h @@ -0,0 +1,284 @@ +/* xmms-shn - a shorten (.shn) plugin for XMMS + * Copyright (C) 2000-2004 Jason Jordan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * $Id$ + */ + +#ifndef _SHN_H +#define _SHN_H + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_STRERROR +extern char *sys_errlist[]; +#define strerror(x) sys_errlist[x] +#endif + +#ifdef HAVE_VSNPRINTF +#define shn_vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#else +#define shn_vsnprintf(a,b,c,d) vsprintf(a,c,d) +#endif + +#define min(a,b) (((a)<(b))?(a):(b)) + +/* surely no headers will be this large. right? RIGHT? */ +#define OUT_BUFFER_SIZE 16384 + +#define BUF_SIZE 4096 + +#define ERROR_OUTPUT_DEVNULL 0 +#define ERROR_OUTPUT_STDERR 1 +#define ERROR_OUTPUT_WINDOW 2 + +#define SEEK_SUFFIX "skt" + +#define NO_SEEK_TABLE -1 + +#define SEEK_HEADER_SIGNATURE "SEEK" +#define SEEK_TRAILER_SIGNATURE "SHNAMPSK" + +#define SEEK_HEADER_SIZE 12 +#define SEEK_TRAILER_SIZE 12 +#define SEEK_ENTRY_SIZE 80 +#define SEEK_RESOLUTION 25600 + +#define WAVE_RIFF (0x46464952) /* 'RIFF' in little-endian */ +#define WAVE_WAVE (0x45564157) /* 'WAVE' in little-endian */ +#define WAVE_FMT (0x20746d66) /* ' fmt' in little-endian */ +#define WAVE_DATA (0x61746164) /* 'data' in little-endian */ + +#define AIFF_FORM (0x4D524F46) /* 'FORM' in little-endian */ + +#define WAVE_FORMAT_UNKNOWN (0x0000) +#define WAVE_FORMAT_PCM (0x0001) +#define WAVE_FORMAT_ADPCM (0x0002) +#define WAVE_FORMAT_IEEE_FLOAT (0x0003) +#define WAVE_FORMAT_ALAW (0x0006) +#define WAVE_FORMAT_MULAW (0x0007) +#define WAVE_FORMAT_OKI_ADPCM (0x0010) +#define WAVE_FORMAT_IMA_ADPCM (0x0011) +#define WAVE_FORMAT_DIGISTD (0x0015) +#define WAVE_FORMAT_DIGIFIX (0x0016) +#define WAVE_FORMAT_DOLBY_AC2 (0x0030) +#define WAVE_FORMAT_GSM610 (0x0031) +#define WAVE_FORMAT_ROCKWELL_ADPCM (0x003b) +#define WAVE_FORMAT_ROCKWELL_DIGITALK (0x003c) +#define WAVE_FORMAT_G721_ADPCM (0x0040) +#define WAVE_FORMAT_G728_CELP (0x0041) +#define WAVE_FORMAT_MPEG (0x0050) +#define WAVE_FORMAT_MPEGLAYER3 (0x0055) +#define WAVE_FORMAT_G726_ADPCM (0x0064) +#define WAVE_FORMAT_G722_ADPCM (0x0065) + +#define CD_BLOCK_SIZE (2352) +#define CD_BLOCKS_PER_SEC (75) +#define CD_MIN_BURNABLE_SIZE (705600) +#define CD_CHANNELS (2) +#define CD_SAMPLES_PER_SEC (44100) +#define CD_BITS_PER_SAMPLE (16) +#define CD_RATE (176400) + +#define CANONICAL_HEADER_SIZE (44) + +#define PROBLEM_NOT_CD_QUALITY (0x00000001) +#define PROBLEM_CD_BUT_BAD_BOUND (0x00000002) +#define PROBLEM_CD_BUT_TOO_SHORT (0x00000004) +#define PROBLEM_HEADER_NOT_CANONICAL (0x00000008) +#define PROBLEM_EXTRA_CHUNKS (0x00000010) +#define PROBLEM_HEADER_INCONSISTENT (0x00000020) + +#define PROB_NOT_CD(f) ((f.problems) & (PROBLEM_NOT_CD_QUALITY)) +#define PROB_BAD_BOUND(f) ((f.problems) & (PROBLEM_CD_BUT_BAD_BOUND)) +#define PROB_TOO_SHORT(f) ((f.problems) & (PROBLEM_CD_BUT_TOO_SHORT)) +#define PROB_HDR_NOT_CANONICAL(f) ((f.problems) & (PROBLEM_HEADER_NOT_CANONICAL)) +#define PROB_EXTRA_CHUNKS(f) ((f.problems) & (PROBLEM_EXTRA_CHUNKS)) +#define PROB_HDR_INCONSISTENT(f) ((f.problems) & (PROBLEM_HEADER_INCONSISTENT)) + +#ifndef _SHN_CONFIG +#define _SHN_CONFIG +typedef struct _shn_config +{ + int error_output_method; + char *seek_tables_path; + char *relative_seek_tables_path; + int verbose; + int swap_bytes; +} shn_config; +#endif + +typedef struct _shn_decode_state +{ + uchar *getbuf; + uchar *getbufp; + int nbitget; + int nbyteget; + ulong gbuffer; + schar *writebuf; + schar *writefub; + int nwritebuf; +} shn_decode_state; + +typedef struct _shn_seek_header +{ + uchar data[SEEK_HEADER_SIZE]; + slong version; + ulong shnFileSize; +} shn_seek_header; + +typedef struct _shn_seek_trailer +{ + uchar data[SEEK_TRAILER_SIZE]; + ulong seekTableSize; +} shn_seek_trailer; + +typedef struct _shn_seek_entry +{ + uchar data[SEEK_ENTRY_SIZE]; +} shn_seek_entry; + +/* old way, kept for reference. + (changed because some compilers apparently don't support #pragma pack(1)) + +typedef struct _shn_seek_header +{ + char signature[4]; + unsigned long version; + unsigned long shnFileSize; +} shn_seek_header; + +typedef struct _shn_seek_trailer +{ + unsigned long seekTableSize; + char signature[8]; +} shn_seek_trailer; + +typedef struct _shn_seek_entry +{ + unsigned long shnSample; + unsigned long shnByteOffset; + unsigned long shnLastPosition; + unsigned short shnByteGet; + unsigned short shnBufferOffset; + unsigned short shnBitOffset; + unsigned long shnGBuffer; + unsigned short shnBitShift; + long cbuf0[3]; + long cbuf1[3]; + long offset0[4]; + long offset1[4]; +} shn_seek_entry; +*/ + +typedef struct _shn_wave_header +{ + char *filename, + m_ss[16]; + + uint header_size; + + ushort channels, + block_align, + bits_per_sample, + wave_format; + + ulong samples_per_sec, + avg_bytes_per_sec, + rate, + length, + data_size, + total_size, + chunk_size, + actual_size; + + double exact_length; + + int file_has_id3v2_tag; + long id3v2_tag_size; + + ulong problems; +} shn_wave_header; + +typedef struct _shn_vars +{ + FILE *fd; + int seek_to; + int eof; + int going; + slong seek_table_entries; + ulong seek_resolution; + int bytes_in_buf; + uchar buffer[OUT_BUFFER_SIZE]; + int bytes_in_header; + uchar header[OUT_BUFFER_SIZE]; + int fatal_error; + schar fatal_error_msg[BUF_SIZE]; + int reading_function_code; + ulong last_file_position; + ulong last_file_position_no_really; + ulong initial_file_position; + ulong bytes_read; + unsigned short bitshift; + long seek_offset; +} shn_vars; + +typedef struct _shn_file +{ + shn_vars vars; + shn_decode_state *decode_state; + shn_wave_header wave_header; + shn_seek_header seek_header; + shn_seek_trailer seek_trailer; + shn_seek_entry *seek_table; + shn_config config; +} shn_file; + +extern shn_seek_entry *shn_seek_entry_search(shn_config,shn_seek_entry *,ulong,ulong,ulong,ulong); +extern void shn_load_seek_table(shn_file *,char *); +extern void shn_unload(shn_file *); +extern void shn_display_about(void); +extern void shn_display_configure(void); +extern void shn_display_info(shn_file *); +extern int shn_verify_header(shn_file *); +extern int shn_filename_contains_a_dot(char *); +extern char *shn_get_base_filename(char *); +extern char *shn_get_base_directory(char *); +extern void shn_length_to_str(shn_file *); +extern ulong shn_uchar_to_ulong_le(uchar *); +extern slong shn_uchar_to_slong_le(uchar *); +extern ushort shn_uchar_to_ushort_le(uchar *); +extern char *shn_format_to_str(ushort); +extern void shn_debug(shn_config,char *, ...); +extern void shn_error(shn_config,char *, ...); +extern void shn_error_fatal(shn_file *,char *, ...); +extern void shn_snprintf(char *,int,char *, ...); +extern FILE *shn_open_and_discard_id3v2_tag(char *,int *,long *); + +#endif diff --git a/Libraries/Shorten/Files/shorten/include/shorten.h b/Libraries/Shorten/Files/shorten/include/shorten.h new file mode 100644 index 000000000..2416a7d94 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/include/shorten.h @@ -0,0 +1,220 @@ +/****************************************************************************** +* * +* Copyright (C) 1992-1995 Tony Robinson * +* * +* See the file doc/LICENSE.shorten for conditions on distribution and usage * +* * +******************************************************************************/ + +/* + * $Id$ + */ + +#ifndef _SHORTEN_H +#define _SHORTEN_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef HAVE_INTTYPES_H +# include +#else +# if SIZEOF_UNSIGNED_LONG == 4 +# define uint32_t unsigned long +# define int32_t long +# else +# define uint32_t unsigned int +# define int32_t int +# endif +# define uint16_t unsigned short +# define uint8_t unsigned char +# define int16_t short +# define int8_t char +#endif + +#undef ulong +#undef ushort +#undef uchar +#undef slong +#undef sshort +#undef schar +#define ulong uint32_t +#define ushort uint16_t +#define uchar uint8_t +#define slong int32_t +#define sshort int16_t +#define schar int8_t + +#include "shn.h" + +#define MAGIC "ajkg" +#define FORMAT_VERSION 2 +#define MIN_SUPPORTED_VERSION 1 +#define MAX_SUPPORTED_VERSION 3 +#define MAX_VERSION 7 + +#define UNDEFINED_UINT -1 +#define DEFAULT_BLOCK_SIZE 256 +#define DEFAULT_V0NMEAN 0 +#define DEFAULT_V2NMEAN 4 +#define DEFAULT_MAXNLPC 0 +#define DEFAULT_NCHAN 1 +#define DEFAULT_NSKIP 0 +#define DEFAULT_NDISCARD 0 +#define NBITPERLONG 32 +#define DEFAULT_MINSNR 256 +#define DEFAULT_MAXRESNSTR "32.0" +#define DEFAULT_QUANTERROR 0 +#define MINBITRATE 2.5 + +#define MAX_LPC_ORDER 64 +#define CHANSIZE 0 +#define ENERGYSIZE 3 +#define BITSHIFTSIZE 2 +#define NWRAP 3 + +#define FNSIZE 2 +#define FN_DIFF0 0 +#define FN_DIFF1 1 +#define FN_DIFF2 2 +#define FN_DIFF3 3 +#define FN_QUIT 4 +#define FN_BLOCKSIZE 5 +#define FN_BITSHIFT 6 +#define FN_QLPC 7 +#define FN_ZERO 8 +#define FN_VERBATIM 9 + +#define VERBATIM_CKSIZE_SIZE 5 /* a var_put code size */ +#define VERBATIM_BYTE_SIZE 8 /* code size 8 on single bytes means + * no compression at all */ +#define VERBATIM_CHUNK_MAX 256 /* max. size of a FN_VERBATIM chunk */ + +#define ULONGSIZE 2 +#define NSKIPSIZE 1 +#define LPCQSIZE 2 +#define LPCQUANT 5 +#define XBYTESIZE 7 + +#define TYPESIZE 4 +#define TYPE_AU1 0 /* original lossless ulaw */ +#define TYPE_S8 1 /* signed 8 bit characters */ +#define TYPE_U8 2 /* unsigned 8 bit characters */ +#define TYPE_S16HL 3 /* signed 16 bit shorts: high-low */ +#define TYPE_U16HL 4 /* unsigned 16 bit shorts: high-low */ +#define TYPE_S16LH 5 /* signed 16 bit shorts: low-high */ +#define TYPE_U16LH 6 /* unsigned 16 bit shorts: low-high */ +#define TYPE_ULAW 7 /* lossy ulaw: internal conversion to linear */ +#define TYPE_AU2 8 /* new ulaw with zero mapping */ +#define TYPE_AU3 9 /* lossless alaw */ +#define TYPE_ALAW 10 /* lossy alaw: internal conversion to linear */ +#define TYPE_RIFF_WAVE 11 /* Microsoft .WAV files */ +#define TYPE_EOF 12 +#define TYPE_GENERIC_ULAW 128 +#define TYPE_GENERIC_ALAW 129 + +#define POSITIVE_ULAW_ZERO 0xff +#define NEGATIVE_ULAW_ZERO 0x7f + +#ifndef MAX_PATH +#define MAX_PATH 2048 +#endif + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +#if defined(unix) && !defined(linux) +#define labs abs +#endif + +#define ROUNDEDSHIFTDOWN(x, n) (((n) == 0) ? (x) : ((x) >> ((n) - 1)) >> 1) + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* BUFSIZ must be a multiple of four to contain a whole number of words */ +#ifdef BUFSIZ +#undef BUFSIZ +#endif + +#define BUFSIZ 512 + +#define V2LPCQOFFSET (1 << LPCQUANT); + +#define UINT_GET(nbit, file) \ + ((version == 0) ? uvar_get(nbit, file) : ulong_get(file)) + +#define putc_exit(val, stream)\ +{ char rval;\ + if((rval = putc((val), (stream))) != (char) (val))\ + complain("FATALERROR: write failed: putc returns EOF");\ +} + +extern int getc_exit_val; +#define getc_exit(stream)\ +(((getc_exit_val = getc(stream)) == EOF) ? \ + complain("FATALERROR: read failed: getc returns EOF"), 0: getc_exit_val) + +/************************/ +/* defined in shorten.c */ +extern void init_offset(slong**, int, int, int); +extern int shorten(FILE*, FILE*, int, char**); + +/**************************/ +/* defined in Sulawalaw.c */ +extern int Sulaw2lineartab[]; +#define Sulaw2linear(i) (Sulaw2lineartab[i]) +#ifndef Sulaw2linear +extern int Sulaw2linear(uchar); +#endif +extern uchar Slinear2ulaw(int); + +extern int Salaw2lineartab[]; +#define Salaw2linear(i) (Salaw2lineartab[i]) +#ifndef Salaw2linear +extern int Salaw2linear(uchar); +#endif +extern uchar Slinear2alaw(int); + +/**********************/ +/* defined in fixio.c */ +extern void init_sizeof_sample(void); +extern void fwrite_type_init(shn_file*); +extern void fwrite_type(slong**,int,int,int,shn_file*); +extern void fwrite_type_quit(shn_file*); +extern void fix_bitshift(slong*, int, int, int); + +/**********************/ +/* defined in vario.c */ +extern void var_get_init(shn_file*); +extern slong uvar_get(int, shn_file*); +extern slong var_get(int, shn_file*); +extern ulong ulong_get(shn_file*); +extern void var_get_quit(shn_file*); + +extern int sizeof_uvar(ulong, int); +extern int sizeof_var(slong, int); + +extern void mkmasktab(void); +extern ulong word_get(shn_file*); + +/**********************/ +/* defined in array.c */ +extern void* pmalloc(ulong, shn_file*); +extern slong** long2d(ulong, ulong, shn_file*); + +#endif diff --git a/Libraries/Shorten/Files/shorten/src/Makefile.am b/Libraries/Shorten/Files/shorten/src/Makefile.am new file mode 100644 index 000000000..49d81c475 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/Makefile.am @@ -0,0 +1,21 @@ +noinst_LTLIBRARIES = libshorten.la +BUILT_SOURCES = bitshift.h + +libshorten_la_LDFLAGS = -module -export-dynamic -avoid-version +libshorten_la_SOURCES = array.c convert.c fixio.c id3v2.c misc.c output.c shorten.c seek.c sulawalaw.c vario.c wave.c decode.c + +INCLUDES = -I$(top_srcdir)/include + +CLEANFILES = $(BUILT_SOURCES) + +array.c: $(BUILT_SOURCES) + +bitshift.h: + -@if [ -x "../util/mkbshift" ] ; then \ + ../util/mkbshift ; \ + else \ + echo "" ; \ + echo "*** missing ../util/mkbshift - run '$(MAKE)' in the top-level directory to build it ***" ; \ + echo "" ; \ + exit 1 ; \ + fi diff --git a/Libraries/Shorten/Files/shorten/src/array.c b/Libraries/Shorten/Files/shorten/src/array.c new file mode 100644 index 000000000..d0b5d2327 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/array.c @@ -0,0 +1,40 @@ +/****************************************************************************** +* * +* Copyright (C) 1992-1995 Tony Robinson * +* * +* See the file doc/LICENSE.shorten for conditions on distribution and usage * +* * +******************************************************************************/ + +/* + * $Id$ + */ + +#include +#include +#include "shorten.h" + +void *pmalloc(ulong size, shn_file *this_shn) { + void *ptr; + + ptr = malloc(size); + + if(ptr == NULL) + shn_error_fatal(this_shn,"Call to malloc(%ld) failed in pmalloc() -\nyour system may be low on memory", size); + + return(ptr); +} + +slong **long2d(ulong n0, ulong n1, shn_file *this_shn) { + slong **array0 = NULL; + + if((array0 = (slong**) pmalloc((ulong) (n0 * sizeof(slong*) + + n0 * n1 * sizeof(slong)),this_shn)) != NULL ) { + slong *array1 = (slong*) (array0 + n0); + int i; + + for(i = 0; i < n0; i++) + array0[i] = array1 + i * n1; + } + return(array0); +} diff --git a/Libraries/Shorten/Files/shorten/src/convert.c b/Libraries/Shorten/Files/shorten/src/convert.c new file mode 100644 index 000000000..876e6bc03 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/convert.c @@ -0,0 +1,42 @@ +/* convert.c - functions to convert little-endian data to endian of host + * Copyright (C) 2000-2004 Jason Jordan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * $Id$ + */ + +#include +#include "shorten.h" + +ulong shn_uchar_to_ulong_le(uchar *buf) +/* converts 4 bytes stored in little-endian format to a ulong */ +{ + return (ulong)((buf[3] << 24) + (buf[2] << 16) + (buf[1] << 8) + buf[0]); +} + +slong shn_uchar_to_slong_le(uchar *buf) +/* converts 4 bytes stored in little-endian format to an slong */ +{ + return (slong)shn_uchar_to_ulong_le(buf); +} + +ushort shn_uchar_to_ushort_le(uchar *buf) +/* converts 4 bytes stored in little-endian format to a ushort */ +{ + return (ushort)((buf[1] << 8) + buf[0]); +} diff --git a/Libraries/Shorten/Files/shorten/src/decode.c b/Libraries/Shorten/Files/shorten/src/decode.c new file mode 100644 index 000000000..ea701f295 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/decode.c @@ -0,0 +1,1154 @@ +#include +#include +#include +#include +#include + +#include "decode.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +int shn_seek(shn_file *this_shn, unsigned int time); +int shn_seekable(shn_file *this_shn); +shn_file *shn_load(char *filename, shn_config config); +int shn_init_decoder(shn_file *this_shn); +int shn_cleanup_decoder(shn_file *this_shn); +unsigned int shn_get_song_length(shn_file *this_shn); +int shn_get_buffer_block_size(shn_file *this_shn, int blocks); +int shn_read(shn_file *this_shn, uchar *read_buffer, int bytes_to_read); +void shn_unload(shn_file *this_shn); +unsigned int shn_get_samplerate(shn_file *this_shn); +unsigned int shn_get_channels(shn_file *this_shn); +unsigned int shn_get_bitspersample(shn_file *this_shn); +static void swap_bytes(shn_file *this_shn,int bytes); +static int get_wave_header(shn_file *this_shn); +static int shn_init_decode_state(shn_file *this_shn); +static int write_to_buffer(shn_file *this_shn, uchar *read_buffer, int bytes_to_read); + +static int buffer_is_full = 0; +static int buffer_ret = 0; + +static slong **buffer = NULL, **offset = NULL; +static slong lpcqoffset = 0; +static int version = FORMAT_VERSION, bitshift = 0; +static int ftype = TYPE_EOF; +static char *magic = MAGIC; +static int blocksize = DEFAULT_BLOCK_SIZE, nchan = DEFAULT_NCHAN; +static int i, chan, nwrap, nskip = DEFAULT_NSKIP; +static int *qlpc = NULL, maxnlpc = DEFAULT_MAXNLPC, nmean = UNDEFINED_UINT; +static int cmd; +static int internal_ftype; +static int cklen; +static uchar tmp; +static ulong seekto_offset; + +int shn_seekable(shn_file *this_shn) { + if(!this_shn) + return 0; + + if(this_shn->vars.seek_table_entries == NO_SEEK_TABLE) { + shn_debug(this_shn->config, "File not seekable"); + return 0; + } + + /* File is seekable */ + return 1; +} + +int shn_seek(shn_file *this_shn, unsigned int time) +{ + if (NULL == this_shn) + return 0; + + if (this_shn->vars.seek_table_entries == NO_SEEK_TABLE) + { + shn_error(this_shn->config, "Cannot seek to %d:%02d because there is no seek information for this file.",time/60,time%60); + return 0; + } + + if(time > (unsigned int)(shn_get_song_length(this_shn) / 1000)) { + shn_error(this_shn->config, "You cannot seek to this position! It is out of range!"); + return 0; + } + this_shn->vars.seek_to = time; + + this_shn->vars.eof = FALSE; + + return 1; +} + +static void swap_bytes(shn_file *this_shn,int bytes) +{ + int i; + uchar tmp; + + for (i=0;ivars.buffer[i+1]; + this_shn->vars.buffer[i+1] = this_shn->vars.buffer[i]; + this_shn->vars.buffer[i] = tmp; + } +} + +static int get_wave_header(shn_file *this_shn) +{ + if(!this_shn) + return 0; + slong **buffer = NULL, **offset = NULL; + slong lpcqoffset = 0; + int version = FORMAT_VERSION, bitshift = 0; + int ftype = TYPE_EOF; + char *magic = MAGIC; + int blocksize = DEFAULT_BLOCK_SIZE, nchan = DEFAULT_NCHAN; + int i, chan, nwrap, nskip = DEFAULT_NSKIP; + int *qlpc = NULL, maxnlpc = DEFAULT_MAXNLPC, nmean = UNDEFINED_UINT; + int cmd; + int internal_ftype; + int cklen; + int retval = 0; + + if (!shn_init_decode_state(this_shn)) + return 0; + + /***********************/ + /* EXTRACT starts here */ + /***********************/ + + /* read magic number */ +#ifdef STRICT_FORMAT_COMPATABILITY + if(FORMAT_VERSION < 2) + { + for(i = 0; i < strlen(magic); i++) { + if(getc_exit(this_shn->vars.fd) != magic[i]) + return 0; + this_shn->vars.bytes_read++; + } + + /* get version number */ + version = getc_exit(this_shn->vars.fd); + this_shn->vars.bytes_read++; + } + else +#endif /* STRICT_FORMAT_COMPATABILITY */ + { + int nscan = 0; + + version = MAX_VERSION + 1; + while(version > MAX_VERSION) + { + int byte = getc(this_shn->vars.fd); + this_shn->vars.bytes_read++; + if(byte == EOF) + return 0; + if(magic[nscan] != '\0' && byte == magic[nscan]) + nscan++; + else + if(magic[nscan] == '\0' && byte <= MAX_VERSION) + version = byte; + else + { + if(byte == magic[0]) + nscan = 1; + else + { + nscan = 0; + } + version = MAX_VERSION + 1; + } + } + } + + /* check version number */ + if(version > MAX_SUPPORTED_VERSION) + return 0; + + /* set up the default nmean, ignoring the command line state */ + nmean = (version < 2) ? DEFAULT_V0NMEAN : DEFAULT_V2NMEAN; + + /* initialise the variable length file read for the compressed stream */ + var_get_init(this_shn); + if (this_shn->vars.fatal_error) + return 0; + + /* initialise the fixed length file write for the uncompressed stream */ + fwrite_type_init(this_shn); + + /* get the internal file type */ + internal_ftype = UINT_GET(TYPESIZE, this_shn); + + /* has the user requested a change in file type? */ + if(internal_ftype != ftype) { + if(ftype == TYPE_EOF) { + ftype = internal_ftype; /* no problems here */ + } + else { /* check that the requested conversion is valid */ + if(internal_ftype == TYPE_AU1 || internal_ftype == TYPE_AU2 || + internal_ftype == TYPE_AU3 || ftype == TYPE_AU1 ||ftype == TYPE_AU2 || ftype == TYPE_AU3) + { + retval = 0; + goto got_enough_data; + } + } + } + + nchan = UINT_GET(CHANSIZE, this_shn); + + /* get blocksize if version > 0 */ + if(version > 0) + { + int byte; + blocksize = UINT_GET((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2),this_shn); + maxnlpc = UINT_GET(LPCQSIZE, this_shn); + nmean = UINT_GET(0, this_shn); + nskip = UINT_GET(NSKIPSIZE, this_shn); + for(i = 0; i < nskip; i++) + { + byte = uvar_get(XBYTESIZE,this_shn); + } + } + else + blocksize = DEFAULT_BLOCK_SIZE; + + nwrap = MAX(NWRAP, maxnlpc); + + /* grab some space for the input buffer */ + buffer = long2d((ulong) nchan, (ulong) (blocksize + nwrap),this_shn); + if (this_shn->vars.fatal_error) + return 0; + offset = long2d((ulong) nchan, (ulong) MAX(1, nmean),this_shn); + if (this_shn->vars.fatal_error) { + if (buffer) { + free(buffer); + buffer = NULL; + } + return 0; + } + + for(chan = 0; chan < nchan; chan++) + { + for(i = 0; i < nwrap; i++) + buffer[chan][i] = 0; + buffer[chan] += nwrap; + } + + if(maxnlpc > 0) { + qlpc = (int*) pmalloc((ulong) (maxnlpc * sizeof(*qlpc)),this_shn); + if (this_shn->vars.fatal_error) { + if (buffer) { + free(buffer); + buffer = NULL; + } + if (offset) { + free(offset); + buffer = NULL; + } + return 0; + } + } + + if(version > 1) + lpcqoffset = V2LPCQOFFSET; + + init_offset(offset, nchan, MAX(1, nmean), internal_ftype); + + /* get commands from file and execute them */ + chan = 0; + while(1) + { + this_shn->vars.reading_function_code = 1; + cmd = uvar_get(FNSIZE,this_shn); + this_shn->vars.reading_function_code = 0; + + switch(cmd) + { + case FN_ZERO: + case FN_DIFF0: + case FN_DIFF1: + case FN_DIFF2: + case FN_DIFF3: + case FN_QLPC: + { + slong coffset, *cbuffer = buffer[chan]; + int resn = 0, nlpc, j; + + if(cmd != FN_ZERO) + { + resn = uvar_get(ENERGYSIZE,this_shn); + if (this_shn->vars.fatal_error) { + retval = 0; + goto got_enough_data; + } + /* this is a hack as version 0 differed in definition of var_get */ + if(version == 0) + resn--; + } + + /* find mean offset : N.B. this code duplicated */ + if(nmean == 0) + coffset = offset[chan][0]; + else + { + slong sum = (version < 2) ? 0 : nmean / 2; + for(i = 0; i < nmean; i++) + sum += offset[chan][i]; + if(version < 2) + coffset = sum / nmean; + else + coffset = ROUNDEDSHIFTDOWN(sum / nmean, bitshift); + } + + switch(cmd) + { + case FN_ZERO: + for(i = 0; i < blocksize; i++) + cbuffer[i] = 0; + break; + case FN_DIFF0: + for(i = 0; i < blocksize; i++) { + cbuffer[i] = var_get(resn,this_shn) + coffset; + if (this_shn->vars.fatal_error) { + retval = 0; + goto got_enough_data; + } + } + break; + case FN_DIFF1: + for(i = 0; i < blocksize; i++) { + cbuffer[i] = var_get(resn,this_shn) + cbuffer[i - 1]; + if (this_shn->vars.fatal_error) { + retval = 0; + goto got_enough_data; + } + } + break; + case FN_DIFF2: + for(i = 0; i < blocksize; i++) { + cbuffer[i] = var_get(resn,this_shn) + (2 * cbuffer[i - 1] - cbuffer[i - 2]); + if (this_shn->vars.fatal_error) { + retval = 0; + goto got_enough_data; + } + } + break; + case FN_DIFF3: + for(i = 0; i < blocksize; i++) { + cbuffer[i] = var_get(resn,this_shn) + 3 * (cbuffer[i - 1] - cbuffer[i - 2]) + cbuffer[i - 3]; + if (this_shn->vars.fatal_error) { + retval = 0; + goto got_enough_data; + } + } + break; + case FN_QLPC: + nlpc = uvar_get(LPCQSIZE,this_shn); + if (this_shn->vars.fatal_error) { + retval = 0; + goto got_enough_data; + } + + for(i = 0; i < nlpc; i++) { + qlpc[i] = var_get(LPCQUANT,this_shn); + if (this_shn->vars.fatal_error) { + retval = 0; + goto got_enough_data; + } + } + for(i = 0; i < nlpc; i++) + cbuffer[i - nlpc] -= coffset; + for(i = 0; i < blocksize; i++) + { + slong sum = lpcqoffset; + + for(j = 0; j < nlpc; j++) + sum += qlpc[j] * cbuffer[i - j - 1]; + cbuffer[i] = var_get(resn,this_shn) + (sum >> LPCQUANT); + if (this_shn->vars.fatal_error) { + retval = 0; + goto got_enough_data; + } + } + if(coffset != 0) + for(i = 0; i < blocksize; i++) + cbuffer[i] += coffset; + break; + } + + /* store mean value if appropriate : N.B. Duplicated code */ + if(nmean > 0) + { + slong sum = (version < 2) ? 0 : blocksize / 2; + + for(i = 0; i < blocksize; i++) + sum += cbuffer[i]; + + for(i = 1; i < nmean; i++) + offset[chan][i - 1] = offset[chan][i]; + if(version < 2) + offset[chan][nmean - 1] = sum / blocksize; + else + offset[chan][nmean - 1] = (sum / blocksize) << bitshift; + } + + if (0 == chan) { + this_shn->vars.initial_file_position = this_shn->vars.last_file_position_no_really; + goto got_enough_data; + } + + /* do the wrap */ + for(i = -nwrap; i < 0; i++) + cbuffer[i] = cbuffer[i + blocksize]; + + fix_bitshift(cbuffer, blocksize, bitshift, internal_ftype); + + if(chan == nchan - 1) + { + fwrite_type(buffer, ftype, nchan, blocksize, this_shn); + this_shn->vars.bytes_in_buf = 0; + } + + chan = (chan + 1) % nchan; + break; + } + break; + + case FN_BLOCKSIZE: + UINT_GET((int) (log((double) blocksize) / M_LN2), this_shn); + break; + + case FN_VERBATIM: + cklen = uvar_get(VERBATIM_CKSIZE_SIZE,this_shn); + + while (cklen--) { + if (this_shn->vars.bytes_in_header >= OUT_BUFFER_SIZE) { + shn_debug(this_shn->config,"Unexpectedly large header - " PACKAGE " can only handle a maximum of %d bytes",OUT_BUFFER_SIZE); + goto got_enough_data; + } + this_shn->vars.bytes_in_buf = 0; + this_shn->vars.header[this_shn->vars.bytes_in_header++] = (char)uvar_get(VERBATIM_BYTE_SIZE,this_shn); + } + retval = 1; + break; + + case FN_BITSHIFT: + bitshift = uvar_get(BITSHIFTSIZE,this_shn); + this_shn->vars.bitshift = bitshift; + break; + + default: + goto got_enough_data; + } + } + +got_enough_data: + + /* wind up */ + var_get_quit(this_shn); + fwrite_type_quit(this_shn); + + if (buffer) free((void *) buffer); + if (offset) free((void *) offset); + if(maxnlpc > 0 && qlpc) + free((void *) qlpc); + + this_shn->vars.bytes_in_buf = 0; + + return retval; +} + + +void shn_unload(shn_file *this_shn) +{ + if (this_shn) + { + if (this_shn->vars.fd) + { + fclose(this_shn->vars.fd); + this_shn->vars.fd = NULL; + } + + if (this_shn->decode_state) + { + if (this_shn->decode_state->getbuf) + { + free(this_shn->decode_state->getbuf); + this_shn->decode_state->getbuf = NULL; + } + + if (this_shn->decode_state->writebuf) + { + free(this_shn->decode_state->writebuf); + this_shn->decode_state->writebuf = NULL; + } + + if (this_shn->decode_state->writefub) + { + free(this_shn->decode_state->writefub); + this_shn->decode_state->writefub = NULL; + } + + free(this_shn->decode_state); + this_shn->decode_state = NULL; + } + + if (this_shn->seek_table) + { + free(this_shn->seek_table); + this_shn->seek_table = NULL; + } + + free(this_shn); + this_shn = NULL; + } +} + +shn_file *shn_load(char *filename, shn_config config) +{ + shn_file *tmp_file; + shn_seek_entry *first_seek_table; + + if (!(tmp_file = malloc(sizeof(shn_file)))) + { + fprintf(stderr, "Could not allocate memory for SHN data structure"); + return NULL; + } + + memset(tmp_file, 0, sizeof(shn_file)); + + /*Copying config */ + tmp_file->config = config; + + tmp_file->vars.fd = NULL; + tmp_file->vars.seek_to = -1; + tmp_file->vars.eof = 0; + tmp_file->vars.going = 0; + tmp_file->vars.seek_table_entries = NO_SEEK_TABLE; + tmp_file->vars.bytes_in_buf = 0; + tmp_file->vars.bytes_in_header = 0; + tmp_file->vars.reading_function_code = 0; + tmp_file->vars.initial_file_position = 0; + tmp_file->vars.last_file_position = 0; + tmp_file->vars.last_file_position_no_really = 0; + tmp_file->vars.bytes_read = 0; + tmp_file->vars.bitshift = 0; + tmp_file->vars.seek_offset = 0; + + tmp_file->decode_state = NULL; + + tmp_file->wave_header.filename = filename; + tmp_file->wave_header.wave_format = 0; + tmp_file->wave_header.channels = 0; + tmp_file->wave_header.block_align = 0; + tmp_file->wave_header.bits_per_sample = 0; + tmp_file->wave_header.samples_per_sec = 0; + tmp_file->wave_header.avg_bytes_per_sec = 0; + tmp_file->wave_header.rate = 0; + tmp_file->wave_header.header_size = 0; + tmp_file->wave_header.data_size = 0; + tmp_file->wave_header.file_has_id3v2_tag = 0; + tmp_file->wave_header.id3v2_tag_size = 0; + + tmp_file->seek_header.version = NO_SEEK_TABLE; + tmp_file->seek_header.shnFileSize = 0; + + tmp_file->seek_trailer.seekTableSize = 0; + + tmp_file->seek_table = NULL; + + if (!(tmp_file->vars.fd = shn_open_and_discard_id3v2_tag(filename,&tmp_file->wave_header.file_has_id3v2_tag,&tmp_file->wave_header.id3v2_tag_size))) + { + shn_debug(tmp_file->config, "Could not open file: '%s'",filename); + shn_unload(tmp_file); + return NULL; + } + + if (0 == get_wave_header(tmp_file)) + { + shn_debug(tmp_file->config, "Unable to read WAVE header from file '%s'",filename); + shn_unload(tmp_file); + return NULL; + } + + if (tmp_file->wave_header.file_has_id3v2_tag) + { + fseek(tmp_file->vars.fd,tmp_file->wave_header.id3v2_tag_size,SEEK_SET); + tmp_file->vars.bytes_read += tmp_file->wave_header.id3v2_tag_size; + tmp_file->vars.seek_offset = tmp_file->wave_header.id3v2_tag_size; + } + else + { + fseek(tmp_file->vars.fd,0,SEEK_SET); + } + + if (0 == shn_verify_header(tmp_file)) + { + shn_debug(tmp_file->config, "Invalid WAVE header in file: '%s'",filename); + shn_unload(tmp_file); + return NULL; + } + + if (tmp_file->decode_state) + { + free(tmp_file->decode_state); + tmp_file->decode_state = NULL; + } + + shn_load_seek_table(tmp_file,filename); + + if (NO_SEEK_TABLE != tmp_file->vars.seek_table_entries) + { + first_seek_table = (shn_seek_entry *)tmp_file->seek_table; + + /* check for broken seek tables - if found, disable seeking */ + if (0 == tmp_file->seek_header.version) + { + /* test, if the bitshift value in the file is identical to the bitshift value of the first seektable entry */ + if (tmp_file->vars.bitshift != shn_uchar_to_ushort_le(first_seek_table->data+22)) + { + shn_debug(tmp_file->config, "Broken seek table detected - seeking disabled for file '%s'.",tmp_file->wave_header.filename); + tmp_file->vars.seek_table_entries = NO_SEEK_TABLE; + } + } + + tmp_file->vars.seek_offset += tmp_file->vars.initial_file_position - shn_uchar_to_ulong_le(first_seek_table->data+8); + + if (0 != tmp_file->vars.seek_offset) + { + shn_debug(tmp_file->config, "Adjusting seek table offsets by %ld bytes due to mismatch between seek table values and input file - seeking might not work correctly.", + tmp_file->vars.seek_offset); + } + } + + fseek(tmp_file->vars.fd,0,SEEK_SET); + tmp_file->vars.going = 1; + tmp_file->vars.seek_to = -1; + + shn_debug(tmp_file->config, "Successfully loaded file: '%s'",filename); + + return tmp_file; +} + +static int shn_init_decode_state(shn_file *this_shn) +{ + if (this_shn->decode_state) + { + if (this_shn->decode_state->getbuf) + { + free(this_shn->decode_state->getbuf); + this_shn->decode_state->getbuf = NULL; + } + + if (this_shn->decode_state->writebuf) + { + free(this_shn->decode_state->writebuf); + this_shn->decode_state->writebuf = NULL; + } + + if (this_shn->decode_state->writefub) + { + free(this_shn->decode_state->writefub); + this_shn->decode_state->writefub = NULL; + } + + free(this_shn->decode_state); + this_shn->decode_state = NULL; + } + + if (!(this_shn->decode_state = malloc(sizeof(shn_decode_state)))) + { + shn_debug(this_shn->config, "Could not allocate memory for decode state data structure"); + return 0; + } + + this_shn->decode_state->getbuf = NULL; + this_shn->decode_state->getbufp = NULL; + this_shn->decode_state->nbitget = 0; + this_shn->decode_state->nbyteget = 0; + this_shn->decode_state->gbuffer = 0; + this_shn->decode_state->writebuf = NULL; + this_shn->decode_state->writefub = NULL; + this_shn->decode_state->nwritebuf = 0; + + this_shn->vars.bytes_in_buf = 0; + + return 1; +} + +int shn_init_decoder(shn_file *this_shn) { + if(!this_shn) + return 0; + if(!shn_init_decode_state(this_shn)) { + shn_error(this_shn->config, "shn_init_decode state failed!\n"); + return 0; + } + /* read magic number */ +#ifdef STRICT_FORMAT_COMPATABILITY + if(FORMAT_VERSION < 2) { + for(i = 0; i < strlen(magic); i++) + if(getc_exit(this_shn->vars.fd) != magic[i]) { + shn_error_fatal(this_shn,"Bad magic number"); + return 0; + } + + /* get version number */ + version = getc_exit(this_shn->vars.fd); + } + else +#endif /* STRICT_FORMAT_COMPATABILITY */ + { + int nscan = 0; + + version = MAX_VERSION + 1; + while(version > MAX_VERSION) { + int byte = getc(this_shn->vars.fd); + if(byte == EOF) { + shn_error_fatal(this_shn,"No magic number"); + return 0; + } + if(magic[nscan] != '\0' && byte == magic[nscan]) + nscan++; + else + if(magic[nscan] == '\0' && byte <= MAX_VERSION) + version = byte; + else { + if(byte == magic[0]) + nscan = 1; + else { + nscan = 0; + } + version = MAX_VERSION + 1; + } + } + } + + /* check version number */ + if(version > MAX_SUPPORTED_VERSION) { + shn_error_fatal(this_shn,"Can't decode version %d", version); + return 0; + } + + /* set up the default nmean, ignoring the command line state */ + nmean = (version < 2) ? DEFAULT_V0NMEAN : DEFAULT_V2NMEAN; + + /* initialise the variable length file read for the compressed stream */ + var_get_init(this_shn); + if (this_shn->vars.fatal_error) + return 0; + + /* initialise the fixed length file write for the uncompressed stream */ + fwrite_type_init(this_shn); + + /* get the internal file type */ + internal_ftype = UINT_GET(TYPESIZE, this_shn); + + /* has the user requested a change in file type? */ + if(internal_ftype != ftype) { + if(ftype == TYPE_EOF) + ftype = internal_ftype; /* no problems here */ + else /* check that the requested conversion is valid */ + if(internal_ftype == TYPE_AU1 || internal_ftype == TYPE_AU2 || internal_ftype == TYPE_AU3 || ftype == TYPE_AU1 ||ftype == TYPE_AU2 || ftype == TYPE_AU3) { + shn_error_fatal(this_shn,"Not able to perform requested output format conversion"); + return 0; + } + } + + nchan = UINT_GET(CHANSIZE, this_shn); + + /* get blocksize if version > 0 */ + if(version > 0) { + int byte; + blocksize = UINT_GET((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2),this_shn); + maxnlpc = UINT_GET(LPCQSIZE, this_shn); + nmean = UINT_GET(0, this_shn); + nskip = UINT_GET(NSKIPSIZE, this_shn); + for(i = 0; i < nskip; i++) { + byte = uvar_get(XBYTESIZE,this_shn); + } + } + else + blocksize = DEFAULT_BLOCK_SIZE; + + nwrap = MAX(NWRAP, maxnlpc); + + /* grab some space for the input buffer */ + buffer = long2d((ulong) nchan, (ulong) (blocksize + nwrap),this_shn); + if (this_shn->vars.fatal_error) + return 0; + offset = long2d((ulong) nchan, (ulong) MAX(1, nmean),this_shn); + if (this_shn->vars.fatal_error) { + if (buffer) { + free(buffer); + buffer = NULL; + } + return 0; + } + + for(chan = 0; chan < nchan; chan++) { + for(i = 0; i < nwrap; i++) + buffer[chan][i] = 0; + buffer[chan] += nwrap; + } + + if(maxnlpc > 0) { + qlpc = (int*) pmalloc((ulong) (maxnlpc * sizeof(*qlpc)),this_shn); + if (this_shn->vars.fatal_error) { + if (buffer) { + free(buffer); + buffer = NULL; + } + if (offset) { + free(offset); + buffer = NULL; + } + return 0; + } + } + + if(version > 1) + lpcqoffset = V2LPCQOFFSET; + + init_offset(offset, nchan, MAX(1, nmean), internal_ftype); + + this_shn->vars.eof = FALSE; + chan = 0; + + /* Success */ + return 1; +} + +int shn_cleanup_decoder(shn_file *this_shn) { + if(!this_shn) + return 0; + this_shn->vars.seek_to = -1; + this_shn->vars.eof = TRUE; + +/* wind up */ + var_get_quit(this_shn); + fwrite_type_quit(this_shn); + + if (buffer) free((void *) buffer); + if (offset) free((void *) offset); + if(maxnlpc > 0 && qlpc) + free((void *) qlpc); + + return 1; +} + +static int write_to_buffer(shn_file *this_shn, uchar *read_buffer, int block_size) +{ + int bytes_to_write,bytes_in_block,i; + + if (this_shn->vars.bytes_in_buf < block_size) + return 0; + + bytes_in_block = min(this_shn->vars.bytes_in_buf, block_size); + + if (bytes_in_block <= 0) + return 0; + + bytes_to_write = bytes_in_block; + while ((bytes_to_write + bytes_in_block) <= this_shn->vars.bytes_in_buf) + bytes_to_write += bytes_in_block; + + if(this_shn->vars.going && this_shn->vars.seek_to == -1) { + if (this_shn->config.swap_bytes) + swap_bytes(this_shn, bytes_to_write); + memcpy((uchar *)read_buffer, (uchar *)(this_shn->vars.buffer), bytes_to_write); + } else + return 0; + + /* shift data from end of buffer to the front */ + this_shn->vars.bytes_in_buf -= bytes_to_write; + + for(i=0;ivars.bytes_in_buf;i++) + this_shn->vars.buffer[i] = this_shn->vars.buffer[i+bytes_to_write]; + + return bytes_to_write; +} + +int shn_get_buffer_block_size(shn_file *this_shn, int blocks) { + int blk_size = blocks * (this_shn->wave_header.bits_per_sample / 8) * this_shn->wave_header.channels; + if(blk_size > OUT_BUFFER_SIZE) { + shn_debug(this_shn->config, "Resetting to default blk_size!\n"); + blk_size = NUM_DEFAULT_BUFFER_BLOCKS * (this_shn->wave_header.bits_per_sample / 8) * this_shn->wave_header.channels; + } + + return blk_size; +} + +unsigned int shn_get_song_length(shn_file *this_shn) { + if(this_shn) { + if(this_shn->wave_header.length > 0) + return (unsigned int)(1000 * this_shn->wave_header.length); + } + /* Something failed or just isn't correct */ + return (unsigned int)0; +} +int shn_read(shn_file *this_shn, uchar *read_buffer, int bytes_to_read) { + if(!this_shn) + return 0; + if(!read_buffer) + return 0; + + /***********************/ + /* EXTRACT starts here */ + /***********************/ + + buffer_is_full = 0; + while(!buffer_is_full) { + cmd = uvar_get(FNSIZE,this_shn); + if (this_shn->vars.fatal_error) + goto cleanup; + + switch(cmd) { + case FN_ZERO: + case FN_DIFF0: + case FN_DIFF1: + case FN_DIFF2: + case FN_DIFF3: + case FN_QLPC: + { + slong coffset, *cbuffer = buffer[chan]; + int resn = 0, nlpc, j; + + if(cmd != FN_ZERO) { + resn = uvar_get(ENERGYSIZE,this_shn); + if (this_shn->vars.fatal_error) + goto cleanup; + /* this is a hack as version 0 differed in definition of var_get */ + if(version == 0) + resn--; + } + + /* find mean offset : N.B. this code duplicated */ + if(nmean == 0) + coffset = offset[chan][0]; + else + { + slong sum = (version < 2) ? 0 : nmean / 2; + for(i = 0; i < nmean; i++) + sum += offset[chan][i]; + if(version < 2) + coffset = sum / nmean; + else + coffset = ROUNDEDSHIFTDOWN(sum / nmean, bitshift); + } + + switch(cmd) { + case FN_ZERO: + for(i = 0; i < blocksize; i++) + cbuffer[i] = 0; + break; + case FN_DIFF0: + for(i = 0; i < blocksize; i++) { + cbuffer[i] = var_get(resn,this_shn) + coffset; + if (this_shn->vars.fatal_error) + goto cleanup; + } + break; + case FN_DIFF1: + for(i = 0; i < blocksize; i++) { + cbuffer[i] = var_get(resn,this_shn) + cbuffer[i - 1]; + if (this_shn->vars.fatal_error) + goto cleanup; + } + break; + case FN_DIFF2: + for(i = 0; i < blocksize; i++) { + cbuffer[i] = var_get(resn,this_shn) + (2 * cbuffer[i - 1] - cbuffer[i - 2]); + if (this_shn->vars.fatal_error) + goto cleanup; + } + break; + case FN_DIFF3: + for(i = 0; i < blocksize; i++) { + cbuffer[i] = var_get(resn,this_shn) + 3 * (cbuffer[i - 1] - cbuffer[i - 2]) + cbuffer[i - 3]; + if (this_shn->vars.fatal_error) + goto cleanup; + } + break; + case FN_QLPC: + nlpc = uvar_get(LPCQSIZE,this_shn); + if (this_shn->vars.fatal_error) + goto cleanup; + + for(i = 0; i < nlpc; i++) { + qlpc[i] = var_get(LPCQUANT,this_shn); + if (this_shn->vars.fatal_error) + goto cleanup; + } + for(i = 0; i < nlpc; i++) + cbuffer[i - nlpc] -= coffset; + for(i = 0; i < blocksize; i++) { + slong sum = lpcqoffset; + + for(j = 0; j < nlpc; j++) + sum += qlpc[j] * cbuffer[i - j - 1]; + cbuffer[i] = var_get(resn,this_shn) + (sum >> LPCQUANT); + if (this_shn->vars.fatal_error) + goto cleanup; + } + if(coffset != 0) + for(i = 0; i < blocksize; i++) + cbuffer[i] += coffset; + break; + } + + /* store mean value if appropriate : N.B. Duplicated code */ + if(nmean > 0) { + slong sum = (version < 2) ? 0 : blocksize / 2; + + for(i = 0; i < blocksize; i++) + sum += cbuffer[i]; + + for(i = 1; i < nmean; i++) + offset[chan][i - 1] = offset[chan][i]; + if(version < 2) + offset[chan][nmean - 1] = sum / blocksize; + else + offset[chan][nmean - 1] = (sum / blocksize) << bitshift; + } + + /* do the wrap */ + for(i = -nwrap; i < 0; i++) + cbuffer[i] = cbuffer[i + blocksize]; + + fix_bitshift(cbuffer, blocksize, bitshift, internal_ftype); + + if(chan == nchan - 1) { + if (!this_shn->vars.going || this_shn->vars.fatal_error) + goto cleanup; + + fwrite_type(buffer, ftype, nchan, blocksize, this_shn); + + buffer_ret = write_to_buffer(this_shn,read_buffer,bytes_to_read); + if(buffer_ret == bytes_to_read) + buffer_is_full = 1; + + if (this_shn->vars.seek_to != -1) { + shn_seek_entry *seek_info; + + shn_debug(this_shn->config, "Seeking to %d:%02d",this_shn->vars.seek_to/60,this_shn->vars.seek_to%60); + + seek_info = shn_seek_entry_search(this_shn->config,this_shn->seek_table,this_shn->vars.seek_to * (ulong)this_shn->wave_header.samples_per_sec,0,(ulong)(this_shn->vars.seek_table_entries - 1),this_shn->vars.seek_resolution); + + buffer[0][-1] = shn_uchar_to_slong_le(seek_info->data+24); + buffer[0][-2] = shn_uchar_to_slong_le(seek_info->data+28); + buffer[0][-3] = shn_uchar_to_slong_le(seek_info->data+32); + offset[0][0] = shn_uchar_to_slong_le(seek_info->data+48); + offset[0][1] = shn_uchar_to_slong_le(seek_info->data+52); + offset[0][2] = shn_uchar_to_slong_le(seek_info->data+56); + offset[0][3] = shn_uchar_to_slong_le(seek_info->data+60); + if (nchan > 1) { + buffer[1][-1] = shn_uchar_to_slong_le(seek_info->data+36); + buffer[1][-2] = shn_uchar_to_slong_le(seek_info->data+40); + buffer[1][-3] = shn_uchar_to_slong_le(seek_info->data+44); + offset[1][0] = shn_uchar_to_slong_le(seek_info->data+64); + offset[1][1] = shn_uchar_to_slong_le(seek_info->data+68); + offset[1][2] = shn_uchar_to_slong_le(seek_info->data+72); + offset[1][3] = shn_uchar_to_slong_le(seek_info->data+76); + } + + bitshift = shn_uchar_to_ushort_le(seek_info->data+22); + + seekto_offset = shn_uchar_to_ulong_le(seek_info->data+8) + this_shn->vars.seek_offset; + + fseek(this_shn->vars.fd,(slong)seekto_offset,SEEK_SET); + fread((uchar*) this_shn->decode_state->getbuf, 1, BUFSIZ, this_shn->vars.fd); + + this_shn->decode_state->getbufp = this_shn->decode_state->getbuf + shn_uchar_to_ushort_le(seek_info->data+14); + this_shn->decode_state->nbitget = shn_uchar_to_ushort_le(seek_info->data+16); + this_shn->decode_state->nbyteget = shn_uchar_to_ushort_le(seek_info->data+12); + this_shn->decode_state->gbuffer = shn_uchar_to_ulong_le(seek_info->data+18); + + this_shn->vars.bytes_in_buf = 0; + + this_shn->vars.seek_to = -1; + } + } + chan = (chan + 1) % nchan; + break; + } + break; + + case FN_QUIT: + /* empty out last of buffer */ + buffer_ret = write_to_buffer(this_shn,read_buffer,this_shn->vars.bytes_in_buf); + if(buffer_ret == bytes_to_read) + buffer_is_full = 1; + + this_shn->vars.eof = TRUE; + + goto cleanup; + break; + + case FN_BLOCKSIZE: + blocksize = UINT_GET((int) (log((double) blocksize) / M_LN2), this_shn); + if (this_shn->vars.fatal_error) + goto cleanup; + break; + + case FN_BITSHIFT: + bitshift = uvar_get(BITSHIFTSIZE,this_shn); + if (this_shn->vars.fatal_error) + goto cleanup; + break; + + case FN_VERBATIM: + cklen = uvar_get(VERBATIM_CKSIZE_SIZE,this_shn); + if (this_shn->vars.fatal_error) + goto cleanup; + + while (cklen--) { + tmp = (uchar)uvar_get(VERBATIM_BYTE_SIZE,this_shn); + if (this_shn->vars.fatal_error) + goto cleanup; + } + break; + + default: + shn_error_fatal(this_shn,"Sanity check fails trying to decode function: %d",cmd); + goto cleanup; + } + } + goto exit_func; + +cleanup: + + buffer_ret = write_to_buffer(this_shn,read_buffer,this_shn->vars.bytes_in_buf); + +exit_func: + return buffer_ret; + +} + +unsigned int shn_get_samplerate(shn_file *this_shn) { + if(this_shn) { + if(this_shn->wave_header.samples_per_sec > 0) + return (unsigned int)this_shn->wave_header.samples_per_sec; + } + /* Something failed or just isn't correct */ + return (unsigned int)0; +} + +unsigned int shn_get_channels(shn_file *this_shn) { + if(this_shn) { + if(this_shn->wave_header.channels > 0) + return (unsigned int)this_shn->wave_header.channels; + } + /* Something failed or just isn't correct */ + return (unsigned int)0; +} + +unsigned int shn_get_bitspersample(shn_file *this_shn) { + if(this_shn) { + if(this_shn->wave_header.bits_per_sample > 0) + return (unsigned int)this_shn->wave_header.bits_per_sample; + } + /* Something failed or just isn't correct */ + return (unsigned int)0; +} + diff --git a/Libraries/Shorten/Files/shorten/src/fixio.c b/Libraries/Shorten/Files/shorten/src/fixio.c new file mode 100644 index 000000000..3381ee376 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/fixio.c @@ -0,0 +1,276 @@ +/****************************************************************************** +* * +* Copyright (C) 1992-1995 Tony Robinson * +* * +* See the file doc/LICENSE.shorten for conditions on distribution and usage * +* * +******************************************************************************/ + +/* + * $Id$ + */ + +#include +#include +#include +#include "shorten.h" +#include "bitshift.h" + +#define CAPMAXSCHAR(x) ((x > 127) ? 127 : x) +#define CAPMAXUCHAR(x) ((x > 255) ? 255 : x) +#define CAPMAXSHORT(x) ((x > 32767) ? 32767 : x) +#define CAPMAXUSHORT(x) ((x > 65535) ? 65535 : x) + +static int sizeof_sample[TYPE_EOF]; + +void init_sizeof_sample() { + sizeof_sample[TYPE_AU1] = sizeof(uchar); + sizeof_sample[TYPE_S8] = sizeof(schar); + sizeof_sample[TYPE_U8] = sizeof(uchar); + sizeof_sample[TYPE_S16HL] = sizeof(ushort); + sizeof_sample[TYPE_U16HL] = sizeof(ushort); + sizeof_sample[TYPE_S16LH] = sizeof(ushort); + sizeof_sample[TYPE_U16LH] = sizeof(ushort); + sizeof_sample[TYPE_ULAW] = sizeof(uchar); + sizeof_sample[TYPE_AU2] = sizeof(uchar); + sizeof_sample[TYPE_AU3] = sizeof(uchar); + sizeof_sample[TYPE_ALAW] = sizeof(uchar); +} + +/***************/ +/* fixed write */ +/***************/ + +void fwrite_type_init(shn_file *this_shn) { + init_sizeof_sample(); + this_shn->decode_state->writebuf = (schar*) NULL; + this_shn->decode_state->writefub = (schar*) NULL; + this_shn->decode_state->nwritebuf = 0; +} + +void fwrite_type_quit(shn_file *this_shn) { + if(this_shn->decode_state->writebuf != NULL) { + free(this_shn->decode_state->writebuf); + this_shn->decode_state->writebuf = NULL; + } + if(this_shn->decode_state->writefub != NULL) { + free(this_shn->decode_state->writefub); + this_shn->decode_state->writefub = NULL; + } +} + +/* convert from signed ints to a given type and write */ +void fwrite_type(slong **data,int ftype,int nchan,int nitem,shn_file *this_shn) +{ + int hiloint = 1, hilo = !(*((char*) &hiloint)); + int i, nwrite = 0, datasize = sizeof_sample[ftype], chan; + slong *data0 = data[0]; + int bufAvailable = OUT_BUFFER_SIZE - this_shn->vars.bytes_in_buf; + + if(this_shn->decode_state->nwritebuf < nchan * nitem * datasize) { + this_shn->decode_state->nwritebuf = nchan * nitem * datasize; + if(this_shn->decode_state->writebuf != NULL) free(this_shn->decode_state->writebuf); + if(this_shn->decode_state->writefub != NULL) free(this_shn->decode_state->writefub); + this_shn->decode_state->writebuf = (schar*) pmalloc((ulong) this_shn->decode_state->nwritebuf,this_shn); + if (!this_shn->decode_state->writebuf) + return; + this_shn->decode_state->writefub = (schar*) pmalloc((ulong) this_shn->decode_state->nwritebuf,this_shn); + if (!this_shn->decode_state->writefub) + return; + } + + switch(ftype) { + case TYPE_AU1: /* leave the conversion to fix_bitshift() */ + case TYPE_AU2: { + uchar *writebufp = (uchar*) this_shn->decode_state->writebuf; + if(nchan == 1) + for(i = 0; i < nitem; i++) + *writebufp++ = data0[i]; + else + for(i = 0; i < nitem; i++) + for(chan = 0; chan < nchan; chan++) + *writebufp++ = data[chan][i]; + break; + } + case TYPE_U8: { + uchar *writebufp = (uchar*) this_shn->decode_state->writebuf; + if(nchan == 1) + for(i = 0; i < nitem; i++) + *writebufp++ = CAPMAXUCHAR(data0[i]); + else + for(i = 0; i < nitem; i++) + for(chan = 0; chan < nchan; chan++) + *writebufp++ = CAPMAXUCHAR(data[chan][i]); + break; + } + case TYPE_S8: { + schar *writebufp = (schar*) this_shn->decode_state->writebuf; + if(nchan == 1) + for(i = 0; i < nitem; i++) + *writebufp++ = CAPMAXSCHAR(data0[i]); + else + for(i = 0; i < nitem; i++) + for(chan = 0; chan < nchan; chan++) + *writebufp++ = CAPMAXSCHAR(data[chan][i]); + break; + } + case TYPE_S16HL: + case TYPE_S16LH: { + short *writebufp = (short*) this_shn->decode_state->writebuf; + if(nchan == 1) + for(i = 0; i < nitem; i++) + *writebufp++ = CAPMAXSHORT(data0[i]); + else + for(i = 0; i < nitem; i++) + for(chan = 0; chan < nchan; chan++) + *writebufp++ = CAPMAXSHORT(data[chan][i]); + break; + } + case TYPE_U16HL: + case TYPE_U16LH: { + ushort *writebufp = (ushort*) this_shn->decode_state->writebuf; + if(nchan == 1) + for(i = 0; i < nitem; i++) + *writebufp++ = CAPMAXUSHORT(data0[i]); + else + for(i = 0; i < nitem; i++) + for(chan = 0; chan < nchan; chan++) + *writebufp++ = CAPMAXUSHORT(data[chan][i]); + break; + } + case TYPE_ULAW: { + uchar *writebufp = (uchar*) this_shn->decode_state->writebuf; + if(nchan == 1) + for(i = 0; i < nitem; i++) + *writebufp++ = Slinear2ulaw(CAPMAXSHORT((data0[i] << 3))); + else + for(i = 0; i < nitem; i++) + for(chan = 0; chan < nchan; chan++) + *writebufp++ = Slinear2ulaw(CAPMAXSHORT((data[chan][i] << 3))); + break; + } + case TYPE_AU3: { + uchar *writebufp = (uchar*) this_shn->decode_state->writebuf; + if(nchan == 1) + for(i = 0; i < nitem; i++) + if(data0[i] < 0) + *writebufp++ = (127 - data0[i]) ^ 0xd5; + else + *writebufp++ = (data0[i] + 128) ^ 0x55; + else + for(i = 0; i < nitem; i++) + for(chan = 0; chan < nchan; chan++) + if(data[chan][i] < 0) + *writebufp++ = (127 - data[chan][i]) ^ 0xd5; + else + *writebufp++ = (data[chan][i] + 128) ^ 0x55; + break; + } + case TYPE_ALAW: { + uchar *writebufp = (uchar*) this_shn->decode_state->writebuf; + if(nchan == 1) + for(i = 0; i < nitem; i++) + *writebufp++ = Slinear2alaw(CAPMAXSHORT((data0[i] << 3))); + else + for(i = 0; i < nitem; i++) + for(chan = 0; chan < nchan; chan++) + *writebufp++ = Slinear2alaw(CAPMAXSHORT((data[chan][i] << 3))); + break; + } + } + + switch(ftype) { + case TYPE_AU1: + case TYPE_S8: + case TYPE_U8: + case TYPE_ULAW: + case TYPE_AU2: + case TYPE_AU3: + case TYPE_ALAW: + if (datasize*nchan*nitem <= bufAvailable) { + memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writebuf,datasize*nchan*nitem); + this_shn->vars.bytes_in_buf += datasize*nchan*nitem; + nwrite = nitem; + } + else + shn_debug(this_shn->config, "Buffer overrun in fwrite_type() [case 1]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable); + break; + case TYPE_S16HL: + case TYPE_U16HL: + if(hilo) + { + if (datasize*nchan*nitem <= bufAvailable) { + memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writebuf,datasize*nchan*nitem); + this_shn->vars.bytes_in_buf += datasize*nchan*nitem; + nwrite = nitem; + } + else + shn_debug(this_shn->config, "Buffer overrun in fwrite_type() [case 2]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable); + } + else + { + swab(this_shn->decode_state->writebuf, this_shn->decode_state->writefub, datasize * nchan * nitem); + if (datasize*nchan*nitem <= bufAvailable) { + memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writefub,datasize*nchan*nitem); + this_shn->vars.bytes_in_buf += datasize*nchan*nitem; + nwrite = nitem; + } + else + shn_debug(this_shn->config, "Buffer overrun in fwrite_type() [case 3]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable); + } + break; + case TYPE_S16LH: + case TYPE_U16LH: + if(hilo) + { + swab(this_shn->decode_state->writebuf, this_shn->decode_state->writefub, datasize * nchan * nitem); + if (datasize*nchan*nitem <= bufAvailable) { + memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writefub,datasize*nchan*nitem); + this_shn->vars.bytes_in_buf += datasize*nchan*nitem; + nwrite = nitem; + } + else + shn_debug(this_shn->config, "Buffer overrun in fwrite_type() [case 4]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable); + } + else + { + if (datasize*nchan*nitem <= bufAvailable) { + memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writebuf,datasize*nchan*nitem); + this_shn->vars.bytes_in_buf += datasize*nchan*nitem; + nwrite = nitem; + } + else + shn_debug(this_shn->config, "Buffer overrun in fwrite_type() [case 5]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable); + } + break; + } + + if(nwrite != nitem) + shn_error_fatal(this_shn,"Failed to write decompressed stream -\npossible corrupt or truncated file"); +} + +/*************/ +/* bitshifts */ +/*************/ + +void fix_bitshift(buffer, nitem, bitshift, ftype) slong *buffer; int nitem, + bitshift, ftype; { + int i; + + if(ftype == TYPE_AU1) + for(i = 0; i < nitem; i++) + buffer[i] = ulaw_outward[bitshift][buffer[i] + 128]; + else if(ftype == TYPE_AU2) + for(i = 0; i < nitem; i++) { + if(buffer[i] >= 0) + buffer[i] = ulaw_outward[bitshift][buffer[i] + 128]; + else if(buffer[i] == -1) + buffer[i] = NEGATIVE_ULAW_ZERO; + else + buffer[i] = ulaw_outward[bitshift][buffer[i] + 129]; + } + else + if(bitshift != 0) + for(i = 0; i < nitem; i++) + buffer[i] <<= bitshift; +} diff --git a/Libraries/Shorten/Files/shorten/src/id3v2.c b/Libraries/Shorten/Files/shorten/src/id3v2.c new file mode 100644 index 000000000..b3ddc6ac1 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/id3v2.c @@ -0,0 +1,120 @@ +/* id3v2.c - functions to handle ID3v2 tags prepended to shn files + * Copyright (C) 2000-2004 Jason Jordan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * $Id$ + */ + +#include +#include "shorten.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define ID3V2_MAGIC "ID3" + +typedef struct { + char magic[3]; + unsigned char version[2]; + unsigned char flags[1]; + unsigned char size[4]; +} _id3v2_header; + +int tagcmp(char *got,char *expected) +/* compare got against expected, up to the length of expected */ +{ + int i; + + for (i=0;*(expected+i);i++) { + if (*(got+i) != *(expected+i)) + return i+1; + } + + return 0; +} + +unsigned long synchsafe_int_to_ulong(unsigned char *buf) +/* converts 4 bytes stored in synchsafe integer format to an unsigned long */ +{ + return (unsigned long)(((buf[0] & 0x7f) << 21) | ((buf[1] & 0x7f) << 14) | ((buf[2] & 0x7f) << 7) | (buf[3] & 0x7f)); +} + +static unsigned long check_for_id3v2_tag(FILE *f) +{ + _id3v2_header id3v2_header; + unsigned long tag_size; + + /* read an ID3v2 header's size worth of data */ + if (sizeof(_id3v2_header) != fread(&id3v2_header,1,sizeof(_id3v2_header),f)) { + return 0; + } + + /* verify this is an ID3v2 header */ + if (tagcmp(id3v2_header.magic,ID3V2_MAGIC) || + 0xff == id3v2_header.version[0] || 0xff == id3v2_header.version[1] || + 0x80 <= id3v2_header.size[0] || 0x80 <= id3v2_header.size[1] || + 0x80 <= id3v2_header.size[2] || 0x80 <= id3v2_header.size[3]) + { + return 0; + } + + /* calculate and return ID3v2 tag size */ + tag_size = synchsafe_int_to_ulong(id3v2_header.size); + + return tag_size; +} + +FILE *shn_open_and_discard_id3v2_tag(char *filename,int *file_has_id3v2_tag,long *id3v2_tag_size) +/* opens a file, and if it contains an ID3v2 tag, skips past it */ +{ + FILE *f; + unsigned long tag_size; + + if (NULL == (f = fopen(filename,"rb"))) { + return NULL; + } + + if (file_has_id3v2_tag) + *file_has_id3v2_tag = 0; + + if (id3v2_tag_size) + *id3v2_tag_size = 0; + + /* check for ID3v2 tag on input */ + if (0 == (tag_size = check_for_id3v2_tag(f))) { + fclose(f); + return fopen(filename,"rb"); + } + + if (file_has_id3v2_tag) + *file_has_id3v2_tag = 2; + + if (id3v2_tag_size) + *id3v2_tag_size = (long)(tag_size + sizeof(_id3v2_header)); + + fprintf(stderr, "Discarding %lu-byte ID3v2 tag at beginning of file '%s'.",tag_size+sizeof(_id3v2_header),filename); + + if (0 != fseek(f,(long)tag_size,SEEK_CUR)) { + fprintf(stderr, "Error while discarding ID3v2 tag in file '%s'.",filename); + fclose(f); + return fopen(filename,"rb"); + } + + return f; +} diff --git a/Libraries/Shorten/Files/shorten/src/misc.c b/Libraries/Shorten/Files/shorten/src/misc.c new file mode 100644 index 000000000..5274818c8 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/misc.c @@ -0,0 +1,149 @@ +/* misc.c - miscellaneous functions + * Copyright (C) 2000-2004 Jason Jordan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * $Id$ + */ + +#include +#include +#include +#include "shorten.h" + +void shn_snprintf(char *dest,int maxlen,char *formatstr, ...) +/* acts like snprintf, but makes 100% sure the string is NULL-terminated */ +{ + va_list args; + + va_start(args,formatstr); + + shn_vsnprintf(dest,maxlen,formatstr,args); + + dest[maxlen-1] = 0; + + va_end(args); +} + +int shn_filename_contains_a_dot(char *filename) +{ + char *slash,*dot; + + dot = strrchr(filename,'.'); + if (!dot) + return 0; + + slash = strrchr(filename,'/'); + if (!slash) + return 1; + + if (slash < dot) + return 1; + else + return 0; +} + +char *shn_get_base_filename(char *filename) +{ + char *b,*e,*p,*base; + + b = strrchr(filename,'/'); + + if (b) + b++; + else + b = filename; + + e = strrchr(filename,'.'); + + if (e < b) + e = filename + strlen(filename); + + if (NULL == (base = malloc((e - b + 1) * sizeof(char)))) + { + fprintf(stderr, "Could not allocate memory for base filename"); + return NULL; + } + + for (p=b;pwave_header)) { + newlength = (ulong)info->wave_header.exact_length; + + tmp = info->wave_header.exact_length - (double)((ulong)info->wave_header.exact_length); + ms = (ulong)((tmp * 1000.0) + 0.5); + + if (1000 == ms) { + ms = 0; + newlength++; + } + + shn_snprintf(info->wave_header.m_ss,16,"%lu:%02lu.%03lu",newlength/60,newlength%60,ms); + } + else { + newlength = info->wave_header.length; + + rem1 = info->wave_header.data_size % CD_RATE; + rem2 = rem1 % CD_BLOCK_SIZE; + + frames = rem1 / CD_BLOCK_SIZE; + if (rem2 >= (CD_BLOCK_SIZE / 2)) + frames++; + + if (frames == CD_BLOCKS_PER_SEC) { + frames = 0; + newlength++; + } + + shn_snprintf(info->wave_header.m_ss,16,"%lu:%02lu.%02lu",newlength/60,newlength%60,frames); + } +} diff --git a/Libraries/Shorten/Files/shorten/src/output.c b/Libraries/Shorten/Files/shorten/src/output.c new file mode 100644 index 000000000..836a5ee06 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/output.c @@ -0,0 +1,99 @@ +/* output.c - functions for message and error output + * Copyright (C) 2000-2004 Jason Jordan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * $Id$ + */ + +#include +#include +#include +#include "shorten.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +void print_lines(char *prefix,char *message) +{ + char *head, *tail; + + head = tail = message; + while (*head != '\0') { + if (*head == '\n') { + *head = '\0'; + fprintf(stderr,"%s%s\n",prefix,tail); + tail = head + 1; + } + head++; + } + fprintf(stderr,"%s%s\n",prefix,tail); +} + +void shn_error(shn_config config, char *msg, ...) +{ + va_list args; + char msgbuf[BUF_SIZE]; + + va_start(args,msg); + + shn_vsnprintf(msgbuf,BUF_SIZE,msg,args); + + switch (config.error_output_method) { + case ERROR_OUTPUT_STDERR: + print_lines(PACKAGE ": ",msgbuf); + break; + default: + if (0 != config.verbose) + print_lines(PACKAGE " [error]: ",msgbuf); + } + + va_end(args); +} + +void shn_debug(shn_config config, char *msg, ...) +{ + va_list args; + char msgbuf[BUF_SIZE]; + + va_start(args,msg); + + shn_vsnprintf(msgbuf,BUF_SIZE,msg,args); + + if (0 != config.verbose) + print_lines(PACKAGE " [debug]: ",msgbuf); + + va_end(args); +} + +void shn_error_fatal(shn_file *this_shn,char *complaint, ...) +{ + va_list args; + + va_start(args,complaint); + + if (NULL != this_shn) { + if (0 == this_shn->vars.fatal_error) { + this_shn->vars.fatal_error = 1; + this_shn->vars.going = 0; + shn_vsnprintf(this_shn->vars.fatal_error_msg,BUF_SIZE,complaint,args); + } + } + + va_end(args); +} diff --git a/Libraries/Shorten/Files/shorten/src/seek.c b/Libraries/Shorten/Files/shorten/src/seek.c new file mode 100644 index 000000000..47e6fd0d8 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/seek.c @@ -0,0 +1,284 @@ +/* seek.c - functions related to real-time seeking + * Copyright (C) 2000-2004 Jason Jordan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * $Id$ + */ + +#include +#include "shorten.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define ID3V1_TAG_SIZE 128 + +shn_seek_entry *shn_seek_entry_search(shn_config config, shn_seek_entry *table,ulong goal,ulong min,ulong max,ulong resolution) +{ + ulong med = (min + max) / 2; + shn_seek_entry *middle = table + med; + ulong sample = shn_uchar_to_ulong_le(middle->data); + + shn_debug(config, "Examining seek table entry %lu with sample %lu (min/max = %lu/%lu, goal sample is %lu, resolution is %lu samples)",med,sample,min,max,goal,resolution); + + if (goal < sample) + return shn_seek_entry_search(config, table,goal,min,med-1,resolution); + if (goal > sample + resolution) + return shn_seek_entry_search(config, table,goal,med+1,max,resolution); + return middle; +} + +int load_separate_seek_table_generic(char *filename,shn_file *this_shn) +{ + FILE *f; + slong seek_table_len; + + shn_debug(this_shn->config, "Looking for seek table in separate file: '%s'",filename); + + if (!(f=fopen(filename,"rb"))) + { + return 0; + } + + fseek(f,0,SEEK_END); + seek_table_len = (slong)ftell(f) - SEEK_HEADER_SIZE; + fseek(f,0,SEEK_SET); + + if (fread((void *)this_shn->seek_header.data,1,SEEK_HEADER_SIZE,f) == SEEK_HEADER_SIZE) + { + this_shn->seek_header.version = (slong)shn_uchar_to_ulong_le(this_shn->seek_header.data+4); + this_shn->seek_header.shnFileSize = shn_uchar_to_ulong_le(this_shn->seek_header.data+8); + if (memcmp(this_shn->seek_header.data,SEEK_HEADER_SIGNATURE,strlen(SEEK_HEADER_SIGNATURE)) == 0) + { + if (this_shn->seek_header.shnFileSize != this_shn->wave_header.actual_size) + { + shn_debug(this_shn->config, "warning: Seek table expected .shn file size %lu differs from actual .shn file size %lu - seek table might not belong to this file", + this_shn->seek_header.shnFileSize,this_shn->wave_header.actual_size); + } + + if ((this_shn->seek_table = malloc(seek_table_len))) + { + if (fread((void *)this_shn->seek_table,1,seek_table_len,f) == seek_table_len) + { + shn_debug(this_shn->config, "Successfully loaded seek table in separate file: '%s'",filename); + + this_shn->vars.seek_table_entries = seek_table_len / SEEK_ENTRY_SIZE; + + if (this_shn->vars.seek_table_entries > 1) + this_shn->vars.seek_resolution = shn_uchar_to_ulong_le(this_shn->seek_table->data+SEEK_ENTRY_SIZE); + else + this_shn->vars.seek_resolution = SEEK_RESOLUTION; + + fclose(f); + + return 1; + } + } + } + } + + fclose(f); + return 0; +} + +int load_appended_seek_table(shn_file *this_shn,char *filename,long bytes_from_end) +{ + switch (bytes_from_end) + { + case 0: + shn_debug(this_shn->config, "Looking for seek table appended to file: '%s'",filename); + break; + case ID3V1_TAG_SIZE: + shn_debug(this_shn->config, "Looking for seek table hidden behind an ID3v1 tag at the end of file: '%s'",filename); + break; + default: + shn_debug(this_shn->config, "Looking for seek table located %ld bytes from the end of file: '%s'",bytes_from_end,filename); + break; + } + + fseek(this_shn->vars.fd,-(SEEK_TRAILER_SIZE+bytes_from_end),SEEK_END); + if (fread((void *)this_shn->seek_trailer.data,1,SEEK_TRAILER_SIZE,this_shn->vars.fd) == SEEK_TRAILER_SIZE) + { + this_shn->seek_trailer.seekTableSize = shn_uchar_to_ulong_le(this_shn->seek_trailer.data); + if (memcmp(this_shn->seek_trailer.data+4,SEEK_TRAILER_SIGNATURE,strlen(SEEK_TRAILER_SIGNATURE)) == 0) + { + fseek(this_shn->vars.fd,-(this_shn->seek_trailer.seekTableSize+bytes_from_end),SEEK_END); + this_shn->seek_trailer.seekTableSize -= (SEEK_HEADER_SIZE + SEEK_TRAILER_SIZE); + if (fread((void *)this_shn->seek_header.data,1,SEEK_HEADER_SIZE,this_shn->vars.fd) == SEEK_HEADER_SIZE) + { + this_shn->seek_header.version = (slong)shn_uchar_to_ulong_le(this_shn->seek_header.data+4); + this_shn->seek_header.shnFileSize = shn_uchar_to_ulong_le(this_shn->seek_header.data+8); + if ((this_shn->seek_table = malloc(this_shn->seek_trailer.seekTableSize))) + { + if (fread((void *)this_shn->seek_table,1,this_shn->seek_trailer.seekTableSize,this_shn->vars.fd) == this_shn->seek_trailer.seekTableSize) + { + shn_debug(this_shn->config, "Successfully loaded seek table appended to file: '%s'",filename); + + this_shn->vars.seek_table_entries = this_shn->seek_trailer.seekTableSize / SEEK_ENTRY_SIZE; + + if (this_shn->vars.seek_table_entries > 1) + this_shn->vars.seek_resolution = shn_uchar_to_ulong_le(this_shn->seek_table->data+SEEK_ENTRY_SIZE); + else + this_shn->vars.seek_resolution = SEEK_RESOLUTION; + + return 1; + } + } + } + } + } + + return 0; +} + +int load_separate_seek_table_samedir(shn_file *this_shn,char *filename) +{ + char *altfilename,*basefile,*basedir; + + if (!(basefile = shn_get_base_filename(filename))) + { + return 0; + } + + if (!(basedir = shn_get_base_directory(filename))) + { + free(basefile); + return 0; + } + + if (!(altfilename = malloc(strlen(basedir)+strlen(basefile)+sizeof(SEEK_SUFFIX)+3))) + { + shn_debug(this_shn->config, "Could not allocate memory for same dir filename"); + free(basefile); + free(basedir); + return 0; + } + + sprintf(altfilename,"%s/%s.%s",basedir,basefile,SEEK_SUFFIX); + + free(basefile); + free(basedir); + + if (load_separate_seek_table_generic(altfilename,this_shn)) + { + free(altfilename); + return 1; + } + + free(altfilename); + return 0; +} + +int load_separate_seek_table_relative(shn_file *this_shn,char *filename) +{ + char *altfilename,*basefile,*basedir; + /* Checking if this_shn->config.relative_seek_tables_path is NULL */ + if(!(this_shn->config.relative_seek_tables_path)) + this_shn->config.relative_seek_tables_path = ""; + + if (0 == strcmp(this_shn->config.relative_seek_tables_path,"")) + return 0; + + + if (!(basefile = shn_get_base_filename(filename))) + { + return 0; + } + + if (!(basedir = shn_get_base_directory(filename))) + { + free(basefile); + return 0; + } + + if (!(altfilename = malloc(strlen(basedir)+strlen(this_shn->config.relative_seek_tables_path)+strlen(basefile)+sizeof(SEEK_SUFFIX)+4))) + { + shn_debug(this_shn->config, "Could not allocate memory for absolute filename"); + free(basefile); + free(basedir); + return 0; + } + + sprintf(altfilename,"%s/%s/%s.%s",basedir,this_shn->config.relative_seek_tables_path,basefile,SEEK_SUFFIX); + + free(basefile); + free(basedir); + + if (load_separate_seek_table_generic(altfilename,this_shn)) + { + free(altfilename); + return 1; + } + + free(altfilename); + return 0; +} + +int load_separate_seek_table_absolute(shn_file *this_shn,char *filename) +{ + char *altfilename,*basefile; + /* Checking this_shn->config.seek_tables_path */ + if(!(this_shn->config.seek_tables_path)) + this_shn->config.seek_tables_path = ""; + + if (!(basefile = shn_get_base_filename(filename))) + { + return 0; + } + + if (!(altfilename = malloc(strlen(this_shn->config.seek_tables_path)+strlen(basefile)+sizeof(SEEK_SUFFIX)+3))) + { + shn_debug(this_shn->config, "Could not allocate memory for same dir filename"); + free(basefile); + return 0; + } + + sprintf(altfilename,"%s/%s.%s",this_shn->config.seek_tables_path,basefile,SEEK_SUFFIX); + + free(basefile); + + if (load_separate_seek_table_generic(altfilename,this_shn)) + { + free(altfilename); + return 1; + } + + free(altfilename); + return 0; +} + +void shn_load_seek_table(shn_file *this_shn,char *filename) +{ + if (load_appended_seek_table(this_shn,filename,0)) + return; + + if (load_appended_seek_table(this_shn,filename,ID3V1_TAG_SIZE)) + return; + + if (load_separate_seek_table_samedir(this_shn,filename)) + return; + + if (load_separate_seek_table_relative(this_shn,filename)) + return; + + if (load_separate_seek_table_absolute(this_shn,filename)) + return; + + shn_debug(this_shn->config, "Could not find any seek tables"); +} diff --git a/Libraries/Shorten/Files/shorten/src/shorten.c b/Libraries/Shorten/Files/shorten/src/shorten.c new file mode 100644 index 000000000..772d08db2 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/shorten.c @@ -0,0 +1,54 @@ +/****************************************************************************** +* * +* Copyright (C) 1992-1995 Tony Robinson * +* * +* See the file doc/LICENSE.shorten for conditions on distribution and usage * +* * +******************************************************************************/ + +/* + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include "shorten.h" + +void init_offset(slong **offset,int nchan,int nblock,int ftype) +{ + slong mean = 0; + int chan, i; + + /* initialise offset */ + switch(ftype) + { + case TYPE_AU1: + case TYPE_S8: + case TYPE_S16HL: + case TYPE_S16LH: + case TYPE_ULAW: + case TYPE_AU2: + case TYPE_AU3: + case TYPE_ALAW: + mean = 0; + break; + case TYPE_U8: + mean = 0x80; + break; + case TYPE_U16HL: + case TYPE_U16LH: + mean = 0x8000; + break; + default: + fprintf(stderr, "Unknown file type: %d", ftype); + } + + for(chan = 0; chan < nchan; chan++) + for(i = 0; i < nblock; i++) + offset[chan][i] = mean; +} diff --git a/Libraries/Shorten/Files/shorten/src/sulawalaw.c b/Libraries/Shorten/Files/shorten/src/sulawalaw.c new file mode 100644 index 000000000..6a242c78e --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/sulawalaw.c @@ -0,0 +1,192 @@ +/* + * $Id$ + */ + +#include +#include +#include +#include +#include "shorten.h" + +int Sulaw2lineartab[] = {-32124, -31100, -30076, -29052, -28028, -27004, + -25980, -24956, -23932, -22908, -21884, -20860, -19836, -18812, + -17788, -16764, -15996, -15484, -14972, -14460, -13948, -13436, + -12924, -12412, -11900, -11388, -10876, -10364, -9852, -9340, -8828, + -8316, -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, -5884, + -5628, -5372, -5116, -4860, -4604, -4348, -4092, -3900, -3772, -3644, + -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492, -2364, + -2236, -2108, -1980, -1884, -1820, -1756, -1692, -1628, -1564, -1500, + -1436, -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876, + -844, -812, -780, -748, -716, -684, -652, -620, -588, -556, -524, + -492, -460, -428, -396, -372, -356, -340, -324, -308, -292, -276, + -260, -244, -228, -212, -196, -180, -164, -148, -132, -120, -112, + -104, -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, 23932, 22908, + 21884, 20860, 19836, 18812, 17788, 16764, 15996, 15484, 14972, 14460, + 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340, + 8828, 8316, 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884, + 5628, 5372, 5116, 4860, 4604, 4348, 4092, 3900, 3772, 3644, 3516, + 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364, 2236, 2108, + 1980, 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308, + 1244, 1180, 1116, 1052, 988, 924, 876, 844, 812, 780, 748, 716, 684, + 652, 620, 588, 556, 524, 492, 460, 428, 396, 372, 356, 340, 324, 308, + 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104, + 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0}; + +#ifndef Sulaw2linear +#ifdef __STDC__ +int Sulaw2linear(uchar ulaw) { +#else +int Sulaw2linear(ulaw) uchar ulaw; { +#endif + return(Sulaw2lineartab[ulaw]); +} +#endif + +/* adapted by ajr for int input */ +#ifdef __STDC__ +uchar Slinear2ulaw(int sample) { +#else +uchar Slinear2ulaw(sample) int sample; { +#endif +/* +** This routine converts from linear to ulaw. +** +** Craig Reese: IDA/Supercomputing Research Center +** Joe Campbell: Department of Defense +** 29 September 1989 +** +** References: +** 1) CCITT Recommendation G.711 (very difficult to follow) +** 2) "A New Digital Technique for Implementation of Any +** Continuous PCM Companding Law," Villeret, Michel, +** et al. 1973 IEEE Int. Conf. on Communications, Vol 1, +** 1973, pg. 11.12-11.17 +** 3) MIL-STD-188-113,"Interoperability and Performance Standards +** for Analog-to_Digital Conversion Techniques," +** 17 February 1987 +** +** Input: Signed 16 bit linear sample +** Output: 8 bit ulaw sample +*/ + +#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ +#define CLIP 32635 + + static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; + int sign, exponent, mantissa; + uchar ulawbyte; + + /* Get the sample into sign-magnitude. */ + if(sample < 0) { + sign = 0x80; + sample = -sample; + } + else + sign = 0; + + /* clip the magnitude */ + if(sample > CLIP) sample = CLIP; + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[( sample >> 7 ) & 0xFF]; + mantissa = (sample >> (exponent + 3)) & 0x0F; + ulawbyte = ~(sign | (exponent << 4) | mantissa); + + return(ulawbyte); +} + + +/****************** + * ALAW starts here + */ + +int Salaw2lineartab[] = {-5504, -5248, -6016, -5760, -4480, -4224, + -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, -3776, -3648, + -4032, -3904, -3264, -3136, -3520, -3392, -22016, -20992, -24064, + -23040, -17920, -16896, -19968, -18944, -30208, -29184, -32256, + -31232, -26112, -25088, -28160, -27136, -11008, -10496, -12032, + -11520, -8960, -8448, -9984, -9472, -15104, -14592, -16128, -15616, + -13056, -12544, -14080, -13568, -344, -328, -376, -360, -280, -264, + -312, -296, -472, -456, -504, -488, -408, -392, -440, -424, -88, -72, + -120, -104, -24, -8, -56, -40, -216, -200, -248, -232, -152, -136, + -184, -168, -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, -688, -656, + -752, -720, -560, -528, -624, -592, -944, -912, -1008, -976, -816, + -784, -880, -848, 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752, 2624, 3008, + 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136, + 3520, 3392, 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008, 10496, + 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616, + 13056, 12544, 14080, 13568, 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, 88, 72, 120, 104, 24, 8, 56, + 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376, 1312, 1504, 1440, + 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760, + 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, + 816, 784, 880, 848}; + +#ifndef Salaw2linear +#ifdef __STDC__ +int Salaw2linear(uchar alaw) { +#else +int Salaw2linear(alaw) uchar alaw; { +#endif + return(Salaw2lineartab[alaw]); +} +#endif + +/* this is derived from the Sun code - it is a bit simpler and has int input */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#ifdef __STDC__ +uchar Slinear2alaw(int linear) { +#else +uchar Slinear2alaw(linear) int linear; { +#endif + int seg; + uchar aval, mask; + static sshort seg_aend[NSEGS] = {0x1f,0x3f,0x7f,0xff,0x1ff,0x3ff,0x7ff,0xfff}; + + linear = linear >> 3; + + if(linear >= 0) { + mask = 0xd5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + linear = -linear - 1; + } + + /* Convert the scaled magnitude to segment number. */ + for(seg = 0; seg < NSEGS && linear > seg_aend[seg]; seg++); + + /* Combine the sign, segment, and quantization bits. */ + if(seg >= NSEGS) /* out of range, return maximum value. */ + return (uchar) (0x7F ^ mask); + else { + aval = (uchar) seg << SEG_SHIFT; + if (seg < 2) + aval |= (linear >> 1) & QUANT_MASK; + else + aval |= (linear >> seg) & QUANT_MASK; + return (aval ^ mask); + } +} diff --git a/Libraries/Shorten/Files/shorten/src/vario.c b/Libraries/Shorten/Files/shorten/src/vario.c new file mode 100644 index 000000000..c6c953bce --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/vario.c @@ -0,0 +1,146 @@ +/****************************************************************************** +* * +* Copyright (C) 1992-1995 Tony Robinson * +* * +* See the file doc/LICENSE.shorten for conditions on distribution and usage * +* * +******************************************************************************/ + +/* + * $Id$ + */ + +#include +#include +#include +#include "shorten.h" + +#define MASKTABSIZE 33 +ulong masktab[MASKTABSIZE]; + +void mkmasktab() { + int i; + ulong val = 0; + + masktab[0] = val; + for(i = 1; i < MASKTABSIZE; i++) { + val <<= 1; + val |= 1; + masktab[i] = val; + } +} + +void var_get_init(shn_file *this_shn) +{ + mkmasktab(); + + this_shn->decode_state->getbuf = (uchar*) pmalloc((ulong) BUFSIZ,this_shn); + this_shn->decode_state->getbufp = this_shn->decode_state->getbuf; + this_shn->decode_state->nbyteget = 0; + this_shn->decode_state->gbuffer = 0; + this_shn->decode_state->nbitget = 0; +} + +ulong word_get(shn_file *this_shn) +{ + ulong buffer; + int bytes; + + if(this_shn->decode_state->nbyteget < 4) + { + this_shn->vars.last_file_position = this_shn->vars.bytes_read; + + bytes = fread((uchar*) this_shn->decode_state->getbuf, 1, BUFSIZ, this_shn->vars.fd); + this_shn->decode_state->nbyteget += bytes; + + if(this_shn->decode_state->nbyteget < 4) { + shn_error_fatal(this_shn,"Premature EOF on compressed stream -\npossible corrupt or truncated file"); + return (ulong)0; + } + + this_shn->vars.bytes_read += bytes; + + this_shn->decode_state->getbufp = this_shn->decode_state->getbuf; + } + + buffer = (((slong) (this_shn->decode_state->getbufp[0])) << 24) | (((slong) (this_shn->decode_state->getbufp[1])) << 16) | + (((slong) (this_shn->decode_state->getbufp[2])) << 8) | ((slong) (this_shn->decode_state->getbufp[3])); + + this_shn->decode_state->getbufp += 4; + this_shn->decode_state->nbyteget -= 4; + + return(buffer); +} + +slong uvar_get(int nbin,shn_file *this_shn) +{ + slong result; + + if (this_shn->vars.reading_function_code) { + this_shn->vars.last_file_position_no_really = this_shn->vars.last_file_position; + } + + if(this_shn->decode_state->nbitget == 0) + { + this_shn->decode_state->gbuffer = word_get(this_shn); + if (this_shn->vars.fatal_error) + return (ulong)0; + this_shn->decode_state->nbitget = 32; + } + + for(result = 0; !(this_shn->decode_state->gbuffer & (1L << --(this_shn->decode_state->nbitget))); result++) + { + if(this_shn->decode_state->nbitget == 0) + { + this_shn->decode_state->gbuffer = word_get(this_shn); + if (this_shn->vars.fatal_error) + return (ulong)0; + this_shn->decode_state->nbitget = 32; + } + } + + while(nbin != 0) + { + if(this_shn->decode_state->nbitget >= nbin) + { + result = (result << nbin) | ((this_shn->decode_state->gbuffer >> (this_shn->decode_state->nbitget-nbin)) &masktab[nbin]); + this_shn->decode_state->nbitget -= nbin; + nbin = 0; + } + else + { + result = (result << this_shn->decode_state->nbitget) | (this_shn->decode_state->gbuffer & masktab[this_shn->decode_state->nbitget]); + this_shn->decode_state->gbuffer = word_get(this_shn); + if (this_shn->vars.fatal_error) + return (ulong)0; + nbin -= this_shn->decode_state->nbitget; + this_shn->decode_state->nbitget = 32; + } + } + + return(result); +} + +ulong ulong_get(shn_file *this_shn) +{ + unsigned int nbit = uvar_get(ULONGSIZE,this_shn); + if (this_shn->vars.fatal_error) + return (ulong)0; + return(uvar_get(nbit,this_shn)); +} + +slong var_get(int nbin,shn_file *this_shn) +{ + ulong uvar = uvar_get(nbin + 1,this_shn); + if (this_shn->vars.fatal_error) + return (slong)0; + + if(uvar & 1) return((slong) ~(uvar >> 1)); + else return((slong) (uvar >> 1)); +} + +void var_get_quit(shn_file *this_shn) +{ + free((void *) this_shn->decode_state->getbuf); + this_shn->decode_state->getbuf = NULL; +} diff --git a/Libraries/Shorten/Files/shorten/src/wave.c b/Libraries/Shorten/Files/shorten/src/wave.c new file mode 100644 index 000000000..2e71dce02 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/src/wave.c @@ -0,0 +1,267 @@ +/* wave.c - functions to parse and verify WAVE headers + * Copyright (C) 2000-2004 Jason Jordan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * $Id$ + */ + +#include +#include +#include +#include +#include "shorten.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +//int is_valid_file(shn_file *info) +/* determines whether the given filename (info->filename) is a regular file, and is readable */ +/*{ + struct stat sz; + FILE *f; + + if (0 != stat(info->wave_header.filename,&sz)) { + if (errno == ENOENT) + shn_error("cannot open '%s' because it does not exist",info->wave_header.filename); + else if (errno == EACCES) + shn_error("cannot open '%s' due to insufficient permissions",info->wave_header.filename); + else if (errno == EFAULT) + shn_error("cannot open '%s' due to bad address",info->wave_header.filename); + else if (errno == ENOMEM) + shn_error("cannot open '%s' because the kernel ran out of memory",info->wave_header.filename); + else if (errno == ENAMETOOLONG) + shn_error("cannot open '%s' because the file name is too long",info->wave_header.filename); + else + shn_error("cannot open '%s' due to an unknown problem",info->wave_header.filename); + return 0; + } + if (0 == S_ISREG(sz.st_mode)) { + if (S_ISLNK(sz.st_mode)) + shn_error("'%s' is a symbolic link, not a regular file",info->wave_header.filename); + else if (S_ISDIR(sz.st_mode)) + shn_error("'%s' is a directory, not a regular file",info->wave_header.filename); + else if (S_ISCHR(sz.st_mode)) + shn_error("'%s' is a character device, not a regular file",info->wave_header.filename); + else if (S_ISBLK(sz.st_mode)) + shn_error("'%s' is a block device, not a regular file",info->wave_header.filename); + else if (S_ISFIFO(sz.st_mode)) + shn_error("'%s' is a fifo, not a regular file",info->wave_header.filename); + else if (S_ISSOCK(sz.st_mode)) + shn_error("'%s' is a socket, not a regular file",info->wave_header.filename); + return 0; + } + info->wave_header.actual_size = (ulong)sz.st_size; + + if (NULL == (f = fopen(info->wave_header.filename,"rb"))) { + shn_error("could not open '%s': %s",info->wave_header.filename,strerror(errno)); + return 0; + } + fclose(f); + + return 1; +}*/ + +int shn_verify_header(shn_file *this_shn) +{ + ulong l; + int cur = 0; + +/* if (0 == is_valid_file(this_shn)) + { + shn_debug(this_shn->config, "while processing '%s': something went wrong while opening this file, see above",this_shn->wave_header.filename); + return 0; + }*/ + + if (this_shn->vars.bytes_in_header < CANONICAL_HEADER_SIZE) { + shn_debug(this_shn->config, "while processing '%s': header is only %d bytes (should be at least %d bytes)", + this_shn->wave_header.filename,this_shn->vars.bytes_in_header,CANONICAL_HEADER_SIZE); + return 0; + } + + if (WAVE_RIFF != shn_uchar_to_ulong_le(this_shn->vars.header+cur)) + { + if (AIFF_FORM == shn_uchar_to_ulong_le(this_shn->vars.header+cur)) + shn_debug(this_shn->config, "while processing '%s': file contains AIFF data, which is currently not supported",this_shn->wave_header.filename); + else + shn_debug(this_shn->config, "while processing '%s': WAVE header is missing RIFF tag - possible corrupt file",this_shn->wave_header.filename); + return 0; + } + cur += 4; + + this_shn->wave_header.chunk_size = shn_uchar_to_ulong_le(this_shn->vars.header+cur); + cur += 4; + + if (WAVE_WAVE != shn_uchar_to_ulong_le(this_shn->vars.header+cur)) + { + shn_debug(this_shn->config, "while processing '%s': WAVE header is missing WAVE tag",this_shn->wave_header.filename); + return 0; + } + cur += 4; + + for (;;) + { + cur += 4; + + l = shn_uchar_to_ulong_le(this_shn->vars.header+cur); + cur += 4; + + if (WAVE_FMT == shn_uchar_to_ulong_le(this_shn->vars.header+cur-8)) + break; + + cur += l; + } + + if (l < 16) + { + shn_debug(this_shn->config, "while processing '%s': fmt chunk in WAVE header was too short",this_shn->wave_header.filename); + return 0; + } + + this_shn->wave_header.wave_format = shn_uchar_to_ushort_le(this_shn->vars.header+cur); + cur += 2; + + switch (this_shn->wave_header.wave_format) + { + case WAVE_FORMAT_PCM: + break; + default: + shn_debug(this_shn->config, "while processing '%s': unsupported format 0x%04x (%s) - only PCM data is supported at this time", + this_shn->wave_header.filename,this_shn->wave_header.wave_format,shn_format_to_str(this_shn->wave_header.wave_format)); + return 0; + } + + this_shn->wave_header.channels = shn_uchar_to_ushort_le(this_shn->vars.header+cur); + cur += 2; + this_shn->wave_header.samples_per_sec = shn_uchar_to_ulong_le(this_shn->vars.header+cur); + cur += 4; + this_shn->wave_header.avg_bytes_per_sec = shn_uchar_to_ulong_le(this_shn->vars.header+cur); + cur += 4; + this_shn->wave_header.block_align = shn_uchar_to_ushort_le(this_shn->vars.header+cur); + cur += 2; + this_shn->wave_header.bits_per_sample = shn_uchar_to_ushort_le(this_shn->vars.header+cur); + cur += 2; + + if (this_shn->wave_header.bits_per_sample != 8 && this_shn->wave_header.bits_per_sample != 16) + { + shn_debug(this_shn->config, "while processing '%s': bits per sample is neither 8 nor 16",this_shn->wave_header.filename); + return 0; + } + + l -= 16; + + if (l > 0) + cur += l; + + for (;;) + { + cur += 4; + + l = shn_uchar_to_ulong_le(this_shn->vars.header+cur); + cur += 4; + + if (WAVE_DATA == shn_uchar_to_ulong_le(this_shn->vars.header+cur-8)) + break; + + cur += l; + } + + this_shn->wave_header.rate = ((uint)this_shn->wave_header.samples_per_sec * + (uint)this_shn->wave_header.channels * + (uint)this_shn->wave_header.bits_per_sample) / 8; + this_shn->wave_header.header_size = cur; + this_shn->wave_header.data_size = l; + this_shn->wave_header.total_size = this_shn->wave_header.chunk_size + 8; + this_shn->wave_header.length = this_shn->wave_header.data_size / this_shn->wave_header.rate; + this_shn->wave_header.exact_length = (double)this_shn->wave_header.data_size / (double)this_shn->wave_header.rate; + + if (this_shn->wave_header.channels == CD_CHANNELS && + this_shn->wave_header.bits_per_sample == CD_BITS_PER_SAMPLE && + this_shn->wave_header.samples_per_sec == CD_SAMPLES_PER_SEC && + this_shn->wave_header.avg_bytes_per_sec == CD_RATE && + this_shn->wave_header.rate == CD_RATE) + { + if (this_shn->wave_header.data_size < CD_MIN_BURNABLE_SIZE) + this_shn->wave_header.problems |= PROBLEM_CD_BUT_TOO_SHORT; + if (this_shn->wave_header.data_size % CD_BLOCK_SIZE != 0) + this_shn->wave_header.problems |= PROBLEM_CD_BUT_BAD_BOUND; + } + else + this_shn->wave_header.problems |= PROBLEM_NOT_CD_QUALITY; + + if (this_shn->wave_header.header_size != CANONICAL_HEADER_SIZE) + this_shn->wave_header.problems |= PROBLEM_HEADER_NOT_CANONICAL; + + if ((ulong)this_shn->wave_header.header_size + this_shn->wave_header.data_size > this_shn->wave_header.total_size) + this_shn->wave_header.problems |= PROBLEM_HEADER_INCONSISTENT; + + if ((ulong)this_shn->wave_header.header_size + this_shn->wave_header.data_size < this_shn->wave_header.total_size) + this_shn->wave_header.problems |= PROBLEM_EXTRA_CHUNKS; + + shn_length_to_str(this_shn); + + /* header looks ok */ + return 1; +} + +char *shn_format_to_str(ushort format) +{ + switch (format) { + case WAVE_FORMAT_UNKNOWN: + return "Microsoft Official Unknown"; + case WAVE_FORMAT_PCM: + return "Microsoft PCM"; + case WAVE_FORMAT_ADPCM: + return "Microsoft ADPCM"; + case WAVE_FORMAT_IEEE_FLOAT: + return "IEEE Float"; + case WAVE_FORMAT_ALAW: + return "Microsoft A-law"; + case WAVE_FORMAT_MULAW: + return "Microsoft U-law"; + case WAVE_FORMAT_OKI_ADPCM: + return "OKI ADPCM format"; + case WAVE_FORMAT_IMA_ADPCM: + return "IMA ADPCM"; + case WAVE_FORMAT_DIGISTD: + return "Digistd format"; + case WAVE_FORMAT_DIGIFIX: + return "Digifix format"; + case WAVE_FORMAT_DOLBY_AC2: + return "Dolby AC2"; + case WAVE_FORMAT_GSM610: + return "GSM 6.10"; + case WAVE_FORMAT_ROCKWELL_ADPCM: + return "Rockwell ADPCM"; + case WAVE_FORMAT_ROCKWELL_DIGITALK: + return "Rockwell DIGITALK"; + case WAVE_FORMAT_G721_ADPCM: + return "G.721 ADPCM"; + case WAVE_FORMAT_G728_CELP: + return "G.728 CELP"; + case WAVE_FORMAT_MPEG: + return "MPEG"; + case WAVE_FORMAT_MPEGLAYER3: + return "MPEG Layer 3"; + case WAVE_FORMAT_G726_ADPCM: + return "G.726 ADPCM"; + case WAVE_FORMAT_G722_ADPCM: + return "G.722 ADPCM"; + } + return "Unknown"; +} diff --git a/Libraries/Shorten/Files/shorten/util/Makefile.am b/Libraries/Shorten/Files/shorten/util/Makefile.am new file mode 100644 index 000000000..beefb669c --- /dev/null +++ b/Libraries/Shorten/Files/shorten/util/Makefile.am @@ -0,0 +1,5 @@ +noinst_PROGRAMS = mkbshift + +mkbshift_SOURCES = Sulawalaw.c array.c exit.c mkbshift.c + +EXTRA_DIST = mkbshift.h diff --git a/Libraries/Shorten/Files/shorten/util/Sulawalaw.c b/Libraries/Shorten/Files/shorten/util/Sulawalaw.c new file mode 100644 index 000000000..ddd7c4119 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/util/Sulawalaw.c @@ -0,0 +1,192 @@ +/* + * $Id$ + */ + +#include +#include +#include +#include +#include "mkbshift.h" + +int Sulaw2lineartab[] = {-32124, -31100, -30076, -29052, -28028, -27004, + -25980, -24956, -23932, -22908, -21884, -20860, -19836, -18812, + -17788, -16764, -15996, -15484, -14972, -14460, -13948, -13436, + -12924, -12412, -11900, -11388, -10876, -10364, -9852, -9340, -8828, + -8316, -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, -5884, + -5628, -5372, -5116, -4860, -4604, -4348, -4092, -3900, -3772, -3644, + -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492, -2364, + -2236, -2108, -1980, -1884, -1820, -1756, -1692, -1628, -1564, -1500, + -1436, -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876, + -844, -812, -780, -748, -716, -684, -652, -620, -588, -556, -524, + -492, -460, -428, -396, -372, -356, -340, -324, -308, -292, -276, + -260, -244, -228, -212, -196, -180, -164, -148, -132, -120, -112, + -104, -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, 23932, 22908, + 21884, 20860, 19836, 18812, 17788, 16764, 15996, 15484, 14972, 14460, + 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340, + 8828, 8316, 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884, + 5628, 5372, 5116, 4860, 4604, 4348, 4092, 3900, 3772, 3644, 3516, + 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364, 2236, 2108, + 1980, 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308, + 1244, 1180, 1116, 1052, 988, 924, 876, 844, 812, 780, 748, 716, 684, + 652, 620, 588, 556, 524, 492, 460, 428, 396, 372, 356, 340, 324, 308, + 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104, + 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0}; + +#ifndef Sulaw2linear +#ifdef __STDC__ +int Sulaw2linear(uchar ulaw) { +#else +int Sulaw2linear(ulaw) uchar ulaw; { +#endif + return(Sulaw2lineartab[ulaw]); +} +#endif + +/* adapted by ajr for int input */ +#ifdef __STDC__ +uchar Slinear2ulaw(int sample) { +#else +uchar Slinear2ulaw(sample) int sample; { +#endif +/* +** This routine converts from linear to ulaw. +** +** Craig Reese: IDA/Supercomputing Research Center +** Joe Campbell: Department of Defense +** 29 September 1989 +** +** References: +** 1) CCITT Recommendation G.711 (very difficult to follow) +** 2) "A New Digital Technique for Implementation of Any +** Continuous PCM Companding Law," Villeret, Michel, +** et al. 1973 IEEE Int. Conf. on Communications, Vol 1, +** 1973, pg. 11.12-11.17 +** 3) MIL-STD-188-113,"Interoperability and Performance Standards +** for Analog-to_Digital Conversion Techniques," +** 17 February 1987 +** +** Input: Signed 16 bit linear sample +** Output: 8 bit ulaw sample +*/ + +#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ +#define CLIP 32635 + + static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; + int sign, exponent, mantissa; + uchar ulawbyte; + + /* Get the sample into sign-magnitude. */ + if(sample < 0) { + sign = 0x80; + sample = -sample; + } + else + sign = 0; + + /* clip the magnitude */ + if(sample > CLIP) sample = CLIP; + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[( sample >> 7 ) & 0xFF]; + mantissa = (sample >> (exponent + 3)) & 0x0F; + ulawbyte = ~(sign | (exponent << 4) | mantissa); + + return(ulawbyte); +} + + +/****************** + * ALAW starts here + */ + +int Salaw2lineartab[] = {-5504, -5248, -6016, -5760, -4480, -4224, + -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, -3776, -3648, + -4032, -3904, -3264, -3136, -3520, -3392, -22016, -20992, -24064, + -23040, -17920, -16896, -19968, -18944, -30208, -29184, -32256, + -31232, -26112, -25088, -28160, -27136, -11008, -10496, -12032, + -11520, -8960, -8448, -9984, -9472, -15104, -14592, -16128, -15616, + -13056, -12544, -14080, -13568, -344, -328, -376, -360, -280, -264, + -312, -296, -472, -456, -504, -488, -408, -392, -440, -424, -88, -72, + -120, -104, -24, -8, -56, -40, -216, -200, -248, -232, -152, -136, + -184, -168, -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, -688, -656, + -752, -720, -560, -528, -624, -592, -944, -912, -1008, -976, -816, + -784, -880, -848, 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752, 2624, 3008, + 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136, + 3520, 3392, 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008, 10496, + 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616, + 13056, 12544, 14080, 13568, 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, 88, 72, 120, 104, 24, 8, 56, + 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376, 1312, 1504, 1440, + 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760, + 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, + 816, 784, 880, 848}; + +#ifndef Salaw2linear +#ifdef __STDC__ +int Salaw2linear(uchar alaw) { +#else +int Salaw2linear(alaw) uchar alaw; { +#endif + return(Salaw2lineartab[alaw]); +} +#endif + +/* this is derived from the Sun code - it is a bit simpler and has int input */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#ifdef __STDC__ +uchar Slinear2alaw(int linear) { +#else +uchar Slinear2alaw(linear) int linear; { +#endif + int seg; + uchar aval, mask; + static sshort seg_aend[NSEGS] = {0x1f,0x3f,0x7f,0xff,0x1ff,0x3ff,0x7ff,0xfff}; + + linear = linear >> 3; + + if(linear >= 0) { + mask = 0xd5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + linear = -linear - 1; + } + + /* Convert the scaled magnitude to segment number. */ + for(seg = 0; seg < NSEGS && linear > seg_aend[seg]; seg++); + + /* Combine the sign, segment, and quantization bits. */ + if(seg >= NSEGS) /* out of range, return maximum value. */ + return (uchar) (0x7F ^ mask); + else { + aval = (uchar) seg << SEG_SHIFT; + if (seg < 2) + aval |= (linear >> 1) & QUANT_MASK; + else + aval |= (linear >> seg) & QUANT_MASK; + return (aval ^ mask); + } +} diff --git a/Libraries/Shorten/Files/shorten/util/array.c b/Libraries/Shorten/Files/shorten/util/array.c new file mode 100644 index 000000000..3383d0d42 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/util/array.c @@ -0,0 +1,63 @@ +/****************************************************************************** +* * +* Copyright (C) 1992-1995 Tony Robinson * +* * +* See the file doc/LICENSE.shorten for conditions on distribution and usage * +* * +******************************************************************************/ + +/* + * $Id$ + */ + +#include +#include +#include "mkbshift.h" + +void *pmalloc(size) ulong size; { + void *ptr; + +#if defined(DOS_MALLOC_FEATURE) && !defined(_WINDOWS) /* mrhmod */ + fprintf(stderr, "requesting %ld bytes: ", size); +#endif + ptr = malloc(size); +#if defined(DOS_MALLOC_FEATURE) && !defined(_WINDOWS) /* mrhmod */ + if(ptr == NULL) + fprintf(stderr, "denied\n"); + else + fprintf(stderr, "accepted\n"); +#endif + + if(ptr == NULL) + perror_exit("call to malloc(%ld) failed in pmalloc()", size); + + return(ptr); +} + +slong **long2d(n0, n1) ulong n0, n1; { + slong **array0; + + if((array0 = (slong**) pmalloc((ulong) (n0 * sizeof(slong*) + + n0 * n1 * sizeof(slong)))) != NULL ) { + slong *array1 = (slong*) (array0 + n0); + int i; + + for(i = 0; i < n0; i++) + array0[i] = array1 + i * n1; + } + return(array0); +} + +float **float2d(n0, n1) ulong n0, n1; { + float **array0; + + if((array0 = (float**) pmalloc((ulong) (n0 * sizeof(float*) + + n0 * n1 * sizeof(float)))) != NULL ) { + float *array1 = (float*) (array0 + n0); + int i; + + for(i = 0; i < n0; i++) + array0[i] = array1 + i * n1; + } + return(array0); +} diff --git a/Libraries/Shorten/Files/shorten/util/exit.c b/Libraries/Shorten/Files/shorten/util/exit.c new file mode 100644 index 000000000..22db23890 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/util/exit.c @@ -0,0 +1,230 @@ +/****************************************************************************** +* * +* Copyright (C) 1992-1995 Tony Robinson * +* * +* See the file doc/LICENSE.shorten for conditions on distribution and usage * +* * +******************************************************************************/ + +/* + * $Id$ + */ + +#include +#include +#include +#ifndef MSDOS +#include +#include +#endif +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDARG_H +#include +#else +#include +#endif + +#include "mkbshift.h" + +extern char *argv0; +extern char *filenameo; +extern FILE *fileo; + +#ifdef _WINDOWS +/* mrhmod - warn about attempt to use stderr (use perror_exit()/error_exit() instead) */ +char *stderrWarningMsg = "caught attempt to use stderr"; +#endif + +jmp_buf exitenv; +char *exitmessage; + +/***************************************************************************/ + +void basic_exit(exitcode) int exitcode; { + + /* try to delete the output file on all abnormal exit conditions */ + if(exitcode != 0 && fileo != NULL && fileo != stdout) + { + fclose(fileo); + unlink(filenameo); + } + + if(exitmessage == NULL) + exit(exitcode < 0 ? 0 : exitcode); + else + longjmp(exitenv, exitcode); +} + +/**************************************************************************** +** error_exit() - standard error handler with printf() syntax +*/ +# ifdef HAVE_STDARG_H +void error_exit(char* fmt, ...) { + va_list args; + + va_start(args, fmt); +# else +void error_exit(va_alist) va_dcl { + va_list args; + char *fmt; + + va_start(args); + fmt = va_arg(args, char*); +# endif + + if(exitmessage == NULL) + { +/* +#if defined(_WINDOWS) && defined(_DEBUG) && !defined(WIN32) + _asm { int 3 } / * mrhmod - catch if debugging * / +#endif +*/ + +#ifndef _WINDOWS /* mrhmod - must use exitmessage 'cos stderr not available */ + fprintf(stderr, "%s: ", argv0); + (void) vfprintf(stderr, fmt, args); +#endif /* _WINDOWS */ + } + else + { + (void) vsprintf(exitmessage, fmt, args); + strcat(exitmessage, "\n"); + } + + va_end(args); + + basic_exit(errno); +} + +/**************************************************************************** +** perror_exit() - system error handler with printf() syntax +** +** Appends system error message based on errno +*/ +# ifdef HAVE_STDARG_H +void perror_exit(char* fmt, ...) { + va_list args; + + va_start(args, fmt); +# else +void perror_exit(va_alist) va_dcl { + va_list args; + char *fmt; + + va_start(args); + fmt = va_arg(args, char*); +# endif + + if(exitmessage == NULL) { +/* +#if defined(_WINDOWS) && defined(_DEBUG) && !defined(WIN32) + _asm { int 3 } / * mrhmod - catch if debugging * / +#endif +*/ + +#ifndef _WINDOWS /* mrhmod - must use exitmessage 'cos stderr not available */ + fprintf(stderr, "%s: ", argv0); + (void) vfprintf(stderr, fmt, args); + (void) fprintf(stderr, ": "); +#ifndef MSDOS + perror("\0"); +#endif + +#endif /* _WINDOWS */ + } + else { + (void) vsprintf(exitmessage, fmt, args); + strcat(exitmessage, ": "); + strcat(exitmessage,strerror(errno)); + strcat(exitmessage, "\n"); + } + + va_end(args); + + basic_exit(errno); +} + +# ifdef HAVE_STDARG_H +void usage_exit(int exitcode, char* fmt, ...) { + va_list args; + + va_start(args, fmt); +# else +void usage_exit(va_alist) va_dcl { + va_list args; + int exitcode; + char *fmt; + + va_start(args); + exitcode = va_arg(args, int); + fmt = va_arg(args, char*); +# endif + + if(exitmessage == NULL) { +#if defined(_WINDOWS) && defined(_DEBUG) && !defined(WIN32) + _asm { int 3 } /* mrhmod - catch if debugging */ +#endif + +#ifndef _WINDOWS /* mrhmod - must use exitmessage 'cos stderr not available */ + if(fmt != NULL) { + fprintf(stderr, "%s: ", argv0); + (void) vfprintf(stderr, fmt, args); + } + fprintf(stderr, "%s: for more information use: %s -h\n", argv0, argv0); +#endif /* _WINDOWS */ + } + else + { + (void) vsprintf(exitmessage, fmt, args); + strcat(exitmessage, "\n"); + } + + va_end(args); + + basic_exit(exitcode); +} + + +# ifdef HAVE_STDARG_H +void update_exit(int exitcode, char* fmt, ...) { + va_list args; + + va_start(args, fmt); +# else +void update_exit(va_alist) va_dcl { + va_list args; + int exitcode; + char *fmt; + + va_start(args); + exitcode = va_arg(args, int); + fmt = va_arg(args, char*); +# endif + + if(exitmessage == NULL) { +#if defined(_WINDOWS) && defined(_DEBUG) && !defined(WIN32) + _asm { int 3 } /* mrhmod - catch if debugging */ +#endif + +#ifndef _WINDOWS /* mrhmod - must use exitmessage 'cos stderr not available */ + if(fmt != NULL) { + fprintf(stderr, "%s: ", argv0); + (void) vfprintf(stderr, fmt, args); + } + fprintf(stderr, "%s: version %s\n",argv0,VERSION); + fprintf(stderr, "%s: please report this problem to ajr@softsound.com\n", argv0); +#endif /* _WINDOWS */ + } +#ifdef _WINDOWS /* mrhmod - output something */ + error_exit( fmt, args ); +#endif + + va_end(args); + + basic_exit(exitcode); +} diff --git a/Libraries/Shorten/Files/shorten/util/mkbshift.c b/Libraries/Shorten/Files/shorten/util/mkbshift.c new file mode 100644 index 000000000..61ef5d34c --- /dev/null +++ b/Libraries/Shorten/Files/shorten/util/mkbshift.c @@ -0,0 +1,110 @@ +/****************************************************************************** +* * +* Copyright (C) 1992-1995 Tony Robinson * +* * +* See the file doc/LICENSE.shorten for conditions on distribution and usage * +* * +******************************************************************************/ + +/* + * $Id$ + */ + +#include +#include "mkbshift.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define USIZE 256 +#define HUSIZE 128 +#define SHIFTSIZE 13 + +char *argv0 = "mkbshift"; +char *filenameo = NULL; +FILE *fileo = NULL; + +int main() { + FILE *fout; + char *filename = "bitshift.h", *writemode = "w"; + int shift, i; + int tab[USIZE]; + slong sample; + slong **forwardmap = long2d((ulong) SHIFTSIZE, (ulong) USIZE); + slong **reversemap = long2d((ulong) SHIFTSIZE, (ulong) USIZE); + + fout = fopen(filename, writemode); + if(fout == NULL) + perror_exit("fopen(\"%s\", \"%s\")", filename, writemode); + + for(i = 0; i < USIZE; i++) tab[i] = 0; + + /* brute force search of the largest number of zero bits in a linear value */ + for(shift = 0; shift < SHIFTSIZE; shift++) + for(sample = -(1L << 15); sample < (1L << 15); sample += 1 << (shift + 3)) + tab[Slinear2ulaw(sample)] = shift; + + /* print this out as a lookup table */ + fprintf(fout, "char ulaw_maxshift[%d] = {", USIZE); + for(i = 0; i < USIZE - 1; i++) + fprintf(fout, "%d,", tab[i]); + fprintf(fout, "%d};\n\n", tab[USIZE - 1]); + + /* compute the greatest inward shift compatable with ??? */ + for(shift = 0; shift < SHIFTSIZE; shift++) { + int nused; + + nused = 0; + for(i = 255; i >= 128; i--) + if(tab[i] >= shift) forwardmap[shift][i] = nused++; + for(i = 255; i >= 128; i--) + if(tab[i] < shift) forwardmap[shift][i] = nused++; + + nused = -1; + for(i = 126; i >= 0; i--) + if(tab[i] >= shift) forwardmap[shift][i] = nused--; + forwardmap[shift][127] = nused--; + for(i = 126; i >= 0; i--) + if(tab[i] < shift) forwardmap[shift][i] = nused--; + + for(i = 0; i < USIZE; i++) + reversemap[shift][forwardmap[shift][i] + HUSIZE] = i; + } + + /* simple check */ + for(shift = 0; shift < SHIFTSIZE; shift++) + for(i = 0; i < USIZE; i++) + if(forwardmap[shift][reversemap[shift][i]] != i - HUSIZE) + error_exit("identity maping failed for shift: %d\tindex: %d\n",shift,i); + + /* print out the ulaw_inward lookup table */ + fprintf(fout, "schar ulaw_inward[%d][%d] = {\n", SHIFTSIZE, USIZE); + for(shift = 0; shift < SHIFTSIZE; shift++) { + fprintf(fout, "{"); + for(i = 0; i < USIZE - 1; i++) + fprintf(fout, "%ld,", forwardmap[shift][i]); + if(shift != SHIFTSIZE - 1) + fprintf(fout, "%ld},\n", forwardmap[shift][USIZE - 1]); + else + fprintf(fout, "%ld}\n};\n", forwardmap[shift][USIZE - 1]); + } + fprintf(fout, "\n"); + + /* print out the ulaw_outward lookup table */ + fprintf(fout, "uchar ulaw_outward[%d][%d] = {\n", SHIFTSIZE, USIZE); + for(shift = 0; shift < SHIFTSIZE; shift++) { + fprintf(fout, "{"); + for(i = 0; i < USIZE - 1; i++) + fprintf(fout, "%ld,", reversemap[shift][i]); + if(shift != SHIFTSIZE - 1) + fprintf(fout, "%ld},\n", reversemap[shift][USIZE - 1]); + else + fprintf(fout, "%ld}\n};\n", reversemap[shift][USIZE - 1]); + } + + fclose(fout); + + /* exit happy */ + return(0); +} diff --git a/Libraries/Shorten/Files/shorten/util/mkbshift.h b/Libraries/Shorten/Files/shorten/util/mkbshift.h new file mode 100644 index 000000000..d4f5ec229 --- /dev/null +++ b/Libraries/Shorten/Files/shorten/util/mkbshift.h @@ -0,0 +1,237 @@ +/****************************************************************************** +* * +* Copyright (C) 1992-1995 Tony Robinson * +* * +* See the file doc/LICENSE.shorten for conditions on distribution and usage * +* * +******************************************************************************/ + +/* + * $Id$ + */ + +#ifndef _MKBSHIFT_H +#define _MKBSHIFT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_STRERROR +extern char *sys_errlist[]; +#define strerror(x) sys_errlist[x] +#endif + +#define MAGIC "ajkg" +#define FORMAT_VERSION 2 +#define MIN_SUPPORTED_VERSION 1 +#define MAX_SUPPORTED_VERSION 3 +#define MAX_VERSION 7 + +#define UNDEFINED_UINT -1 +#define DEFAULT_BLOCK_SIZE 256 +#define DEFAULT_V0NMEAN 0 +#define DEFAULT_V2NMEAN 4 +#define DEFAULT_MAXNLPC 0 +#define DEFAULT_NCHAN 1 +#define DEFAULT_NSKIP 0 +#define DEFAULT_NDISCARD 0 +#define NBITPERLONG 32 +#define DEFAULT_MINSNR 256 +#define DEFAULT_MAXRESNSTR "32.0" +#define DEFAULT_QUANTERROR 0 +#define MINBITRATE 2.5 + +#define MAX_LPC_ORDER 64 +#define CHANSIZE 0 +#define ENERGYSIZE 3 +#define BITSHIFTSIZE 2 +#define NWRAP 3 + +#define FNSIZE 2 +#define FN_DIFF0 0 +#define FN_DIFF1 1 +#define FN_DIFF2 2 +#define FN_DIFF3 3 +#define FN_QUIT 4 +#define FN_BLOCKSIZE 5 +#define FN_BITSHIFT 6 +#define FN_QLPC 7 +#define FN_ZERO 8 +#define FN_VERBATIM 9 + +#define VERBATIM_CKSIZE_SIZE 5 /* a var_put code size */ +#define VERBATIM_BYTE_SIZE 8 /* code size 8 on single bytes means + * no compression at all */ +#define VERBATIM_CHUNK_MAX 256 /* max. size of a FN_VERBATIM chunk */ + +#define ULONGSIZE 2 +#define NSKIPSIZE 1 +#define LPCQSIZE 2 +#define LPCQUANT 5 +#define XBYTESIZE 7 + +#define TYPESIZE 4 +#define TYPE_AU1 0 /* original lossless ulaw */ +#define TYPE_S8 1 /* signed 8 bit characters */ +#define TYPE_U8 2 /* unsigned 8 bit characters */ +#define TYPE_S16HL 3 /* signed 16 bit shorts: high-low */ +#define TYPE_U16HL 4 /* unsigned 16 bit shorts: high-low */ +#define TYPE_S16LH 5 /* signed 16 bit shorts: low-high */ +#define TYPE_U16LH 6 /* unsigned 16 bit shorts: low-high */ +#define TYPE_ULAW 7 /* lossy ulaw: internal conversion to linear */ +#define TYPE_AU2 8 /* new ulaw with zero mapping */ +#define TYPE_AU3 9 /* lossless alaw */ +#define TYPE_ALAW 10 /* lossy alaw: internal conversion to linear */ +#define TYPE_RIFF_WAVE 11 /* Microsoft .WAV files */ +#define TYPE_EOF 12 +#define TYPE_GENERIC_ULAW 128 +#define TYPE_GENERIC_ALAW 129 + +#define POSITIVE_ULAW_ZERO 0xff +#define NEGATIVE_ULAW_ZERO 0x7f + +#undef BOOL +#undef TRUE +#undef FALSE +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +#ifndef MAX_PATH +#define MAX_PATH 2048 +#endif + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +#if defined(unix) && !defined(linux) +#define labs abs +#endif + +#define ROUNDEDSHIFTDOWN(x, n) (((n) == 0) ? (x) : ((x) >> ((n) - 1)) >> 1) + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* BUFSIZ must be a multiple of four to contain a whole number of words */ +#ifdef BUFSIZ +#undef BUFSIZ +#endif +#define BUFSIZ 512 + +#define putc_exit(val, stream)\ +{ char rval;\ + if((rval = putc((val), (stream))) != (char) (val))\ + update_exit(1, "write failed: putc returns EOF\n");\ +} + +extern int getc_exit_val; +#define getc_exit(stream)\ +(((getc_exit_val = getc(stream)) == EOF) ? \ + update_exit(1, "read failed: getc returns EOF\n"), 0: getc_exit_val) + +#ifdef HAVE_INTTYPES_H +# include +#else +# if SIZEOF_UNSIGNED_LONG == 4 +# define uint32_t unsigned long +# define int32_t long +# else +# define uint32_t unsigned int +# define int32_t int +# endif +# define uint16_t unsigned short +# define uint8_t unsigned char +# define int16_t short +# define int8_t char +#endif + +#undef ulong +#undef ushort +#undef uchar +#undef slong +#undef sshort +#undef schar +#define ulong uint32_t +#define ushort uint16_t +#define uchar uint8_t +#define slong int32_t +#define sshort int16_t +#define schar int8_t + +#if defined(__STDC__) || defined(__GNUC__) || defined(sgi) || !defined(unix) +#define PROTO(ARGS) ARGS +#else +#define PROTO(ARGS) () +#endif + +#ifdef NEED_OLD_PROTOTYPES +/*******************************************/ +/* this should be in string.h or strings.h */ +extern int strcmp PROTO ((const char*, const char*)); +extern char* strcpy PROTO ((char*, const char*)); +extern char* strcat PROTO ((char*, const char*)); +extern int strlen PROTO ((const char*)); + +/**************************************/ +/* defined in stdlib.h if you have it */ +extern void* malloc PROTO ((unsigned long)); +extern void free PROTO ((void*)); +extern int atoi PROTO ((const char*)); +extern void swab PROTO ((char*, char*, int)); +extern int fseek PROTO ((FILE*, long, int)); + +/***************************/ +/* other misc system calls */ +extern int unlink PROTO ((const char*)); +extern void exit PROTO ((int)); +#endif + +/**************************/ +/* defined in Sulawalaw.c */ +extern int Sulaw2lineartab[]; +#define Sulaw2linear(i) (Sulaw2lineartab[i]) +#ifndef Sulaw2linear +extern int Sulaw2linear PROTO((uchar)); +#endif +extern uchar Slinear2ulaw PROTO((int)); + +extern int Salaw2lineartab[]; +#define Salaw2linear(i) (Salaw2lineartab[i]) +#ifndef Salaw2linear +extern int Salaw2linear PROTO((uchar)); +#endif +extern uchar Slinear2alaw PROTO((int)); + +/*********************/ +/* defined in exit.c */ +extern void basic_exit PROTO ((int)); +#ifdef HAVE_STDARG_H +extern void error_exit PROTO ((char*,...)); +extern void perror_exit PROTO ((char*,...)); +extern void usage_exit PROTO ((int, char*,...)); +extern void update_exit PROTO ((int, char*,...)); +#else +extern void error_exit PROTO (()); +extern void perror_exit PROTO (()); +extern void usage_exit PROTO (()); +extern void update_exit PROTO (()); +#endif + +/**********************/ +/* defined in array.c */ +extern void* pmalloc PROTO ((ulong)); +extern slong** long2d PROTO ((ulong, ulong)); + +#endif diff --git a/Libraries/Shorten/Files/src/Makefile.am b/Libraries/Shorten/Files/src/Makefile.am new file mode 100644 index 000000000..e47c131c5 --- /dev/null +++ b/Libraries/Shorten/Files/src/Makefile.am @@ -0,0 +1,7 @@ +libdir = `lamip --plugin-dir` +lib_LTLIBRARIES = libinputshorten.la + +INCLUDES = -I. -I.. -I$(top_srcdir)/shorten -I$(top_srcdir)/shorten/util -I$(top_srcdir)/shorten/src +libinputshorten_la_LDFLAGS = -module -avoid-version +libinputshorten_la_LIBADD = $(top_srcdir)/shorten/src/libshorten.la +libinputshorten_la_SOURCES = libinputshorten.c diff --git a/Libraries/Shorten/Files/src/libinputshorten.c b/Libraries/Shorten/Files/src/libinputshorten.c new file mode 100644 index 000000000..c538dac6b --- /dev/null +++ b/Libraries/Shorten/Files/src/libinputshorten.c @@ -0,0 +1,250 @@ +/* + * lamip input plugin - Shorten decoder + * + * + * well... first version is full of memory leaks i guess :) +*/ + +/* General includes */ +#include +#include +#include +#include +#include +#include + +/* General includes for shorten */ +#include "decode.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* we declare the functions we use in the plugin here */ +static int shorten_init(const lamipPluginHandle *); +static void shorten_cleanup(void); +static void shorten_decode(lamipURL *, int); +static void shorten_songinfo(lamipURL *,lamipSonginfo *); + +/* We set the functions in the InputPlugin struct... */ +static InputPlugin shorten_functions; +InputPlugin *lamip_input_info(void) +{ + shorten_functions.common.name = "inputSHORTEN"; + shorten_functions.common.description = "plays *.shn - Shorten"; + shorten_functions.common.init = shorten_init; + shorten_functions.common.cleanup = shorten_cleanup; + shorten_functions.decode = shorten_decode; + shorten_functions.set_song_info = shorten_songinfo; + + return(&shorten_functions); +} + +/* we set the module PCM format in this struct... like every input plugin should do by now */ +static lamipPCMInfo pcmi; + +/* some functions */ + +/* all things for shorten decoder */ +shn_file *shnfile; +shn_config shn_cfg; +static uchar *real_buffer = (uchar *)NULL; + +#define CONFIG_ERROR_OUTPUT_METHOD "error_output_method" +#define CONFIG_SEEK_TABLES_PATH "seek_tables_path" +#define CONFIG_RELATIVE_SEEK_TABLES_PATH "relative_seek_tables_path" +#define CONFIG_VERBOSE "verbose" +#define CONFIG_SWAP_BYTES "swap_bytes" + +#define NUM_BUFFER_BLOCKS 4096L + +static int shorten_init(const lamipPluginHandle *handle) +{ + /* Setting when shorten decoder should get active */ + lamipPluginHandle *config = (lamipPluginHandle *)handle; + lamip_set_mime_type(config, ".shn", NULL); + + /* Initializing the shn_cfg struct, we config it later anyway */ + shn_cfg.error_output_method = ERROR_OUTPUT_DEVNULL; + shn_cfg.seek_tables_path = NULL; + shn_cfg.relative_seek_tables_path = NULL; + shn_cfg.verbose = 0; + shn_cfg.swap_bytes = 0; + + /* Necessary config variables */ + char *val_error_output_method; + char *val_seek_tables_path; + char *val_relative_seek_tables_path; + int val_verbose; + int val_swap_bytes; + + /* config: error output */ + if(!lamip_cfg_getExist(handle, CONFIG_ERROR_OUTPUT_METHOD)) { + lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default DEVNULL...\n", CONFIG_ERROR_OUTPUT_METHOD); + lamip_send_message("SHORTEN: shorten_init: possible values for %s are \"DEVNULL\", \"STDERR\"\n", CONFIG_ERROR_OUTPUT_METHOD); + val_error_output_method = strdup("DEVNULL"); + lamip_cfg_set(config, CONFIG_ERROR_OUTPUT_METHOD, val_error_output_method); + } else { + val_error_output_method = lamip_cfg_get(handle, CONFIG_ERROR_OUTPUT_METHOD); + } + if(strcasecmp(val_error_output_method, "DEVNULL") == 0) + shn_cfg.error_output_method = ERROR_OUTPUT_DEVNULL; + else if(strcasecmp(val_error_output_method, "STDERR") == 0) + shn_cfg.error_output_method = ERROR_OUTPUT_STDERR; + else { + lamip_send_message("SHORTEN: shorten_init: Wrong value for %s found! Resetting to default DEVNULL...\n"); + lamip_send_message("SHORTEN: shorten_init: possible values for %s are \"DEVNULL\", \"STDERR\", \"WINDOW\"\n", CONFIG_ERROR_OUTPUT_METHOD); + val_error_output_method = strdup("DEVNULL"); + lamip_cfg_set(config, CONFIG_ERROR_OUTPUT_METHOD, val_error_output_method); + shn_cfg.error_output_method = ERROR_OUTPUT_DEVNULL; + } + + /* config: absolute seek tables path */ + if(!lamip_cfg_getExist(handle, CONFIG_SEEK_TABLES_PATH)) { + lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default...\n", CONFIG_SEEK_TABLES_PATH); + val_seek_tables_path = strdup("/tmp"); + lamip_cfg_set(config, CONFIG_SEEK_TABLES_PATH, val_seek_tables_path); + } else { + val_seek_tables_path = lamip_cfg_get(handle, CONFIG_SEEK_TABLES_PATH); + } + shn_cfg.seek_tables_path = strdup(val_seek_tables_path); + + /* config: relative seek tables path */ + if(!lamip_cfg_getExist(handle, CONFIG_RELATIVE_SEEK_TABLES_PATH)) { + lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default...\n", CONFIG_RELATIVE_SEEK_TABLES_PATH); + val_relative_seek_tables_path = strdup(""); + lamip_cfg_set(config, CONFIG_RELATIVE_SEEK_TABLES_PATH, val_relative_seek_tables_path); + } else { + val_relative_seek_tables_path = lamip_cfg_get(handle, CONFIG_RELATIVE_SEEK_TABLES_PATH); + } + shn_cfg.relative_seek_tables_path = strdup(val_relative_seek_tables_path); + + /* config: verbose */ + if(!lamip_cfg_getExist(handle, CONFIG_VERBOSE)) { + lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default...\n", CONFIG_VERBOSE); + val_verbose = 0; + lamip_cfg_setBool(config, CONFIG_VERBOSE, val_verbose); + } else { + val_verbose = lamip_cfg_getBool(handle, CONFIG_VERBOSE); + } + shn_cfg.verbose = val_verbose; + + /* config: swap bytes */ + if(!lamip_cfg_getExist(handle, CONFIG_SWAP_BYTES)) { + lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default...\n", CONFIG_SWAP_BYTES); + val_swap_bytes = 0; + lamip_cfg_setBool(config, CONFIG_SWAP_BYTES, val_swap_bytes); + } else { + val_swap_bytes = lamip_cfg_getBool(handle, CONFIG_SWAP_BYTES); + } + shn_cfg.swap_bytes = val_swap_bytes; + + /* Config cleanup */ + free(val_error_output_method); + free(val_seek_tables_path); + free(val_relative_seek_tables_path); + + return 1; +} + +static void shorten_cleanup(void) +{ + return; +} + +static void shorten_decode(lamipURL *url, int subtrack) +{ + if(!url) { + lamip_send_message("SHORTEN: shorten_decode: Got no url!\n"); + return; + } + char *filename = lamip_url_getURL(url); + if(!filename) { + lamip_send_message("SHORTEN: shorten_decode: Got no filename! We cannot play from stream by now!\n"); + return; + } + shnfile = shn_load(filename, shn_cfg); + if(!shnfile) { + lamip_send_message("SHORTEN: shorten_decode: Error in opening file! Give it another try with skipping id3v2...\n"); + return; + } + if(!shn_init_decoder(shnfile)) { + lamip_send_message("SHORTEN: shorten_decode: shn_init_decoder() failed! Aborting...\n"); + shn_unload(shnfile); + shnfile = (shn_file *)NULL; + return; + } + + pcmi.channels = shn_get_channels(shnfile); + pcmi.samplerate = shn_get_samplerate(shnfile); + switch(shn_get_bitspersample(shnfile)) { + case 8: + pcmi.format = PCM_FORMAT_U8; + break; + case 16: + pcmi.format = PCM_FORMAT_S16_LE; + break; + /* Next two bit depths aren't supported by shorten anyway */ + case 24: + pcmi.format = PCM_FORMAT_S24_LE; + break; + case 32: + pcmi.format = PCM_FORMAT_S32_LE; + break; + default: + lamip_send_message("SHORTEN: shorten_decode: Not supported bits_per_sample format!"); + } + + /* Getting a clean buffer */ + int buffer_size = shn_get_buffer_block_size(shnfile, NUM_BUFFER_BLOCKS); + if(real_buffer) { + free(real_buffer); + real_buffer = (uchar *)NULL; + } + if(!real_buffer) { + real_buffer = (uchar *)malloc(20000); + if(!real_buffer) { + lamip_send_message("SHORTEN: shorten_decode: malloc for real_buffer failed! Aborting...\n"); + shn_unload(shnfile); + shnfile = (shn_file *)NULL; + return; + } + } + int read_buffer; + int seekable = shn_seekable(shnfile); + lamip_open(&pcmi, shn_get_song_length(shnfile)); + while(lamip_isContinue()) { + read_buffer = shn_read(shnfile, real_buffer, buffer_size); + if(read_buffer <= 0) { + lamip_drain(); + break; + } + + lamip_writeData((uchar *)real_buffer, buffer_size); + + /* If seeking */ + if(lamip_isSeek() && seekable) + if(!(shn_seek(shnfile, (unsigned int)(lamip_isSeekGetAndReset() / 1000)))) + lamip_send_message("SHORTEN: shorten_decode: Seeking failed!\n"); + } + lamip_close(); + if(!shn_cleanup_decoder(shnfile)) + lamip_send_message("SHORTEN: shorten_decode: shn_cleanup_decoder() failed!\n"); + shn_unload(shnfile); + shnfile = NULL; + if(real_buffer) { + free((uchar *)real_buffer); + real_buffer = (uchar *)NULL; + } + return; +} + +static void shorten_songinfo(lamipURL* url, lamipSonginfo* songinfo) +{ + /* TODO - perhaps it ain't necessary... due to a changing lamip core :) */ + return; +} + diff --git a/Libraries/WavPack/Files/AUTHORS b/Libraries/WavPack/Files/AUTHORS new file mode 100644 index 000000000..e69de29bb diff --git a/Libraries/WavPack/Files/ChangeLog b/Libraries/WavPack/Files/ChangeLog new file mode 100644 index 000000000..4daae17d1 --- /dev/null +++ b/Libraries/WavPack/Files/ChangeLog @@ -0,0 +1,106 @@ + --------------------------- + Release 4.2 - April 2, 2005 + --------------------------- + + wavpack.exe (command-line encoder) - 4.2 + ---------------------------------------- + fixed: handling of wav files larger than 2 gig + improved: stereo lossless encoding speed (including "extra" mode) + added: -i option to ignore length specified in wav header + added: -w option to write APEv2 tags directly from command line + + wvunpack.exe (command-line decoder) - 4.2 + ----------------------------------------- + improved: decoding speed + + in_wv.dll (winamp plugin) - 2.2 + ------------------------------- + added: winamp media library support + improved: decoding speed + + foo_wavpack.dll (foobar plugin) - 2.2 + ------------------------------------- + improved: decoding speed + + nxWavPack.dll (Nero plugin) - 1.1 + Cool_wv4.flt (CoolEdit / Audition filter) - 2.4 + ----------------------------------------------- + fixed: handling of wav files larger than 2 gig + improved: encoding and decoding speed + + WavPack Library Source Code - 4.2 + --------------------------------- + improved: encoding and decoding speed + fixed: works correctly with 64-bit compilers + added: mode bit to open files in "streaming" mode + + + -------------------------- + Update - December 12, 2004 + -------------------------- + + WavPack_Apollo.dll (Apollo plugin) - 1.2 + ---------------------------------------- + fixed: crash when Apollo opened and WavPack plugin can't find config file + + + -------------------------------- + Release 4.1 - September 14, 2004 + -------------------------------- + + wavpack.exe (command-line encoder) - 4.1 + ---------------------------------------- + fixed: hybrid mode + "extra" mode + very low bitrates making corrupt files + fixed: mono or multichannel files causing crash (no corruption possible) + added: third name specification for "correction" file (EAC specific) + added: -t option to preserve timestamps + added: error summary for batch mode + + wvunpack.exe (command-line decoder) - 4.1 + ----------------------------------------- + fixed: hybrid mode decoding bugs (very obscure situations) + added: -s option to dump file summary to stdout + added: -t option to preserve timestamps + added: error summary for batch mode + + wvselfx.exe (self-extraction stub) - 4.1 + ---------------------------------------- + fixed: hybrid mode decoding bugs (very obscure situations) + + in_wv.dll (winamp plugin) - 2.1 + ------------------------------- + fixed: international characters in tags display properly (UTF-8 to Ansi) + added: maximum tag data field width changed from 64 chars to 128 chars + added: new infobox items including encoder version & modes, track #, md5 + + foo_wavpack.dll (foobar plugin) - 2.1 + ------------------------------------- + added: new database items including encoder version & modes and md5 + + WavPack_Apollo.dll (Apollo plugin) - 1.1 + ---------------------------------------- + fixed: international characters in tags display properly (UTF-8 to Ansi) + + Cool_wv4.flt (CoolEdit / Audition filter) - 2.2 + ----------------------------------------------- + fixed: hybrid mode + "extra" mode + very low bitrates making corrupt files + fixed: saving mono file causing crash (no corruption possible) + fixed: hybrid mode decoding bugs (very obscure situations) + fixed: partial saves (with "Cancel") have incorrect RIFF header if unpacked + + nxWavPack.dll (Nero plugin) - 1.0 + --------------------------------- + new + + WavPack Library Source Code - 4.1 + --------------------------------- + fixed: hybrid mode + "extra" mode + very low bitrates making corrupt files + fixed: mono or multichannel files causing crash (no corruption possible) + fixed: hybrid mode decoding bugs (very obscure situations) + added: mode bits for determining additional encode info (extra, sfx) + added: function to return total compressed file length (including wvc) + added: function to return encoder version (1, 2, 3, or 4) + added: ability to obtain MD5 sum before decoding file (requires seek to end) + added: mode bit for determining tag type (for proper character translation) + added: ability to encode WavPack files without knowing length in advance + added: option for small "information only" version of library diff --git a/Libraries/WavPack/Files/Makefile.am b/Libraries/WavPack/Files/Makefile.am new file mode 100644 index 000000000..820a49baa --- /dev/null +++ b/Libraries/WavPack/Files/Makefile.am @@ -0,0 +1,27 @@ +AUTOMAKE_OPTIONS = foreign +bin_PROGRAMS = wavpack wvunpack + +lib_LTLIBRARIES = libwavpack.la + +wpincludedir = $(prefix)/include/wavpack + +wpinclude_HEADERS = md5.h wavpack.h wputils.h unpack3.h + +libwavpack_la_SOURCES = bits.c float.c metadata.c unpack.c unpack3.c utils.c \ + wputils.c words.c md5.c extra1.c extra2.c pack.c \ + md5.h wavpack.h wputils.h unpack3.h +libwavpack_la_CFLAGS = -DPACK -DUNPACK -DUSE_FSTREAMS -DTAGS -DSEEKING -DVER3 +libwavpack_la_LDFLAGS = -lm @ICONV_LIBS@ + +wavpack_SOURCES = wavpack.c +wavpack_CFLAGS = -DPACK +wavpack_LDFLAGS = -lm -lcurses +wavpack_LDADD = libwavpack.la + +wvunpack_SOURCES = wvunpack.c +wvunpack_CFLAGS = -DUNPACK -DUSE_FSTREAMS +wvunpack_LDFLAGS = -lm -lcurses +wvunpack_LDADD = libwavpack.la + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = wavpack.pc diff --git a/Libraries/WavPack/Files/NEWS b/Libraries/WavPack/Files/NEWS new file mode 100644 index 000000000..e69de29bb diff --git a/Libraries/WavPack/Files/README b/Libraries/WavPack/Files/README new file mode 100644 index 000000000..df3507acd --- /dev/null +++ b/Libraries/WavPack/Files/README @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +This package contains all the source code required to build the WavPack +command-line programs and the WavPack library and it has been tested on many +platforms. For example code using the library you might want to check some +of the plugin sources in the Windows source release. + +To build everything, type: + +1. ./autogen.sh +2. ./configure +3. make +4. make install (optionally, to install into /usr/local/bin) + +Notes: + +1. This code is designed to be much easier to port to other platforms than + the previous version of WavPack. File I/O is done with streams and all file + functions (except "fopen") are handled in a wrapper in the "utils.c" + module. The code is even written to be endian-independent and a compile + option is provided to eliminate the DOS-specific directory searches. + + To maintain compatibility on various platforms, the following conventions + are used: + + a "short" must be 16-bits + an "int" must be at least 16-bits, but may be larger + a "char" must default to signed (Watcom users take note!) + + For version 4.2 references to "long" variables were eliminated to allow + compilation on 64-bit machines. + +2. For WavPack file decoding, a library interface in "wputils.c" provides all + the functionality required for both the winamp plugin and the "wvunpack" + command-line program (including the transparent decoding of "correction" + files). There is also an alternate entry point that uses reader callbacks + for all input, although in this case it is the caller's responsibility to + to open the "correction" file. It is no longer necessary to manipulate the + WavPack structures directly; everything is handled with function calls. In + fact, a new header file called "wputils.h" can be used that hides all the + WavPack internals from the application. + +3. For WavPack file creation, the library interface in "wputils.c" provides + all the functionality for both the Audition filter and the "wavpack" + command-line program. No file I/O is performed by the library when creating + WavPack files. Instead, the user supplies a "write_block" function that + accepts completed WavPack blocks. For version 4.2 limited functionality + has been added to append APEv2 tags to WavPack files during creation. + +4. The following #define's are used to control the optimum configuration of + the library for the desired application and must be the same for the + compilation of ALL files: + + UNPACK to unpack audio samples from WavPack files + PACK to create WavPack files from raw audio data + INFO_ONLY to obtain information from WavPack files, but not audio + SEEKING to allow seeking to a specific sample index (unpack only) + USE_FSTREAMS to open WavPack files by name using fstreams (via fopen) + TAGS to read specified fields from ID3v1 and APEv2 tags and + create APEv2 tags + VER3 to handle WavPack files from versions prior to 4.0 + WIN32 required for Win32 platform + + The following files are required for various configurations: + + UNPACK or + INFO_ONLY: wputils.c unpack.c words.c bits.c metadata.c float.c + + PACK: wputils.c pack.c extra1.c extra2.c + words.c bits.c metadata.c float.c + + PACK and + UNPACK: wputils.c unpack.c pack.c extra1.c extra2.c + words.c bits.c metadata.c float.c + +5. An alternate version of this library is available specifically designed + for "resource limited" CPUs or hardware decoding. This "tiny" decoder + library works with less than 32k of code and less than 4k of data. + +6. Questions or comments should be directed to david@wavpack.com diff --git a/Libraries/WavPack/Files/autogen.sh b/Libraries/WavPack/Files/autogen.sh new file mode 100755 index 000000000..39b82fcd9 --- /dev/null +++ b/Libraries/WavPack/Files/autogen.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +touch NEWS README AUTHORS ChangeLog +aclocal +libtoolize --copy +automake --add-missing +autoconf diff --git a/Libraries/WavPack/Files/bits.c b/Libraries/WavPack/Files/bits.c new file mode 100644 index 000000000..d062d7cf1 --- /dev/null +++ b/Libraries/WavPack/Files/bits.c @@ -0,0 +1,259 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// bits.c + +// This module provides utilities to support the BitStream structure which is +// used to read and write all WavPack audio data streams. It also contains a +// wrapper for the stream I/O functions and a set of functions dealing with +// endian-ness, both for enhancing portability. Finally, a debug wrapper for +// the malloc() system is provided. + +#include "wavpack.h" + +#include +#include +#include +#include + +#if defined(WIN32) +#include +#else +#include +#endif + +////////////////////////// Bitstream functions //////////////////////////////// + +#if defined(UNPACK) || defined(INFO_ONLY) + +// 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) +{ + bs->error = bs->sr = bs->bc = 0; + bs->ptr = (bs->buf = buffer_start) - 1; + bs->end = buffer_end; + 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) +{ + bs->ptr = bs->buf - 1; + bs->error = 1; +} + +// This function is called to close the bitstream. It returns the number of +// full bytes actually read as bits. + +uint32_t bs_close_read (Bitstream *bs) +{ + uint32_t bytes_read; + + if (bs->bc < 8) + bs->ptr++; + + if ((bs->buf - bs->ptr) & 1) + bs->ptr++; + + bytes_read = bs->ptr - bs->buf; + CLEAR (*bs); + return bytes_read; +} + +#endif + +#ifdef PACK + +// Open the specified BitStream using the specified buffer pointers. It is +// assumed that enough buffer space has been allocated for all data that will +// be written, otherwise an error will be generated. + +static void bs_write (Bitstream *bs); + +void bs_open_write (Bitstream *bs, uchar *buffer_start, uchar *buffer_end) +{ + bs->error = bs->sr = bs->bc = 0; + bs->ptr = bs->buf = buffer_start; + bs->end = buffer_end; + bs->wrap = bs_write; +} + +// This function is only called from the putbit() and putbits() macros when +// the buffer is full, which is now flagged as an error. + +static void bs_write (Bitstream *bs) +{ + bs->ptr = bs->buf; + bs->error = 1; +} + +// This function forces a flushing write of the specified BitStream, and +// returns the total number of bytes written into the buffer. + +uint32_t bs_close_write (Bitstream *bs) +{ + uint32_t bytes_written; + + if (bs->error) + return (uint32_t) -1; + + while (bs->bc || ((bs->ptr - bs->buf) & 1)) putbit_1 (bs); + bytes_written = bs->ptr - bs->buf; + CLEAR (*bs); + return bytes_written; +} + +#endif + +/////////////////////// 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 (*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 (*format)) + cp += *format - '0'; + + break; + } + + format++; + } +} + +////////////////////////// Debug Wrapper for Malloc /////////////////////////// + +#ifdef DEBUG_ALLOC + +void *vptrs [512]; + +static void *add_ptr (void *ptr) +{ + int i; + + for (i = 0; i < 512; ++i) + if (!vptrs [i]) { + vptrs [i] = ptr; + break; + } + + if (i == 512) + error_line ("too many mallocs!"); + + return ptr; +} + +static void *del_ptr (void *ptr) +{ + int i; + + for (i = 0; i < 512; ++i) + if (vptrs [i] == ptr) { + vptrs [i] = NULL; + break; + } + + if (i == 512) + error_line ("free invalid ptr!"); + + return ptr; +} + +void *malloc_db (uint32_t size) +{ + if (size) + return add_ptr (malloc (size)); + else + return NULL; +} + +void free_db (void *ptr) +{ + if (ptr) + free (del_ptr (ptr)); +} + +void *realloc_db (void *ptr, uint32_t size) +{ + if (ptr && size) + return add_ptr (realloc (del_ptr (ptr), size)); + else if (size) + return malloc_db (size); + else + free_db (ptr); + + return NULL; +} + +int32_t dump_alloc (void) +{ + int i, j; + + for (j = i = 0; i < 512; ++i) + if (vptrs [i]) + j++; + + return j; +} + +#endif diff --git a/Libraries/WavPack/Files/compile b/Libraries/WavPack/Files/compile new file mode 100755 index 000000000..ad57e2f68 --- /dev/null +++ b/Libraries/WavPack/Files/compile @@ -0,0 +1,142 @@ +#! /bin/sh +# Wrapper for compilers which do not understand `-c -o'. + +scriptversion=2005-02-03.08 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand `-c -o'. +Remove `-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file `INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; +esac + +ofile= +cfile= +eat= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as `compile cc -o foo foo.c'. + # So we strip `-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no `-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # `.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'` + +# Create the lock directory. +# Note: use `[/.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/Libraries/WavPack/Files/configure.ac b/Libraries/WavPack/Files/configure.ac new file mode 100644 index 000000000..441c9b2bd --- /dev/null +++ b/Libraries/WavPack/Files/configure.ac @@ -0,0 +1,62 @@ +# wavpack 4.2 configure.ac + +AC_INIT(wavpack, 4.2, bryant@wavpack.com) +AM_INIT_AUTOMAKE(wavpack, 4.2, bryant@wavpack.com) +AC_CONFIG_SRCDIR([pack.c]) + +# Check for os version +VERSION_OS=$(uname -s) +AC_DEFINE_UNQUOTED(VERSION_OS, "$VERSION_OS", [os version]) + +# Check for processor characteristics +AC_C_BIGENDIAN(AC_DEFINE([HIGHFIRST], [1], [big-endian machine])) + +# Checks for programs. +AC_PROG_CC +AC_PROG_LIBTOOL + +# Checks for libraries. +AC_CHECK_LIB(m, log10, [], AC_MSG_ERROR(math library not found)) + +# Check for iconv +AC_ARG_WITH(iconv, [ --with-iconv[=DIR] Add ICONV support (on)]) +if test "$with_iconv" = "no" ; then + AC_MSG_ERROR([[Sorry, you can't deactivate iconv.]]) +else + if test "$with_iconv" != "yes" -a "$with_iconv" != "" ; then + CPPFLAGS="${CPPFLAGS} -I$with_iconv/include" + ICONV_LIBS="-L$with_iconv/lib" + fi + + AC_CHECK_HEADER(iconv.h, + AC_MSG_CHECKING(for iconv) + AC_TRY_LINK([#include +#include ],[ +iconv_t cd = iconv_open ("",""); +iconv (cd, NULL, NULL, NULL, NULL);],[ + AC_MSG_RESULT(yes) + WITH_ICONV=1 + ICONV=""],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING(for iconv in -liconv) + + _ldflags="${LDFLAGS}" + _libs="${LIBS}" + LDFLAGS="${LDFLAGS} ${ICONV_LIBS}" + LIBS="${LIBS} -liconv" + + AC_TRY_LINK([#include +#include ],[ +iconv_t cd = iconv_open ("",""); +iconv (cd, NULL, NULL, NULL, NULL);],[ + AC_MSG_RESULT(yes) + WITH_ICONV=1 + ICONV_LIBS="${ICONV_LIBS} -liconv" + ICONV="${ICONV_LIBS}"],[ + AC_MSG_ERROR([[Can't find iconv libraries.]])])]), + AC_MSG_ERROR([[Can't find iconv headers.]])) +fi +AC_SUBST(ICONV) +AC_SUBST(ICONV_LIBS) + +AC_OUTPUT(Makefile wavpack.pc) diff --git a/Libraries/WavPack/Files/depcomp b/Libraries/WavPack/Files/depcomp new file mode 100755 index 000000000..ffcd540c3 --- /dev/null +++ b/Libraries/WavPack/Files/depcomp @@ -0,0 +1,529 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2005-02-09.22 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputing dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + stat=$? + + if test -f "$tmpdepfile"; then : + else + stripped=`echo "$stripped" | sed 's,^.*/,,'` + tmpdepfile="$stripped.u" + fi + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + outname="$stripped.o" + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using \ : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a + # static library. This mecanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two + # compilations output dependencies in in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 + tmpdepfile2=$dir$base.o.d # libtool 1.5 + tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 + tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.o.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + tmpdepfile4=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for `:' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + "$@" $dashmflag | + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/Libraries/WavPack/Files/extra1.c b/Libraries/WavPack/Files/extra1.c new file mode 100644 index 000000000..9c7064069 --- /dev/null +++ b/Libraries/WavPack/Files/extra1.c @@ -0,0 +1,563 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// extra1.c + +// This module handles the "extra" mode for mono files. + +#include "wavpack.h" + +#include +#include +#include +#include + +// #define EXTRA_DUMP + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +#endif + +//////////////////////////////// local tables /////////////////////////////// + +extern const char default_terms [], high_terms [], fast_terms []; + +// #define MINMAX_WEIGHTS + +#ifdef MINMAX_WEIGHTS +static int32_t min_weight, max_weight; +static int min_term, max_term; +#endif + +static void decorr_mono_pass (int32_t *in_samples, int32_t *out_samples, uint32_t num_samples, struct decorr_pass *dpp, int dir) +{ + int m = 0; + + dpp->sum_A = 0; + +#ifdef MINMAX_WEIGHTS + dpp->min = dpp->max = 0; +#endif + + if (dir < 0) { + out_samples += (num_samples - 1); + in_samples += (num_samples - 1); + dir = -1; + } + else + dir = 1; + + if (dpp->term > MAX_TERM) { + while (num_samples--) { + int32_t left, sam_A; + + if (dpp->term & 1) + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + else + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_A [0] = left = in_samples [0]; + + left -= apply_weight (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, left); + dpp->sum_A += dpp->weight_A; +#ifdef MINMAX_WEIGHTS + if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A; + if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A; +#endif + out_samples [0] = left; + in_samples += dir; + out_samples += dir; + } + } + else if (dpp->term > 0) { + while (num_samples--) { + int k = (m + dpp->term) & (MAX_TERM - 1); + int32_t left, sam_A; + + sam_A = dpp->samples_A [m]; + dpp->samples_A [k] = left = in_samples [0]; + m = (m + 1) & (MAX_TERM - 1); + + left -= apply_weight (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, left); + dpp->sum_A += dpp->weight_A; +#ifdef MINMAX_WEIGHTS + if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A; + if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A; +#endif + out_samples [0] = left; + in_samples += dir; + out_samples += dir; + } + } + +#ifdef MINMAX_WEIGHTS + if (dpp->term != 0) { + if (dpp->max > max_weight) { max_weight = dpp->max; max_term = dpp->term; } + if (dpp->min < min_weight) { min_weight = dpp->min; min_term = dpp->term; } + } +#endif + + if (m && dpp->term > 0 && dpp->term <= MAX_TERM) { + int32_t temp_A [MAX_TERM]; + int k; + + memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A)); + + for (k = 0; k < MAX_TERM; k++) { + dpp->samples_A [k] = temp_A [m]; + m = (m + 1) & (MAX_TERM - 1); + } + } +} + +static void reverse_mono_decorr (struct decorr_pass *dpp) +{ + if (dpp->term > MAX_TERM) { + int32_t sam_A; + + if (dpp->term & 1) + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + else + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_A [0] = sam_A; + + if (dpp->term & 1) + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + else + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + + dpp->samples_A [1] = sam_A; + } + else if (dpp->term > 1) { + int i = 0, j = dpp->term - 1, cnt = dpp->term / 2; + + while (cnt--) { + i &= (MAX_TERM - 1); + j &= (MAX_TERM - 1); + dpp->samples_A [i] ^= dpp->samples_A [j]; + dpp->samples_A [j] ^= dpp->samples_A [i]; + dpp->samples_A [i++] ^= dpp->samples_A [j--]; + } + + CLEAR (dpp->samples_A); + } +} + +static void decorr_mono_buffer (int32_t *samples, int32_t *outsamples, uint32_t num_samples, struct decorr_pass *dpp) +{ + int delta = dpp->delta, pre_delta, term = dpp->term; + struct decorr_pass dp; + + if (delta == 7) + pre_delta = 7; + else if (delta < 2) + pre_delta = 3; + else + pre_delta = delta + 1; + + CLEAR (dp); + dp.term = term; + dp.delta = pre_delta; + decorr_mono_pass (samples, outsamples, num_samples > 2048 ? 2048 : num_samples, &dp, -1); + dp.delta = delta; + reverse_mono_decorr (&dp); + memcpy (dpp->samples_A, dp.samples_A, sizeof (dp.samples_A)); + dpp->weight_A = dp.weight_A; + + if (delta == 0) { + dp.delta = 1; + decorr_mono_pass (samples, outsamples, num_samples, &dp, 1); + dp.delta = 0; + memcpy (dp.samples_A, dpp->samples_A, sizeof (dp.samples_A)); + dpp->weight_A = dp.weight_A = dp.sum_A / num_samples; + } + +// if (memcmp (dpp, &dp, sizeof (dp))) +// error_line ("decorr_passes don't match, delta = %d", delta); + + decorr_mono_pass (samples, outsamples, num_samples, &dp, 1); +} + +static void recurse_mono (WavpackContext *wpc, int32_t *sampleptrs[], struct decorr_pass dps[], + int depth, int nterms, int delta, uint32_t input_bits, uint32_t *best_bits) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + int term, branches = ((wpc->config.extra_flags & EXTRA_BRANCHES) >> 6) - depth; + int32_t *samples, *outsamples; + uint32_t term_bits [22], bits; + + if (branches < 1 || depth + 1 == nterms) + branches = 1; + + CLEAR (term_bits); + samples = sampleptrs [depth]; + outsamples = sampleptrs [depth + 1]; + + for (term = 1; term <= 18; ++term) { + if (term == 17 && branches == 1 && depth + 1 < nterms) + continue; + + if (term >= 9 && term <= 16) + if (term > MAX_TERM || !(wpc->config.flags & CONFIG_HIGH_FLAG) || (wpc->config.extra_flags & EXTRA_SKIP_8TO16)) + continue; + + if ((wpc->config.flags & CONFIG_FAST_FLAG) && (term >= 5 && term <= 16)) + continue; + + dps [depth].term = term; + dps [depth].delta = delta; + decorr_mono_buffer (samples, outsamples, wps->wphdr.block_samples, &dps [depth]); + bits = log2buffer (outsamples, wps->wphdr.block_samples); + + if (bits < *best_bits) { + *best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * (depth + 1)); + memcpy (sampleptrs [nterms + 1], sampleptrs [depth + 1], wps->wphdr.block_samples * 4); + } + + term_bits [term + 3] = bits; + } + + while (depth + 1 < nterms && branches--) { + uint32_t local_best_bits = input_bits; + int best_term = 0, i; + + for (i = 0; i < 22; ++i) + if (term_bits [i] && term_bits [i] < local_best_bits) { + local_best_bits = term_bits [i]; + term_bits [i] = 0; + best_term = i - 3; + } + + if (!best_term) + break; + + dps [depth].term = best_term; + dps [depth].delta = delta; + decorr_mono_buffer (samples, outsamples, wps->wphdr.block_samples, &dps [depth]); + +// if (log2buffer (outsamples, wps->wphdr.block_samples * 2) != local_best_bits) +// error_line ("data doesn't match!"); + + recurse_mono (wpc, sampleptrs, dps, depth + 1, nterms, delta, local_best_bits, best_bits); + } +} + +static void delta_mono (WavpackContext *wpc, int32_t *sampleptrs[], struct decorr_pass dps[], + int nterms, uint32_t *best_bits) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + int lower = FALSE, delta, d; + uint32_t bits; + + if (wps->decorr_passes [0].term) + delta = wps->decorr_passes [0].delta; + else + return; + + for (d = delta - 1; d >= 0; --d) { + int i; + + if (!d && (wps->wphdr.flags & HYBRID_FLAG)) + break; + + for (i = 0; i < nterms && wps->decorr_passes [i].term; ++i) { + dps [i].term = wps->decorr_passes [i].term; + dps [i].delta = d; + decorr_mono_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]); + } + + bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples); + + if (bits < *best_bits) { + lower = TRUE; + *best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4); + } + else + break; + } + + for (d = delta + 1; !lower && d <= 7; ++d) { + int i; + + for (i = 0; i < nterms && wps->decorr_passes [i].term; ++i) { + dps [i].term = wps->decorr_passes [i].term; + dps [i].delta = d; + decorr_mono_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]); + } + + bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples); + + if (bits < *best_bits) { + *best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4); + } + else + break; + } +} + +static void sort_mono (WavpackContext *wpc, int32_t *sampleptrs[], struct decorr_pass dps[], + int nterms, uint32_t *best_bits) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + int reversed = TRUE; + uint32_t bits; + + while (reversed) { + int ri, i; + + memcpy (dps, wps->decorr_passes, sizeof (wps->decorr_passes)); + reversed = FALSE; + + for (ri = 0; ri < nterms && wps->decorr_passes [ri].term; ++ri) { + + if (ri + 1 >= nterms || !wps->decorr_passes [ri+1].term) + break; + + if (wps->decorr_passes [ri].term == wps->decorr_passes [ri+1].term) { + decorr_mono_buffer (sampleptrs [ri], sampleptrs [ri+1], wps->wphdr.block_samples, &dps [ri]); + continue; + } + + dps [ri] = wps->decorr_passes [ri+1]; + dps [ri+1] = wps->decorr_passes [ri]; + + for (i = ri; i < nterms && wps->decorr_passes [i].term; ++i) + decorr_mono_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]); + + bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples); + + if (bits < *best_bits) { + reversed = TRUE; + *best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4); + } + else { + dps [ri] = wps->decorr_passes [ri]; + dps [ri+1] = wps->decorr_passes [ri+1]; + decorr_mono_buffer (sampleptrs [ri], sampleptrs [ri+1], wps->wphdr.block_samples, &dps [ri]); + } + } + } +} + +#define EXTRA_ADVANCED (EXTRA_BRANCHES | EXTRA_SORT_FIRST | EXTRA_SORT_LAST | EXTRA_TRY_DELTAS) + +void analyze_mono (WavpackContext *wpc, int32_t *samples) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; +#ifdef EXTRA_DUMP + uint32_t bits, best_bits, default_bits, cnt; +#else + uint32_t bits, best_bits, cnt; +#endif + const char *decorr_terms = default_terms, *tp; + int32_t *sampleptrs [MAX_NTERMS+2], *lptr; + struct decorr_pass dps [MAX_NTERMS]; + int nterms, i; + + CLEAR (wps->decorr_passes); + cnt = wps->wphdr.block_samples; + lptr = samples; + + while (cnt--) + if (*lptr++) + break; + + if (cnt == (uint32_t) -1) { + scan_word (wps, samples, wps->wphdr.block_samples, -1); + wps->num_terms = 0; + return; + } + + if (wpc->config.flags & CONFIG_HIGH_FLAG) + decorr_terms = high_terms; + else if (wpc->config.flags & CONFIG_FAST_FLAG) + decorr_terms = fast_terms; + + for (nterms = 0, tp = decorr_terms; *tp; tp++) + if (*tp > 0) + ++nterms; + + if (wpc->config.extra_flags & EXTRA_TERMS) + if ((nterms += (wpc->config.extra_flags & EXTRA_TERMS) >> 10) > MAX_NTERMS) + nterms = MAX_NTERMS; + + for (i = 0; i < nterms + 2; ++i) + sampleptrs [i] = malloc (wps->wphdr.block_samples * 4); + + memcpy (sampleptrs [nterms + 1], samples, wps->wphdr.block_samples * 4); + best_bits = log2buffer (sampleptrs [nterms + 1], wps->wphdr.block_samples); + memcpy (sampleptrs [0], samples, wps->wphdr.block_samples * 4); + CLEAR (dps); + + for (tp = decorr_terms, i = 0; *tp; tp++) + if (*tp > 0) { + dps [i].term = *tp; + dps [i].delta = 2; + decorr_mono_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]); + ++i; + } + +#ifdef EXTRA_DUMP + default_bits = bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples); +#else + bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples); +#endif + + if (bits < best_bits) { + best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4); + } + + if ((wps->wphdr.flags & HYBRID_FLAG) && (wpc->config.extra_flags & EXTRA_ADVANCED)) { + int shaping_weight, new = wps->wphdr.flags & NEW_SHAPING; + int32_t *rptr = sampleptrs [nterms + 1], error = 0, temp; + + scan_word (wps, rptr, wps->wphdr.block_samples, -1); + cnt = wps->wphdr.block_samples; + lptr = sampleptrs [0]; + + if (wps->wphdr.flags & HYBRID_SHAPE) { + while (cnt--) { + shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + temp = -apply_weight (shaping_weight, error); + + if (new && shaping_weight < 0 && temp) { + if (temp == error) + temp = (temp < 0) ? temp + 1 : temp - 1; + + lptr [0] += (error = nosend_word (wps, rptr [0], 0) - rptr [0] + temp); + } + else + lptr [0] += (error = nosend_word (wps, rptr [0], 0) - rptr [0]) + temp; + + lptr++; + rptr++; + } + + wps->dc.shaping_acc [0] -= wps->dc.shaping_delta [0] * wps->wphdr.block_samples; + } + else + while (cnt--) { + lptr [0] += nosend_word (wps, rptr [0], 0) - rptr [0]; + lptr++; + rptr++; + } + + memcpy (dps, wps->decorr_passes, sizeof (dps)); + + for (i = 0; i < nterms && dps [i].term; ++i) + decorr_mono_buffer (sampleptrs [i], sampleptrs [i + 1], wps->wphdr.block_samples, dps + i); + +#ifdef EXTRA_DUMP + best_bits = default_bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples); +#else + best_bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples); +#endif + + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 4); + } + + if (wpc->config.extra_flags & EXTRA_BRANCHES) + recurse_mono (wpc, sampleptrs, dps, 0, nterms, (int) floor (wps->delta_decay + 0.5), + log2buffer (sampleptrs [0], wps->wphdr.block_samples), &best_bits); + + if (wpc->config.extra_flags & EXTRA_SORT_FIRST) + sort_mono (wpc, sampleptrs, dps, nterms, &best_bits); + + if (wpc->config.extra_flags & EXTRA_TRY_DELTAS) { + delta_mono (wpc, sampleptrs, dps, nterms, &best_bits); + + if ((wpc->config.extra_flags & EXTRA_ADJUST_DELTAS) && wps->decorr_passes [0].term) + wps->delta_decay = (wps->delta_decay * 2.0 + wps->decorr_passes [0].delta) / 3.0; + else + wps->delta_decay = 2.0; + } + + if (wpc->config.extra_flags & EXTRA_SORT_LAST) + sort_mono (wpc, sampleptrs, dps, nterms, &best_bits); + +#if 0 + memcpy (dps, wps->decorr_passes, sizeof (dps)); + + for (i = 0; i < nterms && dps [i].term; ++i) + decorr_mono_pass (sampleptrs [i], sampleptrs [i + 1], wps->wphdr.block_samples, dps + i, 1); + + if (log2buffer (sampleptrs [i], wps->wphdr.block_samples) != best_bits) + error_line ("(1) samples do not match!"); + + if (log2buffer (sampleptrs [nterms + 1], wps->wphdr.block_samples) != best_bits) + error_line ("(2) samples do not match!"); +#endif + + scan_word (wps, sampleptrs [nterms + 1], wps->wphdr.block_samples, -1); + +#ifdef EXTRA_DUMP + if (wpc->config.extra_flags & EXTRA_DUMP_TERMS) { + char string [256], substring [20]; + int i; + + sprintf (string, "M: delta = %.4f%%, terms =", + ((double) best_bits - default_bits) / 256.0 / wps->wphdr.block_samples / 32.0 * 100.0); + + for (i = 0; i < nterms; ++i) { + if (wps->decorr_passes [i].term) { + if (i && wps->decorr_passes [i-1].delta == wps->decorr_passes [i].delta) + sprintf (substring, " %d", wps->decorr_passes [i].term); + else + sprintf (substring, " %d->%d", wps->decorr_passes [i].term, + wps->decorr_passes [i].delta); + } + else + sprintf (substring, " *"); + + strcat (string, substring); + } + + error_line (string); + } +#endif + + for (i = 0; i < nterms; ++i) + if (!wps->decorr_passes [i].term) + break; + + wps->num_terms = i; + + for (i = 0; i < nterms + 2; ++i) + free (sampleptrs [i]); + +#ifdef MINMAX_WEIGHTS + error_line ("weight range = %ld (%d) to %ld (%d)", min_weight, min_term, max_weight, max_term); +#endif +} diff --git a/Libraries/WavPack/Files/extra2.c b/Libraries/WavPack/Files/extra2.c new file mode 100644 index 000000000..777abe3be --- /dev/null +++ b/Libraries/WavPack/Files/extra2.c @@ -0,0 +1,792 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// extra2.c + +// This module handles the "extra" mode for stereo files. + +#include "wavpack.h" + +#include +#include +#include +#include + +// #define EXTRA_DUMP + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +#endif + +extern const char default_terms [], high_terms [], fast_terms []; + +// #define MINMAX_WEIGHTS + +#ifdef MINMAX_WEIGHTS +static int32_t min_weight, max_weight; +static int min_term, max_term; +#endif + +static void decorr_stereo_pass (int32_t *in_samples, int32_t *out_samples, int32_t num_samples, struct decorr_pass *dpp, int dir) +{ + int m = 0; + + dpp->sum_A = dpp->sum_B = 0; + +#ifdef MINMAX_WEIGHTS + dpp->min = dpp->max = 0; +#endif + + if (dir < 0) { + out_samples += (num_samples - 1) * 2; + in_samples += (num_samples - 1) * 2; + dir = -2; + } + else + dir = 2; + + if (dpp->term == 17) { + while (num_samples--) { + int32_t left, right; + int32_t sam_A, sam_B; + + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_A [0] = left = in_samples [0]; + left -= apply_weight (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, left); + dpp->sum_A += dpp->weight_A; + out_samples [0] = left; + + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_B [0] = right = in_samples [1]; + right -= apply_weight (dpp->weight_B, sam_B); + update_weight (dpp->weight_B, dpp->delta, sam_B, right); + dpp->sum_B += dpp->weight_B; + out_samples [1] = right; + +#ifdef MINMAX_WEIGHTS + if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A; + if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B; + if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A; + if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B; +#endif + in_samples += dir; + out_samples += dir; + } + } + else if (dpp->term == 18) { + while (num_samples--) { + int32_t left, right; + int32_t sam_A, sam_B; + + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_A [0] = left = in_samples [0]; + left -= apply_weight (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, left); + dpp->sum_A += dpp->weight_A; + out_samples [0] = left; + + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_B [0] = right = in_samples [1]; + right -= apply_weight (dpp->weight_B, sam_B); + update_weight (dpp->weight_B, dpp->delta, sam_B, right); + dpp->sum_B += dpp->weight_B; + out_samples [1] = right; + +#ifdef MINMAX_WEIGHTS + if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A; + if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B; + if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A; + if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B; +#endif + in_samples += dir; + out_samples += dir; + } + } + else if (dpp->term > 0) { + while (num_samples--) { + int k = (m + dpp->term) & (MAX_TERM - 1); + int32_t left, right; + int32_t sam_A, sam_B; + + sam_A = dpp->samples_A [m]; + dpp->samples_A [k] = left = in_samples [0]; + left -= apply_weight (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, left); + dpp->sum_A += dpp->weight_A; + out_samples [0] = left; + + sam_B = dpp->samples_B [m]; + dpp->samples_B [k] = right = in_samples [1]; + right -= apply_weight (dpp->weight_B, sam_B); + update_weight (dpp->weight_B, dpp->delta, sam_B, right); + dpp->sum_B += dpp->weight_B; + out_samples [1] = right; + +#ifdef MINMAX_WEIGHTS + if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A; + if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B; + if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A; + if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B; +#endif + in_samples += dir; + out_samples += dir; + m = (m + 1) & (MAX_TERM - 1); + } + } + else if (dpp->term == -1) { + while (num_samples--) { + int32_t left = in_samples [0]; + int32_t right = in_samples [1]; + int32_t sam_A = dpp->samples_A [0], sam_B = left; + + dpp->samples_A [0] = right; + right -= apply_weight (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, right); + left -= apply_weight (dpp->weight_A, sam_A); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, left); + dpp->sum_A += dpp->weight_A; + dpp->sum_B += dpp->weight_B; +#ifdef MINMAX_WEIGHTS + if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A; + if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B; + if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A; + if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B; +#endif + out_samples [0] = left; + out_samples [1] = right; + in_samples += dir; + out_samples += dir; + } + } + else if (dpp->term == -2) { + while (num_samples--) { + int32_t left = in_samples [0]; + int32_t right = in_samples [1]; + int32_t sam_B = dpp->samples_B [0], sam_A = right; + + dpp->samples_B [0] = left; + left -= apply_weight (dpp->weight_A, sam_A); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, left); + right -= apply_weight (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, right); + dpp->sum_A += dpp->weight_A; + dpp->sum_B += dpp->weight_B; +#ifdef MINMAX_WEIGHTS + if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A; + if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B; + if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A; + if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B; +#endif + out_samples [0] = left; + out_samples [1] = right; + in_samples += dir; + out_samples += dir; + } + } + else if (dpp->term == -3) { + while (num_samples--) { + int32_t left = in_samples [0]; + int32_t right = in_samples [1]; + int32_t sam_A = dpp->samples_A [0], sam_B = dpp->samples_B [0]; + + dpp->samples_A [0] = right; + dpp->samples_B [0] = left; + left -= apply_weight (dpp->weight_A, sam_A); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, left); + right -= apply_weight (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, right); + dpp->sum_A += dpp->weight_A; + dpp->sum_B += dpp->weight_B; +#ifdef MINMAX_WEIGHTS + if (dpp->weight_A > dpp->max) dpp->max = dpp->weight_A; + if (dpp->weight_B > dpp->max) dpp->max = dpp->weight_B; + if (dpp->weight_A < dpp->min) dpp->min = dpp->weight_A; + if (dpp->weight_B < dpp->min) dpp->min = dpp->weight_B; +#endif + out_samples [0] = left; + out_samples [1] = right; + in_samples += dir; + out_samples += dir; + } + } + +#ifdef MINMAX_WEIGHTS + if (dpp->term != 0) { + if (dpp->max > max_weight) { max_weight = dpp->max; max_term = dpp->term; } + if (dpp->min < min_weight) { min_weight = dpp->min; min_term = dpp->term; } + } +#endif + + if (m && dpp->term > 0 && dpp->term <= MAX_TERM) { + int32_t temp_A [MAX_TERM], temp_B [MAX_TERM]; + int k; + + memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A)); + memcpy (temp_B, dpp->samples_B, sizeof (dpp->samples_B)); + + for (k = 0; k < MAX_TERM; k++) { + dpp->samples_A [k] = temp_A [m]; + dpp->samples_B [k] = temp_B [m]; + m = (m + 1) & (MAX_TERM - 1); + } + } +} + +static void reverse_decorr (struct decorr_pass *dpp) +{ + if (dpp->term > MAX_TERM) { + int32_t sam_A, sam_B; + + if (dpp->term & 1) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_A [0] = sam_A; + dpp->samples_B [0] = sam_B; + + if (dpp->term & 1) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + + dpp->samples_A [1] = sam_A; + dpp->samples_B [1] = sam_B; + } + else if (dpp->term > 1) { + int i = 0, j = dpp->term - 1, cnt = dpp->term / 2; + + while (cnt--) { + i &= (MAX_TERM - 1); + j &= (MAX_TERM - 1); + dpp->samples_A [i] ^= dpp->samples_A [j]; + dpp->samples_A [j] ^= dpp->samples_A [i]; + dpp->samples_A [i] ^= dpp->samples_A [j]; + dpp->samples_B [i] ^= dpp->samples_B [j]; + dpp->samples_B [j] ^= dpp->samples_B [i]; + dpp->samples_B [i++] ^= dpp->samples_B [j--]; + } + +// CLEAR (dpp->samples_A); +// CLEAR (dpp->samples_B); + } + else if (dpp->term == -1) { + } + else if (dpp->term == -2) { + } + else if (dpp->term == -3) { + } +} + +static void decorr_stereo_buffer (int32_t *samples, int32_t *outsamples, int32_t num_samples, struct decorr_pass *dpp) +{ + int delta = dpp->delta, pre_delta; + int term = dpp->term; + struct decorr_pass dp; + + if (delta == 7) + pre_delta = 7; + else if (delta < 2) + pre_delta = 3; + else + pre_delta = delta + 1; + + CLEAR (dp); + dp.term = term; + dp.delta = pre_delta; + decorr_stereo_pass (samples, outsamples, num_samples > 2048 ? 2048 : num_samples, &dp, -1); + dp.delta = delta; + reverse_decorr (&dp); + memcpy (dpp->samples_A, dp.samples_A, sizeof (dp.samples_A)); + memcpy (dpp->samples_B, dp.samples_B, sizeof (dp.samples_B)); + dpp->weight_A = dp.weight_A; + dpp->weight_B = dp.weight_B; + + if (delta == 0) { + dp.delta = 1; + decorr_stereo_pass (samples, outsamples, num_samples, &dp, 1); + dp.delta = 0; + memcpy (dp.samples_A, dpp->samples_A, sizeof (dp.samples_A)); + memcpy (dp.samples_B, dpp->samples_B, sizeof (dp.samples_B)); + dpp->weight_A = dp.weight_A = dp.sum_A / num_samples; + dpp->weight_B = dp.weight_B = dp.sum_B / num_samples; + } + +// if (memcmp (dpp, &dp, sizeof (dp))) +// error_line ("decorr_passes don't match, delta = %d", delta); + + decorr_stereo_pass (samples, outsamples, num_samples, &dp, 1); +} + +static void recurse_stereo (WavpackContext *wpc, int32_t *sampleptrs[], struct decorr_pass dps[], + int depth, int nterms, int delta, uint32_t input_bits, uint32_t *best_bits) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + int term, branches = ((wpc->config.extra_flags & EXTRA_BRANCHES) >> 6) - depth; + int32_t *samples, *outsamples; + uint32_t term_bits [22], bits; + + if (branches < 1 || depth + 1 == nterms) + branches = 1; + + CLEAR (term_bits); + samples = sampleptrs [depth]; + outsamples = sampleptrs [depth + 1]; + + for (term = -3; term <= 18; ++term) { + if (!term) + continue; + + if (term == 17 && branches == 1 && depth + 1 < nterms) + continue; + + if (term == -1 || term == -2) + if (!(wps->wphdr.flags & CROSS_DECORR)) + continue; + + if (term >= 9 && term <= 16) + if (term > MAX_TERM || !(wpc->config.flags & CONFIG_HIGH_FLAG) || (wpc->config.extra_flags & EXTRA_SKIP_8TO16)) + continue; + + if ((wpc->config.flags & CONFIG_FAST_FLAG) && (term >= 5 && term <= 16)) + continue; + + dps [depth].term = term; + dps [depth].delta = delta; + decorr_stereo_buffer (samples, outsamples, wps->wphdr.block_samples, &dps [depth]); + bits = log2buffer (outsamples, wps->wphdr.block_samples * 2); + + if (bits < *best_bits) { + *best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * (depth + 1)); + memcpy (sampleptrs [nterms + 1], sampleptrs [depth + 1], wps->wphdr.block_samples * 8); + } + + term_bits [term + 3] = bits; + } + + while (depth + 1 < nterms && branches--) { + uint32_t local_best_bits = input_bits; + int best_term = 0, i; + + for (i = 0; i < 22; ++i) + if (term_bits [i] && term_bits [i] < local_best_bits) { + local_best_bits = term_bits [i]; + term_bits [i] = 0; + best_term = i - 3; + } + + if (!best_term) + break; + + dps [depth].term = best_term; + dps [depth].delta = delta; + decorr_stereo_buffer (samples, outsamples, wps->wphdr.block_samples, &dps [depth]); + +// if (log2buffer (outsamples, wps->wphdr.block_samples * 2) != local_best_bits) +// error_line ("data doesn't match!"); + + recurse_stereo (wpc, sampleptrs, dps, depth + 1, nterms, delta, local_best_bits, best_bits); + } +} + +static void delta_stereo (WavpackContext *wpc, int32_t *sampleptrs[], struct decorr_pass dps[], + int nterms, uint32_t *best_bits) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + int lower = FALSE; + int delta, d; + uint32_t bits; + + if (wps->decorr_passes [0].term) + delta = wps->decorr_passes [0].delta; + else + return; + + for (d = delta - 1; d >= 0; --d) { + int i; + + if (!d && (wps->wphdr.flags & HYBRID_FLAG)) + break; + + for (i = 0; i < nterms && wps->decorr_passes [i].term; ++i) { + dps [i].term = wps->decorr_passes [i].term; + dps [i].delta = d; + decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]); + } + + bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2); + + if (bits < *best_bits) { + lower = TRUE; + *best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8); + } + else + break; + } + + for (d = delta + 1; !lower && d <= 7; ++d) { + int i; + + for (i = 0; i < nterms && wps->decorr_passes [i].term; ++i) { + dps [i].term = wps->decorr_passes [i].term; + dps [i].delta = d; + decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]); + } + + bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2); + + if (bits < *best_bits) { + *best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8); + } + else + break; + } +} + +static void sort_stereo (WavpackContext *wpc, int32_t *sampleptrs[], struct decorr_pass dps[], + int nterms, uint32_t *best_bits) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + int reversed = TRUE; + uint32_t bits; + + while (reversed) { + int ri, i; + + memcpy (dps, wps->decorr_passes, sizeof (wps->decorr_passes)); + reversed = FALSE; + + for (ri = 0; ri < nterms && wps->decorr_passes [ri].term; ++ri) { + + if (ri + 1 >= nterms || !wps->decorr_passes [ri+1].term) + break; + + if (wps->decorr_passes [ri].term == wps->decorr_passes [ri+1].term) { + decorr_stereo_buffer (sampleptrs [ri], sampleptrs [ri+1], wps->wphdr.block_samples, &dps [ri]); + continue; + } + + dps [ri] = wps->decorr_passes [ri+1]; + dps [ri+1] = wps->decorr_passes [ri]; + + for (i = ri; i < nterms && wps->decorr_passes [i].term; ++i) + decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]); + + bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2); + + if (bits < *best_bits) { + reversed = TRUE; + *best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8); + } + else { + dps [ri] = wps->decorr_passes [ri]; + dps [ri+1] = wps->decorr_passes [ri+1]; + decorr_stereo_buffer (sampleptrs [ri], sampleptrs [ri+1], wps->wphdr.block_samples, &dps [ri]); + } + } + } +} + +#define EXTRA_ADVANCED (EXTRA_BRANCHES | EXTRA_SORT_FIRST | EXTRA_SORT_LAST | EXTRA_TRY_DELTAS) + +void analyze_stereo (WavpackContext *wpc, int32_t *samples) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; +#ifdef EXTRA_DUMP + uint32_t bits, best_bits, default_bits, cnt; +#else + uint32_t bits, best_bits, cnt; +#endif + const char *decorr_terms = default_terms, *tp; + int32_t *sampleptrs [MAX_NTERMS+2], *lptr; + struct decorr_pass dps [MAX_NTERMS]; + int nterms, i; + + + CLEAR (wps->decorr_passes); + cnt = wps->wphdr.block_samples * 2; + lptr = samples; + + while (cnt--) + if (*lptr++) + break; + + if (cnt == (uint32_t) -1) { + scan_word (wps, samples, wps->wphdr.block_samples, -1); + wps->num_terms = 0; + return; + } + + if (wpc->config.flags & CONFIG_HIGH_FLAG) + decorr_terms = high_terms; + else if (wpc->config.flags & CONFIG_FAST_FLAG) + decorr_terms = fast_terms; + + nterms = strlen (decorr_terms); + + if (wpc->config.extra_flags & EXTRA_TERMS) + if ((nterms += (wpc->config.extra_flags & EXTRA_TERMS) >> 10) > MAX_NTERMS) + nterms = MAX_NTERMS; + + for (i = 0; i < nterms + 2; ++i) + sampleptrs [i] = malloc (wps->wphdr.block_samples * 8); + + memcpy (sampleptrs [nterms + 1], samples, wps->wphdr.block_samples * 8); + best_bits = log2buffer (sampleptrs [nterms + 1], wps->wphdr.block_samples * 2); + + if ((wpc->config.extra_flags & EXTRA_STEREO_MODES) || !(wps->wphdr.flags & JOINT_STEREO)) { + memcpy (sampleptrs [0], samples, wps->wphdr.block_samples * 8); + + CLEAR (dps); + + for (tp = decorr_terms, i = 0; *tp;) { + if (*tp > 0 || (wps->wphdr.flags & CROSS_DECORR)) + dps [i].term = *tp++; + else { + dps [i].term = -3; + tp++; + } + + dps [i].delta = 2; + decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]); + ++i; + } + +#ifdef EXTRA_DUMP + default_bits = bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2); +#else + bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2); +#endif + + wps->wphdr.flags &= ~JOINT_STEREO; + + if (bits < best_bits) { + best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8); + } + } + + if ((wpc->config.extra_flags & EXTRA_STEREO_MODES) || (wps->wphdr.flags & JOINT_STEREO)) { + memcpy (sampleptrs [0], samples, wps->wphdr.block_samples * 8); + cnt = wps->wphdr.block_samples; + lptr = sampleptrs [0]; + + while (cnt--) { + lptr [1] += ((lptr [0] -= lptr [1]) >> 1); + lptr += 2; + } + + CLEAR (dps); + + for (tp = decorr_terms, i = 0; *tp;) { + if (*tp > 0 || (wps->wphdr.flags & CROSS_DECORR)) + dps [i].term = *tp++; + else { + dps [i].term = -3; + tp++; + } + + dps [i].delta = 2; + decorr_stereo_buffer (sampleptrs [i], sampleptrs [i+1], wps->wphdr.block_samples, &dps [i]); + ++i; + } + +#ifdef EXTRA_DUMP + default_bits = bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2); +#else + bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2); +#endif + + wps->wphdr.flags |= JOINT_STEREO; + + if (bits < best_bits) { + best_bits = bits; + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8); + } + else { + memcpy (sampleptrs [0], samples, wps->wphdr.block_samples * 8); + wps->wphdr.flags &= ~JOINT_STEREO; + } + } + + if ((wps->wphdr.flags & HYBRID_FLAG) && (wpc->config.extra_flags & EXTRA_ADVANCED)) { + int shaping_weight, new = wps->wphdr.flags & NEW_SHAPING; + int32_t *rptr = sampleptrs [nterms + 1], error [2], temp; + + scan_word (wps, rptr, wps->wphdr.block_samples, -1); + cnt = wps->wphdr.block_samples; + lptr = sampleptrs [0]; + CLEAR (error); + + if (wps->wphdr.flags & HYBRID_SHAPE) { + while (cnt--) { + shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + temp = -apply_weight (shaping_weight, error [0]); + + if (new && shaping_weight < 0 && temp) { + if (temp == error [0]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + lptr [0] += (error [0] = nosend_word (wps, rptr [0], 0) - rptr [0] + temp); + } + else + lptr [0] += (error [0] = nosend_word (wps, rptr [0], 0) - rptr [0]) + temp; + + shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16; + temp = -apply_weight (shaping_weight, error [1]); + + if (new && shaping_weight < 0 && temp) { + if (temp == error [1]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + lptr [1] += (error [1] = nosend_word (wps, rptr [1], 1) - rptr [1] + temp); + } + else + lptr [1] += (error [1] = nosend_word (wps, rptr [1], 1) - rptr [1]) + temp; + + lptr += 2; + rptr += 2; + } + + wps->dc.shaping_acc [0] -= wps->dc.shaping_delta [0] * wps->wphdr.block_samples; + wps->dc.shaping_acc [1] -= wps->dc.shaping_delta [1] * wps->wphdr.block_samples; + } + else + while (cnt--) { + lptr [0] += nosend_word (wps, rptr [0], 0) - rptr [0]; + lptr [1] += nosend_word (wps, rptr [1], 1) - rptr [1]; + lptr += 2; + rptr += 2; + } + + memcpy (dps, wps->decorr_passes, sizeof (dps)); + + for (i = 0; i < nterms && dps [i].term; ++i) + decorr_stereo_buffer (sampleptrs [i], sampleptrs [i + 1], wps->wphdr.block_samples, dps + i); + +#ifdef EXTRA_DUMP + best_bits = default_bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2); +#else + best_bits = log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2); +#endif + + CLEAR (wps->decorr_passes); + memcpy (wps->decorr_passes, dps, sizeof (dps [0]) * i); + memcpy (sampleptrs [nterms + 1], sampleptrs [i], wps->wphdr.block_samples * 8); + } + + if (wpc->config.extra_flags & EXTRA_BRANCHES) + recurse_stereo (wpc, sampleptrs, dps, 0, nterms, (int) floor (wps->delta_decay + 0.5), + log2buffer (sampleptrs [0], wps->wphdr.block_samples * 2), &best_bits); + + if (wpc->config.extra_flags & EXTRA_SORT_FIRST) + sort_stereo (wpc, sampleptrs, dps, nterms, &best_bits); + + if (wpc->config.extra_flags & EXTRA_TRY_DELTAS) { + delta_stereo (wpc, sampleptrs, dps, nterms, &best_bits); + + if ((wpc->config.extra_flags & EXTRA_ADJUST_DELTAS) && wps->decorr_passes [0].term) + wps->delta_decay = (wps->delta_decay * 2.0 + wps->decorr_passes [0].delta) / 3.0; + else + wps->delta_decay = 2.0; + } + + if (wpc->config.extra_flags & EXTRA_SORT_LAST) + sort_stereo (wpc, sampleptrs, dps, nterms, &best_bits); + +#if 0 + memcpy (dps, wps->decorr_passes, sizeof (dps)); + + for (i = 0; i < nterms && dps [i].term; ++i) + decorr_stereo_pass (sampleptrs [i], sampleptrs [i + 1], wps->wphdr.block_samples, dps + i, 1); + + if (log2buffer (sampleptrs [i], wps->wphdr.block_samples * 2) != best_bits) + error_line ("(1) samples do not match!"); + + if (log2buffer (sampleptrs [nterms + 1], wps->wphdr.block_samples * 2) != best_bits) + error_line ("(2) samples do not match!"); +#endif + + scan_word (wps, sampleptrs [nterms + 1], wps->wphdr.block_samples, -1); + +#ifdef EXTRA_DUMP + if (1) { + char string [256], substring [20]; + int i; + + sprintf (string, "%s: delta = %.4f%%, terms =", + (wps->wphdr.flags & JOINT_STEREO) ? "JS" : "TS", + ((double) best_bits - default_bits) / 256.0 / wps->wphdr.block_samples / 32.0 * 100.0); + + for (i = 0; i < nterms; ++i) { + if (wps->decorr_passes [i].term) { + if (i && wps->decorr_passes [i-1].delta == wps->decorr_passes [i].delta) + sprintf (substring, " %d", wps->decorr_passes [i].term); + else + sprintf (substring, " %d->%d", wps->decorr_passes [i].term, + wps->decorr_passes [i].delta); + } + else + sprintf (substring, " *"); + + strcat (string, substring); + } + + error_line (string); + } +#endif + + for (i = 0; i < nterms; ++i) + if (!wps->decorr_passes [i].term) + break; + + wps->num_terms = i; + + for (i = 0; i < nterms + 2; ++i) + free (sampleptrs [i]); + +#ifdef MINMAX_WEIGHTS + error_line ("weight range = %ld (%d) to %ld (%d)", min_weight, min_term, max_weight, max_term); +#endif +} diff --git a/Libraries/WavPack/Files/float.c b/Libraries/WavPack/Files/float.c new file mode 100644 index 000000000..19fb3a460 --- /dev/null +++ b/Libraries/WavPack/Files/float.c @@ -0,0 +1,371 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// float.c + +#include "wavpack.h" + +#include + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +#endif + +#ifdef PACK + +void write_float_info (WavpackStream *wps, WavpackMetadata *wpmd) +{ + char *byteptr; + + byteptr = wpmd->data = malloc (4); + wpmd->id = ID_FLOAT_INFO; + *byteptr++ = wps->float_flags; + *byteptr++ = wps->float_shift; + *byteptr++ = wps->float_max_exp; + *byteptr++ = wps->float_norm_exp; + wpmd->byte_length = byteptr - (char *) wpmd->data; +} + +int scan_float_data (WavpackStream *wps, f32 *values, int32_t num_values) +{ + int32_t shifted_ones = 0, shifted_zeros = 0, shifted_both = 0; + int32_t false_zeros = 0, neg_zeros = 0; + uint32_t ordata = 0, crc = 0xffffffff; + int max_exp = 0, shift_count; + int32_t count, value; + f32 *dp; + + wps->float_shift = wps->float_flags = 0; + + for (dp = values, count = num_values; count--; dp++) { + crc = crc * 27 + dp->mantissa * 9 + dp->exponent * 3 + dp->sign; + + if (dp->exponent > max_exp && dp->exponent < 255) + max_exp = dp->exponent; + } + + wps->crc_x = crc; + + for (dp = values, count = num_values; count--; dp++) { + if (dp->exponent == 255) { + wps->float_flags |= FLOAT_EXCEPTIONS; + value = 0x1000000; + shift_count = 0; + } + else if (dp->exponent) { + shift_count = max_exp - dp->exponent; + value = 0x800000 + dp->mantissa; + } + else { + shift_count = max_exp ? max_exp - 1 : 0; + value = dp->mantissa; + +// if (dp->mantissa) +// denormals++; + } + + if (shift_count < 25) + value >>= shift_count; + else + value = 0; + + if (!value) { + if (dp->exponent || dp->mantissa) + ++false_zeros; + else if (dp->sign) + ++neg_zeros; + } + else if (shift_count) { + int32_t mask = (1 << shift_count) - 1; + + if (!(dp->mantissa & mask)) + shifted_zeros++; + else if ((dp->mantissa & mask) == mask) + shifted_ones++; + else + shifted_both++; + } + + ordata |= value; + * (int32_t *) dp = (dp->sign) ? -value : value; + } + + wps->float_max_exp = max_exp; + + if (shifted_both) + wps->float_flags |= FLOAT_SHIFT_SENT; + else if (shifted_ones && !shifted_zeros) + wps->float_flags |= FLOAT_SHIFT_ONES; + else if (shifted_ones && shifted_zeros) + wps->float_flags |= FLOAT_SHIFT_SAME; + else if (ordata && !(ordata & 1)) { + while (!(ordata & 1)) { + wps->float_shift++; + ordata >>= 1; + } + + for (dp = values, count = num_values; count--; dp++) + * (int32_t *) dp >>= wps->float_shift; + } + + wps->wphdr.flags &= ~MAG_MASK; + + while (ordata) { + wps->wphdr.flags += 1 << MAG_LSB; + ordata >>= 1; + } + + if (false_zeros || neg_zeros) + wps->float_flags |= FLOAT_ZEROS_SENT; + + if (neg_zeros) + wps->float_flags |= FLOAT_NEG_ZEROS; + +// error_line ("samples = %d, max exp = %d, pre-shift = %d, denormals = %d", +// num_values, max_exp, wps->float_shift, denormals); +// if (wps->float_flags & FLOAT_EXCEPTIONS) +// error_line ("exceptions!"); +// error_line ("shifted ones/zeros/both = %d/%d/%d, true/neg/false zeros = %d/%d/%d", +// shifted_ones, shifted_zeros, shifted_both, true_zeros, neg_zeros, false_zeros); + + return wps->float_flags & (FLOAT_EXCEPTIONS | FLOAT_ZEROS_SENT | FLOAT_SHIFT_SENT | FLOAT_SHIFT_SAME); +} + +void send_float_data (WavpackStream *wps, f32 *values, int32_t num_values) +{ + int max_exp = wps->float_max_exp; + int32_t count, value, shift_count; + f32 *dp; + + for (dp = values, count = num_values; count--; dp++) { + if (dp->exponent == 255) { + if (dp->mantissa) { + putbit_1 (&wps->wvxbits); + putbits (dp->mantissa, 23, &wps->wvxbits); + } + else { + putbit_0 (&wps->wvxbits); + } + + value = 0x1000000; + shift_count = 0; + } + else if (dp->exponent) { + shift_count = max_exp - dp->exponent; + value = 0x800000 + dp->mantissa; + } + else { + shift_count = max_exp ? max_exp - 1 : 0; + value = dp->mantissa; + } + + if (shift_count < 25) + value >>= shift_count; + else + value = 0; + + if (!value) { + if (wps->float_flags & FLOAT_ZEROS_SENT) { + if (dp->exponent || dp->mantissa) { + putbit_1 (&wps->wvxbits); + putbits (dp->mantissa, 23, &wps->wvxbits); + + if (max_exp >= 25) { + putbits (dp->exponent, 8, &wps->wvxbits); + } + + putbit (dp->sign, &wps->wvxbits); + } + else { + putbit_0 (&wps->wvxbits); + + if (wps->float_flags & FLOAT_NEG_ZEROS) + putbit (dp->sign, &wps->wvxbits); + } + } + } + else if (shift_count) { + if (wps->float_flags & FLOAT_SHIFT_SENT) { + int32_t data = dp->mantissa & ((1 << shift_count) - 1); + putbits (data, shift_count, &wps->wvxbits); + } + else if (wps->float_flags & FLOAT_SHIFT_SAME) { + putbit (dp->mantissa & 1, &wps->wvxbits); + } + } + } +} + +#endif + +#if defined(UNPACK) || defined(INFO_ONLY) + +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; +} + +#endif + +#ifdef UNPACK + +static void float_values_nowvx (WavpackStream *wps, int32_t *values, int32_t num_values); + +void float_values (WavpackStream *wps, int32_t *values, int32_t num_values) +{ + uint32_t crc = wps->crc_x; + + if (!bs_is_open (&wps->wvxbits)) { + float_values_nowvx (wps, values, num_values); + return; + } + + while (num_values--) { + int shift_count = 0, exp = wps->float_max_exp; + f32 outval = { 0, 0, 0 }; + uint32_t temp; + + if (*values == 0) { + if (wps->float_flags & FLOAT_ZEROS_SENT) { + if (getbit (&wps->wvxbits)) { + getbits (&temp, 23, &wps->wvxbits); + outval.mantissa = temp; + + if (exp >= 25) { + getbits (&temp, 8, &wps->wvxbits); + outval.exponent = temp; + } + + outval.sign = getbit (&wps->wvxbits); + } + else if (wps->float_flags & FLOAT_NEG_ZEROS) + outval.sign = getbit (&wps->wvxbits); + } + } + else { + *values <<= wps->float_shift; + + if (*values < 0) { + *values = -*values; + outval.sign = 1; + } + + if (*values == 0x1000000) { + if (getbit (&wps->wvxbits)) { + getbits (&temp, 23, &wps->wvxbits); + outval.mantissa = temp; + } + + outval.exponent = 255; + } + else { + if (exp) + while (!(*values & 0x800000) && --exp) { + shift_count++; + *values <<= 1; + } + + if (shift_count) { + if ((wps->float_flags & FLOAT_SHIFT_ONES) || + ((wps->float_flags & FLOAT_SHIFT_SAME) && getbit (&wps->wvxbits))) + *values |= ((1 << shift_count) - 1); + else if (wps->float_flags & FLOAT_SHIFT_SENT) { + getbits (&temp, shift_count, &wps->wvxbits); + *values |= temp & ((1 << shift_count) - 1); + } + } + + outval.mantissa = *values; + outval.exponent = exp; + } + } + + crc = crc * 27 + outval.mantissa * 9 + outval.exponent * 3 + outval.sign; + * (f32 *) values++ = outval; + } + + wps->crc_x = crc; +} + +static void float_values_nowvx (WavpackStream *wps, int32_t *values, int32_t num_values) +{ + while (num_values--) { + int shift_count = 0, exp = wps->float_max_exp; + f32 outval = { 0, 0, 0 }; + + if (*values) { + *values <<= wps->float_shift; + + if (*values < 0) { + *values = -*values; + outval.sign = 1; + } + + if (*values >= 0x1000000) { + while (*values & 0xf000000) { + *values >>= 1; + ++exp; + } + } + else if (exp) { + while (!(*values & 0x800000) && --exp) { + shift_count++; + *values <<= 1; + } + + if (shift_count && (wps->float_flags & FLOAT_SHIFT_ONES)) + *values |= ((1 << shift_count) - 1); + } + + outval.mantissa = *values; + outval.exponent = exp; + } + + * (f32 *) values++ = outval; + } +} + +void float_normalize (int32_t *values, int32_t num_values, int delta_exp) +{ + f32 *fvalues = (f32 *) values, fzero = { 0, 0, 0 }; + int exp; + + if (!delta_exp) + return; + + while (num_values--) { + if ((exp = fvalues->exponent) == 0 || exp + delta_exp <= 0) + *fvalues = fzero; + else if (exp == 255 || (exp += delta_exp) >= 255) { + fvalues->exponent = 255; + fvalues->mantissa = 0; + } + else + fvalues->exponent = exp; + + fvalues++; + } +} + +#endif diff --git a/Libraries/WavPack/Files/format.txt b/Libraries/WavPack/Files/format.txt new file mode 100644 index 000000000..8185a0b7f --- /dev/null +++ b/Libraries/WavPack/Files/format.txt @@ -0,0 +1,96 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + + WavPack 4.0 File / Block Format + ------------------------------- + +A WavPack 4.0 file consists of a series of WavPack audio blocks. It may also +contain tags and other information, but these must be outside the blocks +(either before, in-between, or after) and are ignored for the purpose of +unpacking audio data. The WavPack blocks are easy to identify by their +unique header data, and by looking in the header it is very easy to +determine the total size of the block, both in physical bytes and compressed +samples. There are no seek tables. + +The blocks (or frames, if you prefer) are completely independent in that they +can be decoded to mono or stereo audio all by themselves. A single function +is provided to convert a whole block into its corresponding audio data. +Similarly, a function is provided to convert a block of audio samples into +a finished WavPack block. These all work in memory; disk I/O is handled +outside. It is also possible to decode or encode blocks in smaller increments +if it is important to distribute CPU load more evenly over time. The blocks may +also be decoded without reading the whole block into memory, although this +would only be important for hardware decoding. + +The blocks may contain any number of samples, either stereo or mono. Obviously, +putting more samples in each block is more efficient, but they are reasonably +efficient down to even a thousand samples. I have set the max size to 1 MB for +the whole block, but this is arbitrary. The blocks may be lossless or lossy +(currently the lossy modes are basically CBR, but I am planning a quality +based VBR version also). + +For multichannel audio, the data is divided into some number of stereo and mono +streams and multiplexed into separate blocks. Because blocks are independent +there can be a mix of sampling rates, but all the streams must be sliced at +the same point in time which is a multiple of all the sampling rates. The +metadata contains source information (like front, center, rear, etc.). + +Correction files (.wvc) have an identical structure to the main file (.wv) and +there is a one-to-one correspondence between main file blocks that contain +audio and their correction file match (blocks that do not contain audio do +not exist in the correction file). The only difference in the headers of +main blocks and correction blocks is the CRC value, although it is easy to +tell the blocks apart by looking at the metadata ids. + +Here is the 32-byte header at the front of every block: + +typedef struct { + char ckID [4]; // "wvpk" + long ckSize; // size of entire frame (minus 8, of course) + short version; // 0x403 for now + uchar track_no; // track number (0 if not used, like now) + uchar index_no; // track sub-index (0 if not used, like now) + ulong total_samples; // for entire file (-1 if unknown) + ulong block_index; // index of first sample in block (to file begin) + ulong block_samples; // # samples in this block + ulong flags; // various flags for id and decoding + ulong crc; // crc for actual decoded data +} WavpackHeader; + +The "flags" field contains information for decoding the block along with some +general information including sample size and format, hybrid/lossless, +mono/stereo and sampling rate. This structure is stored "little-endian". + +Following the 32-byte header to the end of the block are a series of "metadata" +sub-blocks. These may from 2 bytes long to the size of the entire block and are +extremely easy to parse (even without knowing what they mean). Currently these +mostly contain extra information needed to decode the audio, but may also +contain user information. The only non-audio information I currently have +implemented is a copy of the original wave RIFF header (or trailer if present), +and the MD5 checksums, but there is plenty of flexibility here. For example, +these metadata blocks could store cuesheets, artist/title information, +replaygain values, even pictures or lyrics. The final metadata sub-blocks are +the actual audio bitstreams, which have ids for standard audio (wvbits), +correction data (wvcbits), and a special extension for large integer and +floating-point data (wvxbits). + +The format of the metadata is: + + uchar id; // mask meaning + // ---- ------- + // 0x1f metadata function + // 0x20 decoder need not understand metadata + // 0x40 actual data byte length is 1 less + // 0x80 large block (> 255 words) + + uchar word_size; // small block: data size in words (padded) + or... + uchar word_size [3]; // large block: data size in words (padded, + little-endian) + + ushort data [word_size]; // data, padded to an even # of bytes diff --git a/Libraries/WavPack/Files/install-sh b/Libraries/WavPack/Files/install-sh new file mode 100755 index 000000000..1a8353401 --- /dev/null +++ b/Libraries/WavPack/Files/install-sh @@ -0,0 +1,323 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2005-02-02.21 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit 1; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit 0 +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/Libraries/WavPack/Files/license.txt b/Libraries/WavPack/Files/license.txt new file mode 100644 index 000000000..c21b40fbb --- /dev/null +++ b/Libraries/WavPack/Files/license.txt @@ -0,0 +1,25 @@ + Copyright (c) 1998 - 2005 Conifer Software + All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Conifer Software nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Libraries/WavPack/Files/md5.c b/Libraries/WavPack/Files/md5.c new file mode 100644 index 000000000..fe27c4539 --- /dev/null +++ b/Libraries/WavPack/Files/md5.c @@ -0,0 +1,263 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* Brutally hacked by John Walker back from ANSI C to K&R (no + prototypes) to maintain the tradition that Netfone will compile + with Sun's original "cc". */ + +#include /* for memcpy() */ +#include "md5.h" + +#ifdef sgi +#define HIGHFIRST +#endif + +#ifdef sun +#define HIGHFIRST +#endif + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(buf, longs) + unsigned char *buf; unsigned longs; +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(ctx) + struct MD5Context *ctx; +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(ctx, buf, len) + struct MD5Context *ctx; unsigned char *buf; unsigned len; +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(digest, ctx) + unsigned char digest[16]; struct MD5Context *ctx; +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *) ctx->in)[14] = ctx->bits[0]; + ((uint32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(buf, in) + uint32 buf[4]; uint32 in[16]; +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/Libraries/WavPack/Files/md5.h b/Libraries/WavPack/Files/md5.h new file mode 100644 index 000000000..5aa29d201 --- /dev/null +++ b/Libraries/WavPack/Files/md5.h @@ -0,0 +1,26 @@ +#ifndef MD5_H +#define MD5_H + +#if defined (__alpha__) || defined (__x86_64__) +typedef unsigned int uint32; +#else +typedef unsigned long uint32; +#endif + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; + +extern void MD5Init(); +extern void MD5Update(); +extern void MD5Final(); +extern void MD5Transform(); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ +typedef struct MD5Context MD5_CTX; + +#endif /* !MD5_H */ diff --git a/Libraries/WavPack/Files/metadata.c b/Libraries/WavPack/Files/metadata.c new file mode 100644 index 000000000..922c16694 --- /dev/null +++ b/Libraries/WavPack/Files/metadata.c @@ -0,0 +1,310 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 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" + +#include +#include + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +#endif + +#if defined(UNPACK) || defined(INFO_ONLY) + +int read_metadata_buff (WavpackMetadata *wpmd, uchar *blockbuff, uchar **buffptr) +{ + WavpackHeader *wphdr = (WavpackHeader *) blockbuff; + uchar *buffend = blockbuff + wphdr->ckSize + 8; + + if (buffend - *buffptr < 2) + return FALSE; + + wpmd->id = *(*buffptr)++; + wpmd->byte_length = *(*buffptr)++ << 1; + + if (wpmd->id & ID_LARGE) { + wpmd->id &= ~ID_LARGE; + + if (buffend - *buffptr < 2) + return FALSE; + + wpmd->byte_length += *(*buffptr)++ << 9; + wpmd->byte_length += *(*buffptr)++ << 17; + } + + if (wpmd->id & ID_ODD_SIZE) { + wpmd->id &= ~ID_ODD_SIZE; + wpmd->byte_length--; + } + + if (wpmd->byte_length) { + if (buffend - *buffptr < wpmd->byte_length + (wpmd->byte_length & 1)) { + wpmd->data = NULL; + return FALSE; + } + + wpmd->data = *buffptr; + (*buffptr) += wpmd->byte_length + (wpmd->byte_length & 1); + } + else + wpmd->data = NULL; + + return TRUE; +} + +int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + WavpackStream *wps = wpc->streams [wpc->current_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_SHAPING_WEIGHTS: + return read_shaping_info (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 (wps, wpmd); + + case ID_WVC_BITSTREAM: + return init_wvc_bitstream (wps, wpmd); + + case ID_WVX_BITSTREAM: + return init_wvx_bitstream (wps, wpmd); + + case ID_RIFF_HEADER: case ID_RIFF_TRAILER: + return read_wrapper_data (wpc, wpmd); + + case ID_MD5_CHECKSUM: + if (wpmd->byte_length == 16) { + memcpy (wpc->config.md5_checksum, wpmd->data, 16); + wpc->config.flags |= CONFIG_MD5_CHECKSUM; + wpc->config.md5_read = 1; + } + + return TRUE; + + default: + return (wpmd->id & ID_OPTIONAL_DATA) ? TRUE : FALSE; + } +} + +#endif + +#ifdef PACK + +int copy_metadata (WavpackMetadata *wpmd, uchar *buffer_start, uchar *buffer_end) +{ + uint32_t mdsize = wpmd->byte_length + (wpmd->byte_length & 1); + WavpackHeader *wphdr = (WavpackHeader *) buffer_start; + + if (wpmd->byte_length & 1) + ((char *) wpmd->data) [wpmd->byte_length] = 0; + + mdsize += (wpmd->byte_length > 510) ? 4 : 2; + buffer_start += wphdr->ckSize + 8; + + if (buffer_start + mdsize >= buffer_end) + return FALSE; + + buffer_start [0] = wpmd->id | (wpmd->byte_length & 1 ? ID_ODD_SIZE : 0); + buffer_start [1] = (wpmd->byte_length + 1) >> 1; + + if (wpmd->byte_length > 510) { + buffer_start [0] |= ID_LARGE; + buffer_start [2] = (wpmd->byte_length + 1) >> 9; + buffer_start [3] = (wpmd->byte_length + 1) >> 17; + } + + if (wpmd->data && wpmd->byte_length) { + if (wpmd->byte_length > 510) { + buffer_start [0] |= ID_LARGE; + buffer_start [2] = (wpmd->byte_length + 1) >> 9; + buffer_start [3] = (wpmd->byte_length + 1) >> 17; + memcpy (buffer_start + 4, wpmd->data, mdsize - 4); + } + else + memcpy (buffer_start + 2, wpmd->data, mdsize - 2); + } + + wphdr->ckSize += mdsize; + return TRUE; +} + +int add_to_metadata (WavpackContext *wpc, void *data, uint32_t bcount, uchar id) +{ + WavpackMetadata *mdp; + uchar *src = data; + + while (bcount) { + if (wpc->metacount) { + uint32_t bc = bcount; + + mdp = wpc->metadata + wpc->metacount - 1; + + if (mdp->id == id) { + if (wpc->metabytes + bcount > 1000000) + bc = 1000000 - wpc->metabytes; + + mdp->data = realloc (mdp->data, mdp->byte_length + bc); + memcpy ((char *) mdp->data + mdp->byte_length, src, bc); + mdp->byte_length += bc; + wpc->metabytes += bc; + bcount -= bc; + src += bc; + + if (wpc->metabytes >= 1000000 && !write_metadata_block (wpc)) + return FALSE; + } + } + + if (bcount) { + wpc->metadata = realloc (wpc->metadata, (wpc->metacount + 1) * sizeof (WavpackMetadata)); + mdp = wpc->metadata + wpc->metacount++; + mdp->byte_length = 0; + mdp->data = NULL; + mdp->id = id; + } + } + + return TRUE; +} + +static char *write_metadata (WavpackMetadata *wpmd, char *outdata) +{ + uchar id = wpmd->id, wordlen [3]; + + wordlen [0] = (wpmd->byte_length + 1) >> 1; + wordlen [1] = (wpmd->byte_length + 1) >> 9; + wordlen [2] = (wpmd->byte_length + 1) >> 17; + + if (wpmd->byte_length & 1) { +// ((char *) wpmd->data) [wpmd->byte_length] = 0; + id |= ID_ODD_SIZE; + } + + if (wordlen [1] || wordlen [2]) + id |= ID_LARGE; + + *outdata++ = id; + *outdata++ = wordlen [0]; + + if (id & ID_LARGE) { + *outdata++ = wordlen [1]; + *outdata++ = wordlen [2]; + } + + if (wpmd->data && wpmd->byte_length) { + memcpy (outdata, wpmd->data, wpmd->byte_length); + outdata += wpmd->byte_length; + + if (wpmd->byte_length & 1) + *outdata++ = 0; + } + + return outdata; +} + +int write_metadata_block (WavpackContext *wpc) +{ + char *block_buff, *block_ptr; + WavpackHeader *wphdr; + + if (wpc->metacount) { + int metacount = wpc->metacount, block_size = sizeof (WavpackHeader); + WavpackMetadata *wpmdp = wpc->metadata; + + while (metacount--) { + block_size += wpmdp->byte_length + (wpmdp->byte_length & 1); + block_size += (wpmdp->byte_length > 510) ? 4 : 2; + wpmdp++; + } + + wphdr = (WavpackHeader *) (block_buff = malloc (block_size)); + + CLEAR (*wphdr); + memcpy (wphdr->ckID, "wvpk", 4); + wphdr->total_samples = wpc->total_samples; + wphdr->version = 0x403; + wphdr->ckSize = block_size - 8; + wphdr->block_samples = 0; + + block_ptr = (char *)(wphdr + 1); + + wpmdp = wpc->metadata; + + while (wpc->metacount) { + block_ptr = write_metadata (wpmdp, block_ptr); + wpc->metabytes -= wpmdp->byte_length; + free_metadata (wpmdp++); + wpc->metacount--; + } + + free (wpc->metadata); + wpc->metadata = NULL; + native_to_little_endian ((WavpackHeader *) block_buff, WavpackHeaderFormat); + + if (!wpc->blockout (wpc->wv_out, block_buff, block_size)) { + free (block_buff); + strcpy (wpc->error_message, "can't write WavPack data, disk probably full!"); + return FALSE; + } + + free (block_buff); + } + + return TRUE; +} + +#endif + +void free_metadata (WavpackMetadata *wpmd) +{ + if (wpmd->data) { + free (wpmd->data); + wpmd->data = NULL; + } +} diff --git a/Libraries/WavPack/Files/missing b/Libraries/WavPack/Files/missing new file mode 100755 index 000000000..09edd8844 --- /dev/null +++ b/Libraries/WavPack/Files/missing @@ -0,0 +1,357 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2005-02-08.22 + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). +case "$1" in + lex|yacc) + # Not GNU programs, they don't have --version. + ;; + + tar) + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + exit 1 + fi + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + touch $file + ;; + + tar) + shift + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/Libraries/WavPack/Files/pack.c b/Libraries/WavPack/Files/pack.c new file mode 100644 index 000000000..46871041d --- /dev/null +++ b/Libraries/WavPack/Files/pack.c @@ -0,0 +1,1413 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// pack.c + +// This module actually handles the compression of the audio data, except for +// the entropy coding which is handled by the words? modules. For efficiency, +// the conversion is isolated to tight loops that handle an entire buffer. + +#include "wavpack.h" + +#include +#include +#include +#include + +// This flag provides faster encoding speed at the expense of more code. The +// improvement applies to 16-bit stereo lossless only. + +#define FAST_ENCODE + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +#endif + +//////////////////////////////// local tables /////////////////////////////// + +// These two tables specify the characteristics of the decorrelation filters. +// Each term represents one layer of the sequential filter, where positive +// values indicate the relative sample involved from the same channel (1=prev), +// 17 & 18 are special functions using the previous 2 samples, and negative +// values indicate cross channel decorrelation (in stereo only). + +const char default_terms [] = { 18,18,2,3,-2,0 }; +const char high_terms [] = { 18,18,2,3,-2,18,2,4,7,5,3,6,8,-1,18,2,0 }; +const char fast_terms [] = { 17,17,0 }; + +///////////////////////////// executable code //////////////////////////////// + +// This function initializes everything required to pack WavPack bitstreams +// and must be called BEFORE any other function in this module. + +void pack_init (WavpackContext *wpc) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uint32_t flags = wps->wphdr.flags; + struct decorr_pass *dpp; + const char *term_string; + int ti; + + wps->sample_index = 0; + wps->delta_decay = 2.0; + CLEAR (wps->decorr_passes); + CLEAR (wps->dc); + + if (wpc->config.flags & CONFIG_AUTO_SHAPING) + wps->dc.shaping_acc [0] = wps->dc.shaping_acc [1] = + (wpc->config.sample_rate < 64000 || (wps->wphdr.flags & CROSS_DECORR)) ? -512L << 16 : 1024L << 16; + else { + int32_t weight = (int32_t) floor (wpc->config.shaping_weight * 1024.0 + 0.5); + + if (weight <= -1000) + weight = -1000; + + wps->dc.shaping_acc [0] = wps->dc.shaping_acc [1] = weight << 16; + } + + if (wpc->config.flags & CONFIG_HIGH_FLAG) + term_string = high_terms; + else if (wpc->config.flags & CONFIG_FAST_FLAG) + term_string = fast_terms; + else + term_string = default_terms; + + for (dpp = wps->decorr_passes, ti = 0; ti < strlen (term_string); ti++) + if (term_string [ti] >= 0 || (flags & CROSS_DECORR)) { + dpp->term = term_string [ti]; + dpp++->delta = 2; + } + else if (!(flags & MONO_FLAG)) { + dpp->term = -3; + dpp++->delta = 2; + } + + wps->num_terms = dpp - wps->decorr_passes; + init_words (wps); +} + +// Allocate room for and copy the decorrelation terms from the decorr_passes +// array into the specified metadata structure. Both the actual term id and +// the delta are packed into single characters. + +void write_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd) +{ + int tcount = wps->num_terms; + struct decorr_pass *dpp; + char *byteptr; + + byteptr = wpmd->data = malloc (tcount + 1); + wpmd->id = ID_DECORR_TERMS; + + for (dpp = wps->decorr_passes; tcount--; ++dpp) + *byteptr++ = ((dpp->term + 5) & 0x1f) | ((dpp->delta << 5) & 0xe0); + + wpmd->byte_length = byteptr - (char *) wpmd->data; +} + +// Allocate room for and copy the decorrelation term weights from the +// decorr_passes array into the specified metadata structure. The weights +// range +/-1024, but are rounded and truncated to fit in signed chars for +// metadata storage. Weights are separate for the two channels + +void write_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd) +{ + int tcount = wps->num_terms; + struct decorr_pass *dpp; + char *byteptr; + + byteptr = wpmd->data = malloc ((tcount * 2) + 1); + wpmd->id = ID_DECORR_WEIGHTS; + + for (dpp = wps->decorr_passes; tcount--; ++dpp) { + dpp->weight_A = restore_weight (*byteptr++ = store_weight (dpp->weight_A)); + + if (!(wps->wphdr.flags & MONO_FLAG)) + dpp->weight_B = restore_weight (*byteptr++ = store_weight (dpp->weight_B)); + } + + wpmd->byte_length = byteptr - (char *) wpmd->data; +} + +// Allocate room for and copy the decorrelation samples from the decorr_passes +// array into the specified metadata structure. The samples are signed 32-bit +// values, but are converted to signed log2 values for storage in metadata. +// Values are stored for both channels and are specified from the first term +// with unspecified samples set to zero. The number of samples stored varies +// with the actual term value, so those must obviously be specified before +// these in the metadata list. Any number of terms can have their samples +// specified from no terms to all the terms, however I have found that +// sending more than the first term's samples is a waste. The "wcount" +// variable can be set to the number of terms to have their samples stored. + +void write_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd) +{ + int tcount = wps->num_terms, wcount = 1, temp; + struct decorr_pass *dpp; + uchar *byteptr; + + byteptr = wpmd->data = malloc (256); + wpmd->id = ID_DECORR_SAMPLES; + + for (dpp = wps->decorr_passes; tcount--; ++dpp) + if (wcount) { + if (dpp->term > MAX_TERM) { + dpp->samples_A [0] = exp2s (temp = log2s (dpp->samples_A [0])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + dpp->samples_A [1] = exp2s (temp = log2s (dpp->samples_A [1])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + dpp->samples_B [0] = exp2s (temp = log2s (dpp->samples_B [0])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + dpp->samples_B [1] = exp2s (temp = log2s (dpp->samples_B [1])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + } + } + else if (dpp->term < 0) { + dpp->samples_A [0] = exp2s (temp = log2s (dpp->samples_A [0])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + dpp->samples_B [0] = exp2s (temp = log2s (dpp->samples_B [0])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + } + else { + int m = 0, cnt = dpp->term; + + while (cnt--) { + dpp->samples_A [m] = exp2s (temp = log2s (dpp->samples_A [m])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + dpp->samples_B [m] = exp2s (temp = log2s (dpp->samples_B [m])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + } + + m++; + } + } + + wcount--; + } + else { + CLEAR (dpp->samples_A); + CLEAR (dpp->samples_B); + } + + wpmd->byte_length = byteptr - (uchar *) wpmd->data; +} + +// Allocate room for and copy the noise shaping info into the specified +// metadata structure. These would normally be written to the +// "correction" file and are used for lossless reconstruction of +// hybrid data. The "delta" parameter is not yet used in encoding as it +// will be part of the "quality" mode. + +void write_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd) +{ + char *byteptr; + int temp; + +#if 0 + if (wps->wphdr.block_samples) { + wps->dc.shaping_delta [0] = (-wps->dc.shaping_acc [0] - wps->dc.shaping_acc [0]) / (int32_t) wps->wphdr.block_samples; + wps->dc.shaping_delta [1] = (-wps->dc.shaping_acc [1] - wps->dc.shaping_acc [1]) / (int32_t) wps->wphdr.block_samples; + } +#endif + + byteptr = wpmd->data = malloc (12); + wpmd->id = ID_SHAPING_WEIGHTS; + + wps->dc.error [0] = exp2s (temp = log2s (wps->dc.error [0])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + wps->dc.shaping_acc [0] = exp2s (temp = log2s (wps->dc.shaping_acc [0])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + wps->dc.error [1] = exp2s (temp = log2s (wps->dc.error [1])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + wps->dc.shaping_acc [1] = exp2s (temp = log2s (wps->dc.shaping_acc [1])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + } + + if (wps->dc.shaping_delta [0] | wps->dc.shaping_delta [1]) { + wps->dc.shaping_delta [0] = exp2s (temp = log2s (wps->dc.shaping_delta [0])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + wps->dc.shaping_delta [1] = exp2s (temp = log2s (wps->dc.shaping_delta [1])); + *byteptr++ = temp; + *byteptr++ = temp >> 8; + } + } + + wpmd->byte_length = byteptr - (char *) wpmd->data; +} + +// Allocate room for and copy the int32 data values into the specified +// metadata structure. This data is used for integer data that has more +// than 24 bits of magnitude or, in some cases, it's used to eliminate +// redundant bits from any audio stream. + +void write_int32_info (WavpackStream *wps, WavpackMetadata *wpmd) +{ + char *byteptr; + + byteptr = wpmd->data = malloc (4); + wpmd->id = ID_INT32_INFO; + *byteptr++ = wps->int32_sent_bits; + *byteptr++ = wps->int32_zeros; + *byteptr++ = wps->int32_ones; + *byteptr++ = wps->int32_dups; + wpmd->byte_length = byteptr - (char *) wpmd->data; +} + +// Allocate room for and copy the multichannel information into the specified +// metadata structure. The first byte is the total number of channels and the +// following bytes represent the channel_mask as described for Microsoft +// WAVEFORMATEX. + +void write_channel_info (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + uint32_t mask = wpc->config.channel_mask; + char *byteptr; + + byteptr = wpmd->data = malloc (4); + wpmd->id = ID_CHANNEL_INFO; + *byteptr++ = wpc->config.num_channels; + + while (mask) { + *byteptr++ = mask; + mask >>= 8; + } + + wpmd->byte_length = byteptr - (char *) wpmd->data; +} + +// Allocate room for and copy the configuration information into the specified +// metadata structure. Currently, we just store the upper 3 bytes of +// config.flags and only in the first block of audio data. Note that this is +// for informational purposes not required for playback or decoding (like +// whether high or fast mode was specified). + +void write_config_info (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + char *byteptr; + + byteptr = wpmd->data = malloc (4); + wpmd->id = ID_CONFIG_BLOCK; + *byteptr++ = (char) (wpc->config.flags >> 8); + *byteptr++ = (char) (wpc->config.flags >> 16); + *byteptr++ = (char) (wpc->config.flags >> 24); + wpmd->byte_length = byteptr - (char *) wpmd->data; +} + +// Pack an entire block of samples (either mono or stereo) into a completed +// WavPack block. This function is actually a shell for pack_samples() and +// performs tasks like handling any shift required by the format, preprocessing +// of floating point data or integer data over 24 bits wide, and implementing +// the "extra" mode (via the extra?.c modules). It is assumed that there is +// sufficient space for the completed block at "wps->blockbuff" and that +// "wps->blockend" points to the end of the available space. A return value of +// FALSE indicates an error. + +static int scan_int32_data (WavpackStream *wps, int32_t *values, int32_t num_values); +static void send_int32_data (WavpackStream *wps, int32_t *values, int32_t num_values); +static int pack_samples (WavpackContext *wpc, int32_t *buffer); + +int pack_block (WavpackContext *wpc, int32_t *buffer) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uint32_t flags = wps->wphdr.flags, sflags = wps->wphdr.flags; + uint32_t sample_count = wps->wphdr.block_samples; + int32_t *orig_data = NULL; + + if (flags & SHIFT_MASK) { + int shift = (flags & SHIFT_MASK) >> SHIFT_LSB; + int mag = (flags & MAG_MASK) >> MAG_LSB; + uint32_t cnt = sample_count; + int32_t *ptr = buffer; + + if (flags & MONO_FLAG) + while (cnt--) + *ptr++ >>= shift; + else + while (cnt--) { + *ptr++ >>= shift; + *ptr++ >>= shift; + } + + if ((mag -= shift) < 0) + flags &= ~MAG_MASK; + else + flags -= (1 << MAG_LSB) * shift; + + wps->wphdr.flags = flags; + } + + if ((flags & FLOAT_DATA) || (flags & MAG_MASK) >> MAG_LSB >= 24) { + if ((!(flags & HYBRID_FLAG) || wpc->wvc_flag) && !(wpc->config.flags & CONFIG_SKIP_WVX)) { + orig_data = malloc (sizeof (f32) * ((flags & MONO_FLAG) ? sample_count : sample_count * 2)); + memcpy (orig_data, buffer, sizeof (f32) * ((flags & MONO_FLAG) ? sample_count : sample_count * 2)); + + if (flags & FLOAT_DATA) { + wps->float_norm_exp = wpc->config.float_norm_exp; + + if (!scan_float_data (wps, (f32 *) buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2)) { + free (orig_data); + orig_data = NULL; + } + } + else { + if (!scan_int32_data (wps, buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2)) { + free (orig_data); + orig_data = NULL; + } + } + } + else { + if (flags & FLOAT_DATA) { + wps->float_norm_exp = wpc->config.float_norm_exp; + + if (scan_float_data (wps, (f32 *) buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2)) + wpc->lossy_blocks = TRUE; + } + else if (scan_int32_data (wps, buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2)) + wpc->lossy_blocks = TRUE; + } + + wpc->config.extra_flags |= EXTRA_SCAN_ONLY; + } + else if (wpc->config.extra_flags) + scan_int32_data (wps, buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2); + + if (wpc->config.extra_flags) { + if (flags & MONO_FLAG) + analyze_mono (wpc, buffer); + else + analyze_stereo (wpc, buffer); + } + else if (!wps->sample_index || !wps->num_terms) { + wpc->config.extra_flags = EXTRA_SCAN_ONLY; + + if (flags & MONO_FLAG) + analyze_mono (wpc, buffer); + else + analyze_stereo (wpc, buffer); + + wpc->config.extra_flags = 0; + } + + if (!pack_samples (wpc, buffer)) { + wps->wphdr.flags = sflags; + + if (orig_data) + free (orig_data); + + return FALSE; + } + else + wps->wphdr.flags = sflags; + + if (orig_data) { + uint32_t data_count; + uchar *cptr; + + if (wpc->wvc_flag) + cptr = wps->block2buff + ((WavpackHeader *) wps->block2buff)->ckSize + 8; + else + cptr = wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 8; + + bs_open_write (&wps->wvxbits, cptr + 8, wpc->wvc_flag ? wps->block2end : wps->blockend); + + if (flags & FLOAT_DATA) + send_float_data (wps, (f32*) orig_data, (flags & MONO_FLAG) ? sample_count : sample_count * 2); + else + send_int32_data (wps, orig_data, (flags & MONO_FLAG) ? sample_count : sample_count * 2); + + data_count = bs_close_write (&wps->wvxbits); + free (orig_data); + + if (data_count) { + if (data_count != (uint32_t) -1) { + *cptr++ = ID_WVX_BITSTREAM | ID_LARGE; + *cptr++ = (data_count += 4) >> 1; + *cptr++ = data_count >> 9; + *cptr++ = data_count >> 17; + *cptr++ = wps->crc_x; + *cptr++ = wps->crc_x >> 8; + *cptr++ = wps->crc_x >> 16; + *cptr = wps->crc_x >> 24; + + if (wpc->wvc_flag) + ((WavpackHeader *) wps->block2buff)->ckSize += data_count + 4; + else + ((WavpackHeader *) wps->blockbuff)->ckSize += data_count + 4; + } + else + return FALSE; + } + } + + return TRUE; +} + +// Scan a buffer of long integer data and determine whether any redundancy in +// the LSBs can be used to reduce the data's magnitude. If yes, then the +// INT32_DATA flag is set and the int32 parameters are set. If bits must still +// be transmitted literally to get down to 24 bits (which is all the integer +// compression code can handle) then we return TRUE to indicate that a wvx +// stream must be created in either lossless mode. + +static int scan_int32_data (WavpackStream *wps, int32_t *values, int32_t num_values) +{ + uint32_t magdata = 0, ordata = 0, xordata = 0, anddata = ~0; + uint32_t crc = 0xffffffff; + int total_shift = 0; + int32_t *dp, count; + + wps->int32_sent_bits = wps->int32_zeros = wps->int32_ones = wps->int32_dups = 0; + + for (dp = values, count = num_values; count--; dp++) { + crc = crc * 9 + (*dp & 0xffff) * 3 + ((*dp >> 16) & 0xffff); + magdata |= (*dp < 0) ? ~*dp : *dp; + xordata |= *dp ^ -(*dp & 1); + anddata &= *dp; + ordata |= *dp; + } + + wps->crc_x = crc; + wps->wphdr.flags &= ~MAG_MASK; + + while (magdata) { + wps->wphdr.flags += 1 << MAG_LSB; + magdata >>= 1; + } + + if (!((wps->wphdr.flags & MAG_MASK) >> MAG_LSB)) { + wps->wphdr.flags &= ~INT32_DATA; + return FALSE; + } + + if (!(ordata & 1)) + while (!(ordata & 1)) { + wps->wphdr.flags -= 1 << MAG_LSB; + wps->int32_zeros++; + total_shift++; + ordata >>= 1; + } + else if (anddata & 1) + while (anddata & 1) { + wps->wphdr.flags -= 1 << MAG_LSB; + wps->int32_ones++; + total_shift++; + anddata >>= 1; + } + else if (!(xordata & 2)) + while (!(xordata & 2)) { + wps->wphdr.flags -= 1 << MAG_LSB; + wps->int32_dups++; + total_shift++; + xordata >>= 1; + } + + if (((wps->wphdr.flags & MAG_MASK) >> MAG_LSB) > 23) { + wps->int32_sent_bits = ((wps->wphdr.flags & MAG_MASK) >> MAG_LSB) - 23; + total_shift += wps->int32_sent_bits; + wps->wphdr.flags &= ~MAG_MASK; + wps->wphdr.flags += 23 << MAG_LSB; + } + + if (total_shift) { + wps->wphdr.flags |= INT32_DATA; + + for (dp = values, count = num_values; count--; dp++) + *dp >>= total_shift; + } + +#if 0 + if (wps->int32_sent_bits + wps->int32_zeros + wps->int32_ones + wps->int32_dups) + error_line ("sent bits = %d, zeros/ones/dups = %d/%d/%d", wps->int32_sent_bits, + wps->int32_zeros, wps->int32_ones, wps->int32_dups); +#endif + + return wps->int32_sent_bits; +} + +// For the specified buffer values and the int32 parameters stored in "wps", +// send the literal bits required to the "wvxbits" bitstream. + +static void send_int32_data (WavpackStream *wps, int32_t *values, int32_t num_values) +{ + int sent_bits = wps->int32_sent_bits, pre_shift; + int32_t mask = (1 << sent_bits) - 1; + int32_t count, value, *dp; + + pre_shift = wps->int32_zeros + wps->int32_ones + wps->int32_dups; + + if (sent_bits) + for (dp = values, count = num_values; count--; dp++) { + value = (*dp >> pre_shift) & mask; + putbits (value, sent_bits, &wps->wvxbits); + } +} + +// Pack an entire block of samples (either mono or stereo) into a completed +// WavPack block. It is assumed that there is sufficient space for the +// completed block at "wps->blockbuff" and that "wps->blockend" points to the +// end of the available space. A return value of FALSE indicates an error. +// Any unsent metadata is transmitted first, then required metadata for this +// block is sent, and finally the compressed integer data is sent. If a "wpx" +// stream is required for floating point data or large integer data, then this +// must be handled outside this function. To find out how much data was written +// the caller must look at the ckSize field of the written WavpackHeader, NOT +// the one in the WavpackStream. + +static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void decorr_stereo_pass_i (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void decorr_stereo_pass_id2 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); + +static int pack_samples (WavpackContext *wpc, int32_t *buffer) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uint32_t sample_count = wps->wphdr.block_samples; + uint32_t flags = wps->wphdr.flags, data_count; + int mag16 = ((flags & MAG_MASK) >> MAG_LSB) >= 16; + int tcount, lossy = FALSE, m = 0; + double noise_acc = 0.0, noise; + struct decorr_pass *dpp; + WavpackMetadata wpmd; + uint32_t crc, crc2, i; + int32_t *bptr; + + crc = crc2 = 0xffffffff; + + wps->wphdr.ckSize = sizeof (WavpackHeader) - 8; + memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader)); + + if (wpc->metacount) { + WavpackMetadata *wpmdp = wpc->metadata; + + while (wpc->metacount) { + copy_metadata (wpmdp, wps->blockbuff, wps->blockend); + wpc->metabytes -= wpmdp->byte_length; + free_metadata (wpmdp++); + wpc->metacount--; + } + + free (wpc->metadata); + wpc->metadata = NULL; + } + + if (!sample_count) + return TRUE; + + write_decorr_terms (wps, &wpmd); + copy_metadata (&wpmd, wps->blockbuff, wps->blockend); + free_metadata (&wpmd); + + write_decorr_weights (wps, &wpmd); + copy_metadata (&wpmd, wps->blockbuff, wps->blockend); + free_metadata (&wpmd); + + write_decorr_samples (wps, &wpmd); + copy_metadata (&wpmd, wps->blockbuff, wps->blockend); + free_metadata (&wpmd); + + write_entropy_vars (wps, &wpmd); + copy_metadata (&wpmd, wps->blockbuff, wps->blockend); + free_metadata (&wpmd); + + if (flags & HYBRID_FLAG) { + write_hybrid_profile (wps, &wpmd); + copy_metadata (&wpmd, wps->blockbuff, wps->blockend); + free_metadata (&wpmd); + } + + if (flags & FLOAT_DATA) { + write_float_info (wps, &wpmd); + copy_metadata (&wpmd, wps->blockbuff, wps->blockend); + free_metadata (&wpmd); + } + + if (flags & INT32_DATA) { + write_int32_info (wps, &wpmd); + copy_metadata (&wpmd, wps->blockbuff, wps->blockend); + free_metadata (&wpmd); + } + + if ((flags & INITIAL_BLOCK) && + (wpc->config.num_channels > 2 || + wpc->config.channel_mask != 0x5 - wpc->config.num_channels)) { + write_channel_info (wpc, &wpmd); + copy_metadata (&wpmd, wps->blockbuff, wps->blockend); + free_metadata (&wpmd); + } + + if ((flags & INITIAL_BLOCK) && !wps->sample_index) { + write_config_info (wpc, &wpmd); + copy_metadata (&wpmd, wps->blockbuff, wps->blockend); + free_metadata (&wpmd); + } + + bs_open_write (&wps->wvbits, wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 12, wps->blockend); + + if (wpc->wvc_flag) { + wps->wphdr.ckSize = sizeof (WavpackHeader) - 8; + memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader)); + + if (flags & HYBRID_SHAPE) { + write_shaping_info (wps, &wpmd); + copy_metadata (&wpmd, wps->block2buff, wps->block2end); + free_metadata (&wpmd); + } + + bs_open_write (&wps->wvcbits, wps->block2buff + ((WavpackHeader *) wps->block2buff)->ckSize + 12, wps->block2end); + } + + /////////////////////// handle lossless mono mode ///////////////////////// + + if (!(flags & HYBRID_FLAG) && (flags & MONO_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t code; + + crc = crc * 3 + (code = *bptr++); + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam; + + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) + sam = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + else + sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_A [0] = code; + } + else { + sam = dpp->samples_A [m]; + dpp->samples_A [(m + dpp->term) & (MAX_TERM - 1)] = code; + } + + code -= apply_weight (dpp->weight_A, sam); + update_weight (dpp->weight_A, dpp->delta, sam, code); + } + + m = (m + 1) & (MAX_TERM - 1); + send_word_lossless (wps, code, 0); + } + + //////////////////// handle the lossless stereo mode ////////////////////// + +#ifdef FAST_ENCODE + else if (!(flags & HYBRID_FLAG) && !(flags & MONO_FLAG)) { + int32_t *eptr = buffer + (sample_count * 2), sam_A, sam_B; + + if (flags & JOINT_STEREO) + for (bptr = buffer; bptr < eptr; bptr += 2) { + crc = crc * 9 + bptr [0] * 3 + bptr [1]; + bptr [1] += ((bptr [0] -= bptr [1]) >> 1); + } + else + for (bptr = buffer; bptr < eptr; bptr += 2) + crc = crc * 9 + bptr [0] * 3 + bptr [1]; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount-- ; dpp++) + if (((flags & MAG_MASK) >> MAG_LSB) >= 16) + decorr_stereo_pass (dpp, buffer, sample_count); + else if (dpp->delta != 2) + decorr_stereo_pass_i (dpp, buffer, sample_count); + else + decorr_stereo_pass_id2 (dpp, buffer, sample_count); + + for (bptr = buffer; bptr < eptr; bptr += 2) { + send_word_lossless (wps, bptr [0], 0); + send_word_lossless (wps, bptr [1], 1); + } + + m = sample_count & (MAX_TERM - 1); + } +#else + else if (!(flags & HYBRID_FLAG) && !(flags & MONO_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i, bptr += 2) { + int32_t left, right, sam_A, sam_B; + + crc = crc * 3 + (left = bptr [0]); + crc = crc * 3 + (right = bptr [1]); + + if (flags & JOINT_STEREO) + right += ((left -= right) >> 1); + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount-- ; dpp++) { + if (dpp->term > 0) { + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_A [0] = left; + dpp->samples_B [0] = right; + } + else { + int k = (m + dpp->term) & (MAX_TERM - 1); + + sam_A = dpp->samples_A [m]; + sam_B = dpp->samples_B [m]; + dpp->samples_A [k] = left; + dpp->samples_B [k] = right; + } + + left -= apply_weight (dpp->weight_A, sam_A); + right -= apply_weight (dpp->weight_B, sam_B); + update_weight (dpp->weight_A, dpp->delta, sam_A, left); + update_weight (dpp->weight_B, dpp->delta, sam_B, right); + } + else { + sam_A = (dpp->term == -2) ? right : dpp->samples_A [0]; + sam_B = (dpp->term == -1) ? left : dpp->samples_B [0]; + dpp->samples_A [0] = right; + dpp->samples_B [0] = left; + left -= apply_weight (dpp->weight_A, sam_A); + right -= apply_weight (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, left); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, right); + } + } + + m = (m + 1) & (MAX_TERM - 1); + send_word_lossless (wps, left, 0); + send_word_lossless (wps, right, 1); + } +#endif + + /////////////////// handle the lossy/hybrid mono mode ///////////////////// + + else if ((flags & HYBRID_FLAG) && (flags & MONO_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t code, temp; + + crc2 = crc2 * 3 + (code = *bptr++); + + if (flags & HYBRID_SHAPE) { + int shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + temp = -apply_weight (shaping_weight, wps->dc.error [0]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [0]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [0] = -code; + code += temp; + } + else + wps->dc.error [0] = -(code += temp); + } + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount-- ; dpp++) + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) + dpp->samples_A [2] = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + else + dpp->samples_A [2] = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + + code -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [2])); + } + else + code -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [m])); + + code = send_word (wps, code, 0); + + while (--dpp >= wps->decorr_passes) { + if (dpp->term > MAX_TERM) { + update_weight (dpp->weight_A, dpp->delta, dpp->samples_A [2], code); + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_A [0] = (code += dpp->aweight_A); + } + else { + int32_t sam = dpp->samples_A [m]; + + update_weight (dpp->weight_A, dpp->delta, sam, code); + dpp->samples_A [(m + dpp->term) & (MAX_TERM - 1)] = (code += dpp->aweight_A); + } + } + + wps->dc.error [0] += code; + m = (m + 1) & (MAX_TERM - 1); + + if ((crc = crc * 3 + code) != crc2) + lossy = TRUE; + + if (wpc->config.flags & CONFIG_CALC_NOISE) { + noise = code - bptr [-1]; + + noise_acc += noise *= noise; + wps->dc.noise_ave = (wps->dc.noise_ave * 0.99) + (noise * 0.01); + + if (wps->dc.noise_ave > wps->dc.noise_max) + wps->dc.noise_max = wps->dc.noise_ave; + } + } + + /////////////////// handle the lossy/hybrid stereo mode /////////////////// + + else if ((flags & HYBRID_FLAG) && !(flags & MONO_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t left, right, temp; + int shaping_weight; + + left = *bptr++; + crc2 = (crc2 * 3 + left) * 3 + (right = *bptr++); + + if (flags & HYBRID_SHAPE) { + shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + temp = -apply_weight (shaping_weight, wps->dc.error [0]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [0]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [0] = -left; + left += temp; + } + else + wps->dc.error [0] = -(left += temp); + + shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16; + temp = -apply_weight (shaping_weight, wps->dc.error [1]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [1]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [1] = -right; + right += temp; + } + else + wps->dc.error [1] = -(right += temp); + } + + if (flags & JOINT_STEREO) + right += ((left -= right) >> 1); + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount-- ; dpp++) + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) { + dpp->samples_A [2] = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + dpp->samples_B [2] = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + dpp->samples_A [2] = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + dpp->samples_B [2] = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + + left -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [2])); + right -= (dpp->aweight_B = apply_weight (dpp->weight_B, dpp->samples_B [2])); + } + else if (dpp->term > 0) { + left -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [m])); + right -= (dpp->aweight_B = apply_weight (dpp->weight_B, dpp->samples_B [m])); + } + else { + if (dpp->term == -1) + dpp->samples_B [0] = left; + else if (dpp->term == -2) + dpp->samples_A [0] = right; + + left -= (dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [0])); + right -= (dpp->aweight_B = apply_weight (dpp->weight_B, dpp->samples_B [0])); + } +#if 0 +if (labs (left) > 60000000 || labs (right) > 60000000) + error_line ("sending %d, %d; samples = %d, %d", left, right, bptr [-2], bptr [-1]); +#endif + left = send_word (wps, left, 0); + right = send_word (wps, right, 1); + + while (--dpp >= wps->decorr_passes) + if (dpp->term > MAX_TERM) { + update_weight (dpp->weight_A, dpp->delta, dpp->samples_A [2], left); + update_weight (dpp->weight_B, dpp->delta, dpp->samples_B [2], right); + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = (left += dpp->aweight_A); + dpp->samples_B [0] = (right += dpp->aweight_B); + } + else if (dpp->term > 0) { + int k = (m + dpp->term) & (MAX_TERM - 1); + + update_weight (dpp->weight_A, dpp->delta, dpp->samples_A [m], left); + dpp->samples_A [k] = (left += dpp->aweight_A); + + update_weight (dpp->weight_B, dpp->delta, dpp->samples_B [m], right); + dpp->samples_B [k] = (right += dpp->aweight_B); + } + else { + if (dpp->term == -1) { + dpp->samples_B [0] = left + dpp->aweight_A; + dpp->aweight_B = apply_weight (dpp->weight_B, dpp->samples_B [0]); + } + else if (dpp->term == -2) { + dpp->samples_A [0] = right + dpp->aweight_B; + dpp->aweight_A = apply_weight (dpp->weight_A, dpp->samples_A [0]); + } + + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], left); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], right); + dpp->samples_B [0] = (left += dpp->aweight_A); + dpp->samples_A [0] = (right += dpp->aweight_B); + } + + if (flags & JOINT_STEREO) + left += (right -= (left >> 1)); + + wps->dc.error [0] += left; + wps->dc.error [1] += right; + m = (m + 1) & (MAX_TERM - 1); + + if ((crc = (crc * 3 + left) * 3 + right) != crc2) + lossy = TRUE; + + if (wpc->config.flags & CONFIG_CALC_NOISE) { + noise = (double)(left - bptr [-2]) * (left - bptr [-2]); + noise += (double)(right - bptr [-1]) * (right - bptr [-1]); + + noise_acc += noise /= 2.0; + wps->dc.noise_ave = (wps->dc.noise_ave * 0.99) + (noise * 0.01); + + if (wps->dc.noise_ave > wps->dc.noise_max) + wps->dc.noise_max = wps->dc.noise_ave; + } + } + + if (m) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0 && dpp->term <= MAX_TERM) { + int32_t temp_A [MAX_TERM], temp_B [MAX_TERM]; + int k; + + memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A)); + memcpy (temp_B, dpp->samples_B, sizeof (dpp->samples_B)); + + for (k = 0; k < MAX_TERM; k++) { + dpp->samples_A [k] = temp_A [m]; + dpp->samples_B [k] = temp_B [m]; + m = (m + 1) & (MAX_TERM - 1); + } + } + + if (wpc->config.flags & CONFIG_CALC_NOISE) + wps->dc.noise_sum += noise_acc; + + flush_word (wps); + data_count = bs_close_write (&wps->wvbits); + + if (data_count) { + if (data_count != (uint32_t) -1) { + uchar *cptr = wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 8; + + *cptr++ = ID_WV_BITSTREAM | ID_LARGE; + *cptr++ = data_count >> 1; + *cptr++ = data_count >> 9; + *cptr++ = data_count >> 17; + ((WavpackHeader *) wps->blockbuff)->ckSize += data_count + 4; + } + else + return FALSE; + } + + ((WavpackHeader *) wps->blockbuff)->crc = crc; + + if (wpc->wvc_flag) { + data_count = bs_close_write (&wps->wvcbits); + + if (data_count && lossy) { + if (data_count != (uint32_t) -1) { + uchar *cptr = wps->block2buff + ((WavpackHeader *) wps->block2buff)->ckSize + 8; + + *cptr++ = ID_WVC_BITSTREAM | ID_LARGE; + *cptr++ = data_count >> 1; + *cptr++ = data_count >> 9; + *cptr++ = data_count >> 17; + ((WavpackHeader *) wps->block2buff)->ckSize += data_count + 4; + } + else + return FALSE; + } + + ((WavpackHeader *) wps->block2buff)->crc = crc2; + } + else if (lossy) + wpc->lossy_blocks = TRUE; + + wps->sample_index += sample_count; + return TRUE; +} + +#ifdef FAST_ENCODE + +static void decorr_stereo_pass_id2 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + 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] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight_d2 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_B [0] = bptr [1]; + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight_d2 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + 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] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight_d2 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_B [0] = bptr [1]; + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight_d2 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + + case 8: + for (m = 0, bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = dpp->samples_A [m]; + dpp->samples_A [m] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight_d2 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + + sam_B = dpp->samples_B [m]; + dpp->samples_B [m] = bptr [1]; + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight_d2 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + m = (m + 1) & (MAX_TERM - 1); + } + + break; + + default: + for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr += 2) { + dpp->samples_A [k] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, dpp->samples_A [m]); + update_weight_d2 (dpp->weight_A, dpp->delta, dpp->samples_A [m], bptr [0]); + + dpp->samples_B [k] = bptr [1]; + bptr [1] -= apply_weight_i (dpp->weight_B, dpp->samples_B [m]); + update_weight_d2 (dpp->weight_B, dpp->delta, dpp->samples_B [m], bptr [1]); + + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = dpp->samples_A [0]; + sam_B = bptr [0]; + dpp->samples_A [0] = bptr [1]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight_clip_d2 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight_clip_d2 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + + case -2: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = bptr [1]; + sam_B = dpp->samples_B [0]; + dpp->samples_B [0] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight_clip_d2 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight_clip_d2 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + + case -3: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = dpp->samples_A [0]; + sam_B = dpp->samples_B [0]; + dpp->samples_A [0] = bptr [1]; + dpp->samples_B [0] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight_clip_d2 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight_clip_d2 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + } +} + +static void decorr_stereo_pass_i (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + 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] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_B [0] = bptr [1]; + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + 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] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_B [0] = bptr [1]; + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + 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] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + + sam_B = dpp->samples_B [m]; + dpp->samples_B [k] = bptr [1]; + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = dpp->samples_A [0]; + sam_B = bptr [0]; + dpp->samples_A [0] = bptr [1]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + + case -2: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = bptr [1]; + sam_B = dpp->samples_B [0]; + dpp->samples_B [0] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + + case -3: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = dpp->samples_A [0]; + sam_B = dpp->samples_B [0]; + dpp->samples_A [0] = bptr [1]; + dpp->samples_B [0] = bptr [0]; + bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + } +} + +static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + 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] = bptr [0]; + bptr [0] -= apply_weight (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_B [0] = bptr [1]; + bptr [1] -= apply_weight (dpp->weight_B, sam_B); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + 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] = bptr [0]; + bptr [0] -= apply_weight (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + dpp->samples_B [1] = dpp->samples_B [0]; + dpp->samples_B [0] = bptr [1]; + bptr [1] -= apply_weight (dpp->weight_B, sam_B); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + 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] = bptr [0]; + bptr [0] -= apply_weight (dpp->weight_A, sam_A); + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + + sam_B = dpp->samples_B [m]; + dpp->samples_B [k] = bptr [1]; + bptr [1] -= apply_weight (dpp->weight_B, sam_B); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = dpp->samples_A [0]; + sam_B = bptr [0]; + dpp->samples_A [0] = bptr [1]; + bptr [0] -= apply_weight (dpp->weight_A, sam_A); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + bptr [1] -= apply_weight (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + + case -2: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = bptr [1]; + sam_B = dpp->samples_B [0]; + dpp->samples_B [0] = bptr [0]; + bptr [0] -= apply_weight (dpp->weight_A, sam_A); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + bptr [1] -= apply_weight (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + + case -3: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = dpp->samples_A [0]; + sam_B = dpp->samples_B [0]; + dpp->samples_A [0] = bptr [1]; + dpp->samples_B [0] = bptr [0]; + bptr [0] -= apply_weight (dpp->weight_A, sam_A); + update_weight_clip (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + bptr [1] -= apply_weight (dpp->weight_B, sam_B); + update_weight_clip (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + } + + break; + } +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// This function returns the accumulated RMS noise as a double if the // +// CALC_NOISE bit was set in the WavPack header. The peak noise can also be // +// returned if desired. See wavpack.c for the calculations required to // +// convert this into decibels of noise below full scale. // +////////////////////////////////////////////////////////////////////////////// + +double pack_noise (WavpackContext *wpc, double *peak) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + + if (peak) + *peak = wps->dc.noise_max; + + return wps->dc.noise_sum; +} diff --git a/Libraries/WavPack/Files/unpack.c b/Libraries/WavPack/Files/unpack.c new file mode 100644 index 000000000..2e1f0e653 --- /dev/null +++ b/Libraries/WavPack/Files/unpack.c @@ -0,0 +1,1453 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 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? modules. For +// maximum efficiency, the conversion is isolated to tight loops that handle +// an entire buffer. + +#include "wavpack.h" + +#include +#include +#include +#include + +// This flag provides faster decoding speed at the expense of more code. The +// improvement applies to 16-bit stereo lossless only. + +#define FAST_DECODE + +#define LOSSY_MUTE + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +#endif + +///////////////////////////// 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) and that the entire block has been read at +// wps->blockbuff. If a correction file is available (wpc->wvc_flag = TRUE) +// then the corresponding correction block must be read into wps->block2buff +// and its WavpackHeader has overwritten the header at wps->wphdr. This is +// where all the metadata blocks are scanned including those that contain +// bitstream data. + +int unpack_init (WavpackContext *wpc) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uchar *blockptr, *block2ptr; + 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 = wps->crc_x = 0xffffffff; + CLEAR (wps->wvbits); + CLEAR (wps->wvcbits); + CLEAR (wps->wvxbits); + CLEAR (wps->decorr_passes); + CLEAR (wps->dc); + CLEAR (wps->w); + + blockptr = wps->blockbuff + sizeof (WavpackHeader); + + while (read_metadata_buff (&wpmd, wps->blockbuff, &blockptr)) + if (!process_metadata (wpc, &wpmd)) { + sprintf (wpc->error_message, "invalid metadata %2x!", wpmd.id); + return FALSE; + } + + block2ptr = wps->block2buff + sizeof (WavpackHeader); + + while (wpc->wvc_flag && wps->wphdr.block_samples && read_metadata_buff (&wpmd, wps->block2buff, &block2ptr)) + if (!process_metadata (wpc, &wpmd)) { + sprintf (wpc->error_message, "invalid metadata %2x in wvc file!", wpmd.id); + return FALSE; + } + + if (wps->wphdr.block_samples && !bs_is_open (&wps->wvbits)) { + if (bs_is_open (&wps->wvcbits)) + strcpy (wpc->error_message, "can't unpack correction files alone!"); + + return FALSE; + } + + if (wps->wphdr.block_samples && !bs_is_open (&wps->wvxbits)) { + 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 (WavpackStream *wps, WavpackMetadata *wpmd) +{ + bs_open_read (&wps->wvbits, wpmd->data, (char *) wpmd->data + wpmd->byte_length); + return TRUE; +} + +// This function initialzes the "correction" bitstream for audio samples, +// which currently must be in the "wvc" file. + +int init_wvc_bitstream (WavpackStream *wps, WavpackMetadata *wpmd) +{ + bs_open_read (&wps->wvcbits, wpmd->data, (char *) wpmd->data + wpmd->byte_length); + return TRUE; +} + +// This function initialzes the "extra" bitstream for audio samples which +// contains the information required to losslessly decompress 32-bit float data +// or integer data that exceeds 24 bits. This bitstream is in the "wv" file +// for pure lossless data or the "wvc" file for hybrid lossless. This data +// would not be used for hybrid lossy mode. There is also a 32-bit CRC stored +// in the first 4 bytes of these blocks. + +int init_wvx_bitstream (WavpackStream *wps, WavpackMetadata *wpmd) +{ + uchar *cp = wpmd->data; + + wps->crc_wvx = *cp++; + wps->crc_wvx |= (int32_t) *cp++ << 8; + wps->crc_wvx |= (int32_t) *cp++ << 16; + wps->crc_wvx |= (int32_t) *cp++ << 24; + + bs_open_read (&wps->wvxbits, cp, (char *) wpmd->data + wpmd->byte_length); + return TRUE; +} + +// Read decorrelation terms from specified metadata block into the +// decorr_passes array. The terms range from -3 to 8, plus 17 & 18; +// other values are reserved and generate errors for now. The delta +// ranges from 0 to 7 with all values valid. Note that the terms are +// stored in the opposite order in the decorr_passes array compared +// to packing. + +int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd) +{ + int termcnt = wpmd->byte_length; + uchar *byteptr = wpmd->data; + struct decorr_pass *dpp; + + if (termcnt > MAX_NTERMS) + return FALSE; + + wps->num_terms = termcnt; + + for (dpp = wps->decorr_passes + termcnt - 1; termcnt--; dpp--) { + dpp->term = (int)(*byteptr & 0x1f) - 5; + dpp->delta = (*byteptr++ >> 5) & 0x7; + + if (!dpp->term || dpp->term < -3 || (dpp->term > MAX_TERM && dpp->term < 17) || dpp->term > 18) + return FALSE; + } + + return TRUE; +} + +// Read decorrelation weights from specified metadata block into the +// decorr_passes array. The weights range +/-1024, but are rounded and +// truncated to fit in signed chars for metadata storage. Weights are +// separate for the two channels and are specified from the "last" term +// (first during encode). Unspecified weights are set to zero. + +int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd) +{ + int termcnt = wpmd->byte_length, tcount; + char *byteptr = wpmd->data; + struct decorr_pass *dpp; + + if (!(wps->wphdr.flags & MONO_FLAG)) + termcnt /= 2; + + if (termcnt > wps->num_terms) + return FALSE; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + dpp->weight_A = dpp->weight_B = 0; + + while (--dpp >= wps->decorr_passes && termcnt--) { + dpp->weight_A = restore_weight (*byteptr++); + + if (!(wps->wphdr.flags & MONO_FLAG)) + dpp->weight_B = restore_weight (*byteptr++); + } + + return TRUE; +} + +// Read decorrelation samples from specified metadata block into the +// decorr_passes array. The samples are signed 32-bit values, but are +// converted to signed log2 values for storage in metadata. Values are +// stored for both channels and are specified from the "last" term +// (first during encode) with unspecified samples set to zero. The +// number of samples stored varies with the actual term value, so +// those must obviously come first in the metadata. + +int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd) +{ + uchar *byteptr = wpmd->data; + uchar *endptr = byteptr + wpmd->byte_length; + struct decorr_pass *dpp; + int tcount; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + CLEAR (dpp->samples_A); + CLEAR (dpp->samples_B); + } + + if (wps->wphdr.version == 0x402 && (wps->wphdr.flags & HYBRID_FLAG)) { + wps->dc.error [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + wps->dc.error [1] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + } + } + + while (dpp-- > wps->decorr_passes && byteptr < endptr) + if (dpp->term > MAX_TERM) { + dpp->samples_A [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + dpp->samples_A [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + dpp->samples_B [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + dpp->samples_B [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + } + } + else if (dpp->term < 0) { + dpp->samples_A [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + dpp->samples_B [0] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + } + else { + int m = 0, cnt = dpp->term; + + while (cnt--) { + dpp->samples_A [m] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + dpp->samples_B [m] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + } + + m++; + } + } + + return byteptr == endptr; +} + +// Read the shaping weights from specified metadata block into the +// WavpackStream structure. Note that there must be two values (even +// for mono streams) and that the values are stored in the same +// manner as decorrelation weights. These would normally be read from +// the "correction" file and are used for lossless reconstruction of +// hybrid data. + +int read_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd) +{ + if (wpmd->byte_length == 2) { + char *byteptr = wpmd->data; + + wps->dc.shaping_acc [0] = (int32_t) restore_weight (*byteptr++) << 16; + wps->dc.shaping_acc [1] = (int32_t) restore_weight (*byteptr++) << 16; + return TRUE; + } + else if (wpmd->byte_length >= (wps->wphdr.flags & MONO_FLAG ? 4 : 8)) { + uchar *byteptr = wpmd->data; + + wps->dc.error [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + wps->dc.shaping_acc [0] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + wps->dc.error [1] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + wps->dc.shaping_acc [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + } + + if (wpmd->byte_length == (wps->wphdr.flags & MONO_FLAG ? 6 : 12)) { + wps->dc.shaping_delta [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + + if (!(wps->wphdr.flags & MONO_FLAG)) + wps->dc.shaping_delta [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8))); + } + + return TRUE; + } + + return FALSE; +} + +// 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; +} + +// Read wrapper data from metadata. Currently, this consists of the RIFF +// header and trailer that wav files contain around the audio data but could +// be used for other formats as well. Because WavPack files contain all the +// information required for decoding and playback, this data can probably +// be ignored except when an exact wavefile restoration is needed. + +int read_wrapper_data (WavpackContext *wpc, WavpackMetadata *wpmd) +{ + if (wpc->open_flags & OPEN_WRAPPER) { + wpc->wrapper_data = realloc (wpc->wrapper_data, wpc->wrapper_bytes + wpmd->byte_length); + memcpy (wpc->wrapper_data + wpc->wrapper_bytes, wpmd->data, wpmd->byte_length); + wpc->wrapper_bytes += wpmd->byte_length; + } + + return TRUE; +} + +#ifdef UNPACK + +// This monster actually unpacks the WavPack bitstream(s) into the specified +// buffer as 32-bit integers or floats (depending on orignal data). Lossy +// samples will be clipped to their original limits (i.e. 8-bit samples are +// clipped to -128/+127) but are still returned in longs. It is up to the +// caller to potentially reformat this for the final output including any +// multichannel distribution, block alignment or endian compensation. The +// function unpack_init() must have been called and the entire WavPack block +// must still be visible (although wps->blockbuff will not be accessed again). +// For maximum clarity, the function is broken up into segments that handle +// various modes. This makes for a few extra infrequent flag checks, but +// makes the code easier to follow because the nesting does not become so +// deep. For maximum efficiency, the conversion is isolated to tight loops +// that handle an entire buffer. The function returns the total number of +// samples unpacked, which can be less than the number requested if an error +// occurs or the end of the block is reached. + +static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void decorr_stereo_pass_i (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void decorr_stereo_pass_id0 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void decorr_stereo_pass_id1 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); +static void decorr_stereo_pass_id2 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count); + +static void fixup_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); + +int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uint32_t flags = wps->wphdr.flags, crc = wps->crc, i; + int32_t mute_limit = (1L << ((flags & MAG_MASK) >> MAG_LSB)) + 2; + int32_t correction [2], read_word, *bptr; + struct decorr_pass *dpp; + int tcount, m = 0; + + if (wps->sample_index + sample_count > wps->wphdr.block_index + wps->wphdr.block_samples) + sample_count = wps->wphdr.block_index + wps->wphdr.block_samples - wps->sample_index; + + if (wps->mute_error) { + memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8)); + wps->sample_index += sample_count; + return sample_count; + } + + if ((flags & HYBRID_FLAG) && !wpc->wvc_flag) + mute_limit *= 2; + + ///////////////// handle version 4 lossless mono data ///////////////////// + + if (!(flags & HYBRID_FLAG) && (flags & MONO_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + if ((read_word = get_word_lossless (wps, 0)) == WORD_EOF) + break; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam, temp; + int k; + + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) + sam = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + else + sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + k = 0; + } + else { + sam = dpp->samples_A [m]; + k = (m + dpp->term) & (MAX_TERM - 1); + } + + temp = apply_weight (dpp->weight_A, sam) + read_word; + update_weight (dpp->weight_A, dpp->delta, sam, read_word); + dpp->samples_A [k] = read_word = temp; + } + + if (labs (read_word) > mute_limit) + break; + + m = (m + 1) & (MAX_TERM - 1); + crc = crc * 3 + read_word; + *bptr++ = read_word; + } + + //////////////// handle version 4 lossless stereo data //////////////////// + + else if (!wpc->wvc_flag && !(flags & MONO_FLAG)) { + int32_t *eptr = buffer + (sample_count * 2); + + i = sample_count; + + if (flags & HYBRID_FLAG) { + for (bptr = buffer; bptr < eptr; bptr += 2) + if ((bptr [0] = get_word (wps, 0, NULL)) == WORD_EOF || + (bptr [1] = get_word (wps, 1, NULL)) == WORD_EOF) { + i = (bptr - buffer) / 2; + break; + } + } + else + for (bptr = buffer; bptr < eptr; bptr += 2) + if ((bptr [0] = get_word_lossless (wps, 0)) == WORD_EOF || + (bptr [1] = get_word_lossless (wps, 1)) == WORD_EOF) { + i = (bptr - buffer) / 2; + break; + } + +#ifdef FAST_DECODE + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (((flags & MAG_MASK) >> MAG_LSB) >= 16) + decorr_stereo_pass (dpp, buffer, sample_count); + else if (dpp->delta > 2) + decorr_stereo_pass_i (dpp, buffer, sample_count); + else if (dpp->delta == 2) + decorr_stereo_pass_id2 (dpp, buffer, sample_count); + else if (dpp->delta == 1) + decorr_stereo_pass_id1 (dpp, buffer, sample_count); + else + decorr_stereo_pass_id0 (dpp, buffer, sample_count); +#else + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + decorr_stereo_pass (dpp, buffer, sample_count); +#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]; + } + + m = sample_count & (MAX_TERM - 1); + } + + //////////////// handle version 4 lossy/hybrid mono data ////////////////// + + else if ((flags & HYBRID_FLAG) && (flags & MONO_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + + if ((read_word = get_word (wps, 0, correction)) == WORD_EOF) + break; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam, temp; + int k; + + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) + sam = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + else + sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + k = 0; + } + else { + sam = dpp->samples_A [m]; + k = (m + dpp->term) & (MAX_TERM - 1); + } + + temp = apply_weight (dpp->weight_A, sam) + read_word; + update_weight (dpp->weight_A, dpp->delta, sam, read_word); + dpp->samples_A [k] = read_word = temp; + } + + m = (m + 1) & (MAX_TERM - 1); + + if (wpc->wvc_flag) { + if (flags & HYBRID_SHAPE) { + int shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + int32_t temp = -apply_weight (shaping_weight, wps->dc.error [0]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [0]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [0] = temp - correction [0]; + } + else + wps->dc.error [0] = -correction [0]; + + read_word += correction [0] - temp; + } + else + read_word += correction [0]; + } + + crc = crc * 3 + read_word; + +#ifdef LOSSY_MUTE + if (labs (read_word) > mute_limit) + break; +#endif + *bptr++ = read_word; + } + + //////////////// handle version 4 lossy/hybrid stereo data //////////////// + + else if (wpc->wvc_flag && !(flags & MONO_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t left, right, left_c, right_c, left2, right2; + + if ((left = get_word (wps, 0, correction)) == WORD_EOF || + (right = get_word (wps, 1, correction + 1)) == WORD_EOF) + break; + + if (flags & CROSS_DECORR) { + left_c = left + correction [0]; + right_c = right + correction [1]; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam_A, sam_B; + + if (dpp->term > 0) { + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + } + else { + sam_A = dpp->samples_A [m]; + sam_B = dpp->samples_B [m]; + } + + left_c += apply_weight (dpp->weight_A, sam_A); + right_c += apply_weight (dpp->weight_B, sam_B); + } + else if (dpp->term == -1) { + left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]); + right_c += apply_weight (dpp->weight_B, left_c); + } + else { + right_c += apply_weight (dpp->weight_B, dpp->samples_B [0]); + + if (dpp->term == -3) + left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]); + else + left_c += apply_weight (dpp->weight_A, right_c); + } + } + + if (flags & JOINT_STEREO) + left_c += (right_c -= (left_c >> 1)); + } + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam_A, sam_B; + + if (dpp->term > 0) { + int k; + + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + k = 0; + } + else { + sam_A = dpp->samples_A [m]; + sam_B = dpp->samples_B [m]; + k = (m + dpp->term) & (MAX_TERM - 1); + } + + left2 = apply_weight (dpp->weight_A, sam_A) + left; + right2 = apply_weight (dpp->weight_B, sam_B) + right; + + update_weight (dpp->weight_A, dpp->delta, sam_A, left); + update_weight (dpp->weight_B, dpp->delta, sam_B, right); + + dpp->samples_A [k] = left = left2; + dpp->samples_B [k] = right = right2; + } + else if (dpp->term == -1) { + left2 = left + apply_weight (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], left); + left = left2; + right2 = right + apply_weight (dpp->weight_B, left2); + update_weight_clip (dpp->weight_B, dpp->delta, left2, right); + dpp->samples_A [0] = right = right2; + } + else { + right2 = right + apply_weight (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], right); + right = right2; + + if (dpp->term == -3) { + right2 = dpp->samples_A [0]; + dpp->samples_A [0] = right; + } + + left2 = left + apply_weight (dpp->weight_A, right2); + update_weight_clip (dpp->weight_A, dpp->delta, right2, left); + dpp->samples_B [0] = left = left2; + } + } + + m = (m + 1) & (MAX_TERM - 1); + + if (!(flags & CROSS_DECORR)) { + left_c = left + correction [0]; + right_c = right + correction [1]; + + if (flags & JOINT_STEREO) + left_c += (right_c -= (left_c >> 1)); + } + + if (flags & JOINT_STEREO) + left += (right -= (left >> 1)); + + if (flags & HYBRID_SHAPE) { + int shaping_weight; + int32_t temp; + + correction [0] = left_c - left; + shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + temp = -apply_weight (shaping_weight, wps->dc.error [0]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [0]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [0] = temp - correction [0]; + } + else + wps->dc.error [0] = -correction [0]; + + left = left_c - temp; + correction [1] = right_c - right; + shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16; + temp = -apply_weight (shaping_weight, wps->dc.error [1]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [1]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [1] = temp - correction [1]; + } + else + wps->dc.error [1] = -correction [1]; + + right = right_c - temp; + } + else { + left = left_c; + right = right_c; + } + +#ifdef LOSSY_MUTE + if (labs (left) > mute_limit || labs (right) > mute_limit) + break; +#endif + crc = (crc * 3 + left) * 3 + right; + *bptr++ = left; + *bptr++ = right; + } + + if (i != sample_count) { + memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8)); + wps->mute_error = TRUE; + i = sample_count; + + if (bs_is_open (&wps->wvxbits)) + bs_close_read (&wps->wvxbits); + } + + if (m) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0 && dpp->term <= MAX_TERM) { + int32_t temp_A [MAX_TERM], temp_B [MAX_TERM]; + int k; + + memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A)); + memcpy (temp_B, dpp->samples_B, sizeof (dpp->samples_B)); + + for (k = 0; k < MAX_TERM; k++) { + dpp->samples_A [k] = temp_A [m]; + dpp->samples_B [k] = temp_B [m]; + m = (m + 1) & (MAX_TERM - 1); + } + } + + fixup_samples (wpc, buffer, i); + + if ((flags & FLOAT_DATA) && (wpc->open_flags & OPEN_NORMALIZE)) + float_normalize (buffer, (flags & MONO_FLAG) ? i : i * 2, + 127 - wps->float_norm_exp + wpc->norm_offset); + + wps->sample_index += i; + wps->crc = crc; + + return i; +} + +static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + 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]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = apply_weight (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [0] = apply_weight (dpp->weight_B, sam_B) + bptr [1]; + + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [0]; + 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; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = apply_weight (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [0] = apply_weight (dpp->weight_B, sam_B) + bptr [1]; + + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [0]; + 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]; + sam_B = dpp->samples_B [m]; + + dpp->samples_A [k] = apply_weight (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [k] = apply_weight (dpp->weight_B, sam_B) + bptr [1]; + + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [k]; + bptr [1] = dpp->samples_B [k]; + + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = bptr [0] + apply_weight (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + bptr [0] = sam_A; + dpp->samples_A [0] = bptr [1] + apply_weight (dpp->weight_B, sam_A); + update_weight_clip (dpp->weight_B, dpp->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 (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [1] = sam_B; + dpp->samples_B [0] = bptr [0] + apply_weight (dpp->weight_A, sam_B); + update_weight_clip (dpp->weight_A, dpp->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 (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + sam_B = bptr [1] + apply_weight (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [0] = dpp->samples_B [0] = sam_A; + bptr [1] = dpp->samples_A [0] = sam_B; + } + + break; + } +} + +#ifdef FAST_DECODE + +static void decorr_stereo_pass_i (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + 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]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = apply_weight_i (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [0] = apply_weight_i (dpp->weight_B, sam_B) + bptr [1]; + + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [0]; + 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; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = apply_weight_i (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [0] = apply_weight_i (dpp->weight_B, sam_B) + bptr [1]; + + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [0]; + 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]; + sam_B = dpp->samples_B [m]; + + dpp->samples_A [k] = apply_weight_i (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [k] = apply_weight_i (dpp->weight_B, sam_B) + bptr [1]; + + update_weight (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [k]; + bptr [1] = dpp->samples_B [k]; + + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = bptr [0] + apply_weight_i (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + bptr [0] = sam_A; + dpp->samples_A [0] = bptr [1] + apply_weight_i (dpp->weight_B, sam_A); + update_weight_clip (dpp->weight_B, dpp->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_i (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [1] = sam_B; + dpp->samples_B [0] = bptr [0] + apply_weight_i (dpp->weight_A, sam_B); + update_weight_clip (dpp->weight_A, dpp->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_i (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + sam_B = bptr [1] + apply_weight_i (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [0] = dpp->samples_B [0] = sam_A; + bptr [1] = dpp->samples_A [0] = sam_B; + } + + break; + } +} + +static void decorr_stereo_pass_id2 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + 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]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = apply_weight_i (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [0] = apply_weight_i (dpp->weight_B, sam_B) + bptr [1]; + + update_weight_d2 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight_d2 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [0]; + 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; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = apply_weight_i (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [0] = apply_weight_i (dpp->weight_B, sam_B) + bptr [1]; + + update_weight_d2 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight_d2 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [0]; + 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]; + sam_B = dpp->samples_B [m]; + + dpp->samples_A [k] = apply_weight_i (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [k] = apply_weight_i (dpp->weight_B, sam_B) + bptr [1]; + + update_weight_d2 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight_d2 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [k]; + bptr [1] = dpp->samples_B [k]; + + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = bptr [0] + apply_weight_i (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip_d2 (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + bptr [0] = sam_A; + dpp->samples_A [0] = bptr [1] + apply_weight_i (dpp->weight_B, sam_A); + update_weight_clip_d2 (dpp->weight_B, dpp->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_i (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip_d2 (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [1] = sam_B; + dpp->samples_B [0] = bptr [0] + apply_weight_i (dpp->weight_A, sam_B); + update_weight_clip_d2 (dpp->weight_A, dpp->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_i (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip_d2 (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + sam_B = bptr [1] + apply_weight_i (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip_d2 (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [0] = dpp->samples_B [0] = sam_A; + bptr [1] = dpp->samples_A [0] = sam_B; + } + + break; + } +} + +static void decorr_stereo_pass_id1 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + 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]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = apply_weight_i (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [0] = apply_weight_i (dpp->weight_B, sam_B) + bptr [1]; + + update_weight_d1 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight_d1 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [0]; + 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; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = apply_weight_i (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [0] = apply_weight_i (dpp->weight_B, sam_B) + bptr [1]; + + update_weight_d1 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight_d1 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [0]; + 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]; + sam_B = dpp->samples_B [m]; + + dpp->samples_A [k] = apply_weight_i (dpp->weight_A, sam_A) + bptr [0]; + dpp->samples_B [k] = apply_weight_i (dpp->weight_B, sam_B) + bptr [1]; + + update_weight_d1 (dpp->weight_A, dpp->delta, sam_A, bptr [0]); + update_weight_d1 (dpp->weight_B, dpp->delta, sam_B, bptr [1]); + + bptr [0] = dpp->samples_A [k]; + bptr [1] = dpp->samples_B [k]; + + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = bptr [0] + apply_weight_i (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip_d1 (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + bptr [0] = sam_A; + dpp->samples_A [0] = bptr [1] + apply_weight_i (dpp->weight_B, sam_A); + update_weight_clip_d1 (dpp->weight_B, dpp->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_i (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip_d1 (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [1] = sam_B; + dpp->samples_B [0] = bptr [0] + apply_weight_i (dpp->weight_A, sam_B); + update_weight_clip_d1 (dpp->weight_A, dpp->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_i (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip_d1 (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]); + sam_B = bptr [1] + apply_weight_i (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip_d1 (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]); + bptr [0] = dpp->samples_B [0] = sam_A; + bptr [1] = dpp->samples_A [0] = sam_B; + } + + break; + } +} + +static void decorr_stereo_pass_id0 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count) +{ + 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]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = bptr [0] += apply_weight_i (dpp->weight_A, sam_A); + dpp->samples_B [0] = bptr [1] += apply_weight_i (dpp->weight_B, sam_B); + } + + break; + + case 18: + for (bptr = buffer; bptr < eptr; bptr += 2) { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + + dpp->samples_A [0] = bptr [0] += apply_weight_i (dpp->weight_A, sam_A); + dpp->samples_B [0] = bptr [1] += apply_weight_i (dpp->weight_B, sam_B); + } + + break; + + default: + for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr += 2) { + dpp->samples_A [k] = bptr [0] += apply_weight_i (dpp->weight_A, dpp->samples_A [m]); + dpp->samples_B [k] = bptr [1] += apply_weight_i (dpp->weight_B, dpp->samples_B [m]); + m = (m + 1) & (MAX_TERM - 1); + k = (k + 1) & (MAX_TERM - 1); + } + + break; + + case -1: + for (bptr = buffer; bptr < eptr; bptr += 2) { + bptr [0] += apply_weight_i (dpp->weight_A, dpp->samples_A [0]); + dpp->samples_A [0] = bptr [1] += apply_weight_i (dpp->weight_B, bptr [0]); + } + + break; + + case -2: + for (bptr = buffer; bptr < eptr; bptr += 2) { + bptr [1] += apply_weight_i (dpp->weight_B, dpp->samples_B [0]); + dpp->samples_B [0] = bptr [0] += apply_weight_i (dpp->weight_A, bptr [1]); + } + + break; + + case -3: + for (bptr = buffer; bptr < eptr; bptr += 2) { + bptr [0] += apply_weight_i (dpp->weight_A, dpp->samples_A [0]); + dpp->samples_A [0] = bptr [1] += apply_weight_i (dpp->weight_B, dpp->samples_B [0]); + dpp->samples_B [0] = bptr [0]; + } + + break; + } +} + +#endif + +// 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 (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uint32_t flags = wps->wphdr.flags; + int lossy_flag = (flags & HYBRID_FLAG) && !wpc->wvc_flag; + 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; + uint32_t data, mask = (1 << sent_bits) - 1; + int32_t *dptr = buffer; + + if (bs_is_open (&wps->wvxbits)) { + uint32_t crc = wps->crc_x; + + while (count--) { +// if (sent_bits) { + getbits (&data, sent_bits, &wps->wvxbits); + *dptr = (*dptr << sent_bits) | (data & mask); +// } + + if (zeros) + *dptr <<= zeros; + else if (ones) + *dptr = ((*dptr + 1) << ones) - 1; + else if (dups) + *dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1); + + crc = crc * 9 + (*dptr & 0xffff) * 3 + ((*dptr >> 16) & 0xffff); + dptr++; + } + + wps->crc_x = crc; + } + else if (!sent_bits && (zeros + ones + dups)) { + while (lossy_flag && (flags & BYTES_STORED) == 3 && shift < 8) { + if (zeros) + zeros--; + else if (ones) + ones--; + else if (dups) + dups--; + else + break; + + shift++; + } + + 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 (lossy_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: + 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) +{ + int result = 0, stream; + + for (stream = 0; stream < wpc->num_streams; stream++) { + WavpackStream *wps = wpc->streams [stream]; + + if (wps->crc != wps->wphdr.crc) + ++result; + else if (bs_is_open (&wps->wvxbits) && wps->crc_x != wps->crc_wvx) + ++result; + } + + return result; +} + +#endif diff --git a/Libraries/WavPack/Files/unpack3.c b/Libraries/WavPack/Files/unpack3.c new file mode 100644 index 000000000..8f8e0c5d0 --- /dev/null +++ b/Libraries/WavPack/Files/unpack3.c @@ -0,0 +1,2010 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// unpack3.c + +// This module provides unpacking for WavPack files prior to version 4.0, +// not including "raw" files. As these modes are all obsolete and are no +// longer written, this code will not be fully documented other than the +// global functions. However, full documenation is provided in the version +// 3.97 source code. + +#include +#include +#include +#include + +#include "wavpack.h" +#include "unpack3.h" + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +#endif + +static void unpack_init3 (WavpackStream3 *wps); +static int bs_open_read3 (Bitstream3 *bs, stream_reader *reader, void *id); +static void bs_close_read3 (Bitstream3 *bs); +#ifdef SEEKING +static void bs_restore3 (Bitstream3 *bs); +#endif + +// This provides an extension to the WavpackOpenFileRead () function contained +// in the wputils.c module. It is assumed that an 'R' had been read as the +// first character of the file/stream (indicating a non-raw pre version 4.0 +// WavPack file) and had been pushed back onto the stream (or simply seeked +// back to). + +WavpackContext *open_file3 (WavpackContext *wpc, char *error) +{ + RiffChunkHeader RiffChunkHeader; + ChunkHeader ChunkHeader; + WavpackHeader3 wphdr; + WavpackStream3 *wps; + WaveHeader3 wavhdr; + + wpc->stream3 = wps = (WavpackStream3 *) malloc (sizeof (WavpackStream3)); + CLEAR (*wps); + + if (wpc->reader->read_bytes (wpc->wv_in, &RiffChunkHeader, sizeof (RiffChunkHeader)) != + sizeof (RiffChunkHeader)) { + strcpy (error, "not a valid WavPack file!"); + return WavpackCloseFile (wpc); + } + + if (!strncmp (RiffChunkHeader.ckID, "RIFF", 4) && !strncmp (RiffChunkHeader.formType, "WAVE", 4)) { + + if (wpc->open_flags & OPEN_WRAPPER) { + wpc->wrapper_data = malloc (wpc->wrapper_bytes = sizeof (RiffChunkHeader)); + memcpy (wpc->wrapper_data, &RiffChunkHeader, sizeof (RiffChunkHeader)); + } + + // If the first chunk is a wave RIFF header, then read the various chunks + // until we get to the "data" chunk (and WavPack header should follow). If + // the first chunk is not a RIFF, then we assume a "raw" WavPack file and + // the WavPack header must be first. + + while (1) { + + if (wpc->reader->read_bytes (wpc->wv_in, &ChunkHeader, sizeof (ChunkHeader)) != + sizeof (ChunkHeader)) { + strcpy (error, "not a valid WavPack file!"); + return WavpackCloseFile (wpc); + } + else { + if (wpc->open_flags & OPEN_WRAPPER) { + wpc->wrapper_data = realloc (wpc->wrapper_data, wpc->wrapper_bytes + sizeof (ChunkHeader)); + memcpy (wpc->wrapper_data + wpc->wrapper_bytes, &ChunkHeader, sizeof (ChunkHeader)); + wpc->wrapper_bytes += sizeof (ChunkHeader); + } + + little_endian_to_native (&ChunkHeader, ChunkHeaderFormat); + + if (!strncmp (ChunkHeader.ckID, "fmt ", 4)) { + + if (ChunkHeader.ckSize < sizeof (wavhdr) || + wpc->reader->read_bytes (wpc->wv_in, &wavhdr, sizeof (wavhdr)) != sizeof (wavhdr)) { + strcpy (error, "not a valid WavPack file!"); + return WavpackCloseFile (wpc); + } + else if (wpc->open_flags & OPEN_WRAPPER) { + wpc->wrapper_data = realloc (wpc->wrapper_data, wpc->wrapper_bytes + sizeof (wavhdr)); + memcpy (wpc->wrapper_data + wpc->wrapper_bytes, &wavhdr, sizeof (wavhdr)); + wpc->wrapper_bytes += sizeof (wavhdr); + } + + little_endian_to_native (&wavhdr, WaveHeader3Format); + + if (ChunkHeader.ckSize > sizeof (wavhdr)) { + uint32_t bytes_to_skip = (ChunkHeader.ckSize + 1 - sizeof (wavhdr)) & ~1L; + + if (wpc->open_flags & OPEN_WRAPPER) { + wpc->wrapper_data = realloc (wpc->wrapper_data, wpc->wrapper_bytes + bytes_to_skip); + wpc->reader->read_bytes (wpc->wv_in, wpc->wrapper_data + wpc->wrapper_bytes, bytes_to_skip); + wpc->wrapper_bytes += bytes_to_skip; + } + else { + uchar *temp = malloc (bytes_to_skip); + wpc->reader->read_bytes (wpc->wv_in, temp, bytes_to_skip); + free (temp); + } + } + } + else if (!strncmp (ChunkHeader.ckID, "data", 4)) { + wpc->total_samples = ChunkHeader.ckSize / wavhdr.NumChannels / + ((wavhdr.BitsPerSample > 16) ? 3 : 2); + + break; + } + else if ((ChunkHeader.ckSize + 1) & ~1L) { + uint32_t bytes_to_skip = (ChunkHeader.ckSize + 1) & ~1L; + + if (wpc->open_flags & OPEN_WRAPPER) { + wpc->wrapper_data = realloc (wpc->wrapper_data, wpc->wrapper_bytes + bytes_to_skip); + wpc->reader->read_bytes (wpc->wv_in, wpc->wrapper_data + wpc->wrapper_bytes, bytes_to_skip); + wpc->wrapper_bytes += bytes_to_skip; + } + else { + uchar *temp = malloc (bytes_to_skip); + wpc->reader->read_bytes (wpc->wv_in, temp, bytes_to_skip); + free (temp); + } + } + } + } + } + else { + strcpy (error, "not a valid WavPack file!"); + return WavpackCloseFile (wpc); + } + + if (wpc->reader->read_bytes (wpc->wv_in, &wphdr, 10) != 10) { + strcpy (error, "not a valid WavPack file!"); + return WavpackCloseFile (wpc); + } + + if (((char *) &wphdr) [8] == 2 && (wpc->reader->read_bytes (wpc->wv_in, ((char *) &wphdr) + 10, 2) != 2)) { + strcpy (error, "not a valid WavPack file!"); + return WavpackCloseFile (wpc); + } + else if (((char *) &wphdr) [8] == 3 && (wpc->reader->read_bytes (wpc->wv_in, ((char *) &wphdr) + 10, + sizeof (wphdr) - 10) != sizeof (wphdr) - 10)) { + strcpy (error, "not a valid WavPack file!"); + return WavpackCloseFile (wpc); + } + + little_endian_to_native (&wphdr, WavpackHeader3Format); + + // make sure this is a version we know about + + if (strncmp (wphdr.ckID, "wvpk", 4) || wphdr.version < 1 || wphdr.version > 3) { + strcpy (error, "not a valid WavPack file!"); + return WavpackCloseFile (wpc); + } + + // Because I ran out of flag bits in the WavPack header, an amazingly ugly + // kludge was forced upon me! This code takes care of preparing the flags + // field for internal use and checking for unknown formats we can't decode + + if (wphdr.version == 3) { + + if (wphdr.flags & EXTREME_DECORR) { + + if ((wphdr.flags & NOT_STORED_FLAGS) || + ((wphdr.bits) && + (((wphdr.flags & NEW_HIGH_FLAG) && + (wphdr.flags & (FAST_FLAG | HIGH_FLAG))) || + (wphdr.flags & CROSS_DECORR)))) { + strcpy (error, "not a valid WavPack file!"); + return WavpackCloseFile (wpc); + } + + if (wphdr.flags & CANCEL_EXTREME) + wphdr.flags &= ~(EXTREME_DECORR | CANCEL_EXTREME); + } + else + wphdr.flags &= ~CROSS_DECORR; + } + + // check to see if we should look for a "correction" file, and if so try + // to open it for reading, then set WVC_FLAG accordingly + + if (wpc->wvc_in && wphdr.version == 3 && wphdr.bits && (wphdr.flags & NEW_HIGH_FLAG)) { + wpc->file2len = wpc->reader->get_length (wpc->wvc_in); + wphdr.flags |= WVC_FLAG; + wpc->wvc_flag = TRUE; + } + else + wphdr.flags &= ~WVC_FLAG; + + // check WavPack version to handle special requirements of versions + // before 3.0 that had smaller headers + + if (wphdr.version < 3) { + wphdr.total_samples = wpc->total_samples; + wphdr.flags = wavhdr.NumChannels == 1 ? MONO_FLAG : 0; + wphdr.shift = 16 - wavhdr.BitsPerSample; + + if (wphdr.version == 1) + wphdr.bits = 0; + } + + wpc->config.sample_rate = wavhdr.SampleRate; + wpc->config.num_channels = wavhdr.NumChannels; + + if (wphdr.flags & MONO_FLAG) + wpc->config.flags |= CONFIG_MONO_FLAG; + + if (wphdr.flags & EXTREME_DECORR) + wpc->config.flags |= CONFIG_HIGH_FLAG; + + if (wphdr.bits) { + if (wphdr.flags & NEW_HIGH_FLAG) + wpc->config.flags |= CONFIG_HYBRID_FLAG; + else + wpc->config.flags |= CONFIG_LOSSY_MODE; + } + else if (!(wphdr.flags & HIGH_FLAG)) + wpc->config.flags |= CONFIG_FAST_FLAG; + + wpc->config.bytes_per_sample = (wphdr.flags & BYTES_3) ? 3 : 2; + wpc->config.bits_per_sample = wavhdr.BitsPerSample; + + memcpy (&wps->wphdr, &wphdr, sizeof (wphdr)); + wps->wvbits.bufsiz = wps->wvcbits.bufsiz = 1024 * 1024; + return wpc; +} + +// return currently decoded sample index + +uint32_t get_sample_index3 (WavpackContext *wpc) +{ + WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3; + + return (wps) ? wps->sample_index : (uint32_t) -1; +} + +int get_version3 (WavpackContext *wpc) +{ + WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3; + + return (wps) ? wps->wphdr.version : 0; +} + +void free_stream3 (WavpackContext *wpc) +{ + WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3; + + if (wps) { +#ifdef SEEKING + if (wps->unpack_data) + free (wps->unpack_data); +#endif + if (wps->wphdr.flags & WVC_FLAG) + bs_close_read3 (&wps->wvcbits); + + bs_close_read3 (&wps->wvbits); + + free (wps); + } +} + +static void bs_read3 (Bitstream3 *bs) +{ + uint32_t bytes_read; + + bytes_read = bs->reader->read_bytes (bs->id, bs->buf, bs->bufsiz); + bs->end = bs->buf + bytes_read; + bs->fpos += bytes_read; + + if (bs->end == bs->buf) { + memset (bs->buf, -1, bs->bufsiz); + bs->end += bs->bufsiz; + } + + bs->ptr = bs->buf; +} + +// Open the specified BitStream and associate with the specified file. The +// "bufsiz" field of the structure must be preset with the desired buffer +// size and the file's read pointer must be set to where the desired bit +// data is located. A return value of TRUE indicates an error in +// allocating buffer space. + +static int bs_open_read3 (Bitstream3 *bs, stream_reader *reader, void *id) +{ + bs->fpos = (bs->reader = reader)->get_pos (bs->id = id); + + if (!bs->buf) + bs->buf = (uchar *) malloc (bs->bufsiz); + + bs->end = bs->buf + bs->bufsiz; + bs->ptr = bs->end - 1; + bs->sr = bs->bc = 0; + bs->error = bs->buf ? 0 : 1; + bs->wrap = bs_read3; + return bs->error; +} + +#ifdef SEEKING + +// This function is called after a call to unpack_restore() has restored +// the BitStream structure to a previous state and causes any required data +// to be read from the file. This function is NOT supported for overlapped +// operation. + +void bs_restore3 (Bitstream3 *bs) +{ + uint32_t bytes_to_read = bs->end - bs->ptr - 1, bytes_read; + + bs->reader->set_pos_abs (bs->id, bs->fpos - bytes_to_read); + + if (bytes_to_read > 0) { + + bytes_read = bs->reader->read_bytes (bs->id, bs->ptr + 1, bytes_to_read); + + if (bytes_to_read != bytes_read) + bs->end = bs->ptr + 1 + bytes_read; + } +} + +#endif + +// This function is called to release any resources used by the BitStream +// and position the file pointer to the first byte past the read bits. + +static void bs_close_read3 (Bitstream3 *bs) +{ + if (bs->buf) { + free (bs->buf); + CLEAR (*bs); + } +} + +static uint32_t bs_unused_bytes (Bitstream3 *bs) +{ + if (bs->bc < 8) { + bs->bc += 8; + bs->ptr++; + } + + return bs->end - bs->ptr; +} + +static uchar *bs_unused_data (Bitstream3 *bs) +{ + if (bs->bc < 8) { + bs->bc += 8; + bs->ptr++; + } + + return bs->ptr; +} + +#ifdef UNPACK + +//////////////////////////////// local macros ///////////////////////////////// + +#define apply_weight_n(bits, weight, sample) ((weight * sample + (1 << (bits - 1))) >> bits) + +#define update_weight_n(bits, weight, source, result) \ + if (source && result) { \ + if ((source ^ result) >= 0) { if (weight++ == (1 << bits)) weight--; } \ + else if (weight-- == min_weight) weight++; \ + } + +#define apply_weight24(weight, sample) (((((sample & 0xffff) * weight) >> 7) + \ + (((sample & ~0xffff) >> 7) * weight) + 1) >> 1) + +#define update_weight2(weight, source, result) \ + if (source && result) { \ + if ((source ^ result) >= 0) { if (weight++ == 256) weight--; } \ + else if (weight-- == min_weight) weight++; \ + } + +//////////////////////////////// local tables /////////////////////////////// + +// These three tables specify the characteristics of the decorrelation filters. +// Each term represents one layer of the sequential filter, where positive +// values indicate the relative sample involved from the same channel (1=prev) +// while -1 and -2 indicate cross channel decorrelation (in stereo only). The +// "simple_terms" table is no longer used for writing, but is kept for older +// file decoding. + +static const char extreme_terms [] = { 1,1,1,2,4,-1,1,2,3,6,-2,8,5,7,4,1,2,3 }; +static const char default_terms [] = { 1,1,1,-1,2,1,-2 }; +static const char simple_terms [] = { 1,1,1,1 }; + +// This function initializes everything required to unpack WavPack +// bitstreams and must be called before any unpacking is performed. Note +// that the (WavpackHeader3 *) in the WavpackStream3 struct must be valid. + +static void init_words3 (WavpackStream3 *wps); + +static void unpack_init3 (WavpackStream3 *wps) +{ + int flags = wps->wphdr.flags; + struct decorr_pass *dpp; + int ti; + + CLEAR (wps->decorr_passes); + CLEAR (wps->dc); + + if (flags & EXTREME_DECORR) { + for (dpp = wps->decorr_passes, ti = 0; ti < sizeof (extreme_terms); ti++) + if (extreme_terms [sizeof (extreme_terms) - ti - 1] > 0 || (flags & CROSS_DECORR)) + dpp++->term = extreme_terms [sizeof (extreme_terms) - ti - 1]; + } + else if (flags & NEW_DECORR_FLAG) { + for (dpp = wps->decorr_passes, ti = 0; ti < sizeof (default_terms); ti++) + if (default_terms [sizeof (default_terms) - ti - 1] > 0 || (flags & CROSS_DECORR)) + dpp++->term = default_terms [sizeof (default_terms) - ti - 1]; + } + else + for (dpp = wps->decorr_passes, ti = 0; ti < sizeof (simple_terms); ti++) + dpp++->term = simple_terms [sizeof (simple_terms) - ti - 1]; + + wps->num_terms = dpp - wps->decorr_passes; + init_words3 (wps); +} + +#ifdef SEEKING + +#define SAVE(destin, item) { memcpy (destin, &item, sizeof (item)); destin = (char *) destin + sizeof (item); } +#define RESTORE(item, source) { memcpy (&item, source, sizeof (item)); source = (char *) source + sizeof (item); } + +// This function returns the size (in bytes) required to save the unpacking +// context. Note that the (WavpackHeader3 *) in the WavpackStream3 struct +// must be valid. + +static int unpack_size (WavpackStream3 *wps) +{ + int flags = wps->wphdr.flags, byte_sum = 0, tcount; + struct decorr_pass *dpp; + + byte_sum += sizeof (wps->wvbits); + + if (flags & WVC_FLAG) + byte_sum += sizeof (wps->wvcbits); + + if (wps->wphdr.version == 3) { + if (wps->wphdr.bits) + byte_sum += sizeof (wps->w4); + else + byte_sum += sizeof (wps->w1); + + byte_sum += sizeof (wps->w3) + sizeof (wps->dc.crc); + } + else + byte_sum += sizeof (wps->w2); + + if (wps->wphdr.bits) + byte_sum += sizeof (wps->dc.error); + else + byte_sum += sizeof (wps->dc.sum_level) + sizeof (wps->dc.left_level) + + sizeof (wps->dc.right_level) + sizeof (wps->dc.diff_level); + + if (flags & OVER_20) + byte_sum += sizeof (wps->dc.last_extra_bits) + sizeof (wps->dc.extra_bits_count); + + if (!(flags & EXTREME_DECORR)) { + byte_sum += sizeof (wps->dc.sample); + byte_sum += sizeof (wps->dc.weight); + } + + if (flags & (HIGH_FLAG | NEW_HIGH_FLAG)) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0) { + byte_sum += sizeof (dpp->samples_A [0]) * dpp->term; + byte_sum += sizeof (dpp->weight_A); + + if (!(flags & MONO_FLAG)) { + byte_sum += sizeof (dpp->samples_B [0]) * dpp->term; + byte_sum += sizeof (dpp->weight_B); + } + } + else { + byte_sum += sizeof (dpp->samples_A [0]) + sizeof (dpp->samples_B [0]); + byte_sum += sizeof (dpp->weight_A) + sizeof (dpp->weight_B); + } + + return byte_sum; +} + +// This function saves the unpacking context at the specified pointer and +// returns the updated pointer. The actual amount of data required can be +// determined beforehand by calling unpack_size() but must be allocated by +// the caller. + +static void *unpack_save (WavpackStream3 *wps, void *destin) +{ + int flags = wps->wphdr.flags, tcount; + struct decorr_pass *dpp; + + SAVE (destin, wps->wvbits); + + if (flags & WVC_FLAG) + SAVE (destin, wps->wvcbits); + + if (wps->wphdr.version == 3) { + if (wps->wphdr.bits) { + SAVE (destin, wps->w4); + } + else { + SAVE (destin, wps->w1); + } + + SAVE (destin, wps->w3); + SAVE (destin, wps->dc.crc); + } + else + SAVE (destin, wps->w2); + + if (wps->wphdr.bits) { + SAVE (destin, wps->dc.error); + } + else { + SAVE (destin, wps->dc.sum_level); + SAVE (destin, wps->dc.left_level); + SAVE (destin, wps->dc.right_level); + SAVE (destin, wps->dc.diff_level); + } + + if (flags & OVER_20) { + SAVE (destin, wps->dc.last_extra_bits); + SAVE (destin, wps->dc.extra_bits_count); + } + + if (!(flags & EXTREME_DECORR)) { + SAVE (destin, wps->dc.sample); + SAVE (destin, wps->dc.weight); + } + + if (flags & (HIGH_FLAG | NEW_HIGH_FLAG)) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0) { + int count = dpp->term; + int index = wps->dc.m; + + SAVE (destin, dpp->weight_A); + + while (count--) { + SAVE (destin, dpp->samples_A [index]); + index = (index + 1) & (MAX_TERM - 1); + } + + if (!(flags & MONO_FLAG)) { + count = dpp->term; + index = wps->dc.m; + + SAVE (destin, dpp->weight_B); + + while (count--) { + SAVE (destin, dpp->samples_B [index]); + index = (index + 1) & (MAX_TERM - 1); + } + } + } + else { + SAVE (destin, dpp->weight_A); + SAVE (destin, dpp->weight_B); + SAVE (destin, dpp->samples_A [0]); + SAVE (destin, dpp->samples_B [0]); + } + + return destin; +} + +// This function restores the unpacking context from the specified pointer +// and returns the updated pointer. After this call, unpack_samples() will +// continue where it left off immediately before unpack_save() was called. +// If the WavPack files and bitstreams might have been closed and reopened, +// then the "keep_resources" flag should be set to avoid using the "old" +// resources that were originally saved (and are probably now invalid). + +static void *unpack_restore (WavpackStream3 *wps, void *source, int keep_resources) +{ + int flags = wps->wphdr.flags, tcount; + struct decorr_pass *dpp; + FILE *temp_file; + uchar *temp_buf; + + unpack_init3 (wps); + temp_file = wps->wvbits.id; + temp_buf = wps->wvbits.buf; + RESTORE (wps->wvbits, source); + + if (keep_resources) { + wps->wvbits.id = temp_file; + wps->wvbits.ptr += temp_buf - wps->wvbits.buf; + wps->wvbits.end += temp_buf - wps->wvbits.buf; + wps->wvbits.buf = temp_buf; + } + + bs_restore3 (&wps->wvbits); + + if (flags & WVC_FLAG) { + temp_file = wps->wvcbits.id; + temp_buf = wps->wvcbits.buf; + RESTORE (wps->wvcbits, source); + + if (keep_resources) { + wps->wvcbits.id = temp_file; + wps->wvcbits.ptr += temp_buf - wps->wvcbits.buf; + wps->wvcbits.end += temp_buf - wps->wvcbits.buf; + wps->wvcbits.buf = temp_buf; + } + + bs_restore3 (&wps->wvcbits); + } + + if (wps->wphdr.version == 3) { + if (wps->wphdr.bits) { + RESTORE (wps->w4, source); + } + else { + RESTORE (wps->w1, source); + } + + RESTORE (wps->w3, source); + RESTORE (wps->dc.crc, source); + } + else + RESTORE (wps->w2, source); + + if (wps->wphdr.bits) { + RESTORE (wps->dc.error, source); + } + else { + RESTORE (wps->dc.sum_level, source); + RESTORE (wps->dc.left_level, source); + RESTORE (wps->dc.right_level, source); + RESTORE (wps->dc.diff_level, source); + } + + if (flags & OVER_20) { + RESTORE (wps->dc.last_extra_bits, source); + RESTORE (wps->dc.extra_bits_count, source); + } + + if (!(flags & EXTREME_DECORR)) { + RESTORE (wps->dc.sample, source); + RESTORE (wps->dc.weight, source); + } + + if (flags & (HIGH_FLAG | NEW_HIGH_FLAG)) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0) { + int count = dpp->term; + int index = wps->dc.m; + + RESTORE (dpp->weight_A, source); + + while (count--) { + RESTORE (dpp->samples_A [index], source); + index = (index + 1) & (MAX_TERM - 1); + } + + if (!(flags & MONO_FLAG)) { + count = dpp->term; + index = wps->dc.m; + + RESTORE (dpp->weight_B, source); + + while (count--) { + RESTORE (dpp->samples_B [index], source); + index = (index + 1) & (MAX_TERM - 1); + } + } + } + else { + RESTORE (dpp->weight_A, source); + RESTORE (dpp->weight_B, source); + RESTORE (dpp->samples_A [0], source); + RESTORE (dpp->samples_B [0], source); + } + + return source; +} + +// This is an extension for WavpackSeekSample (). Note that because WavPack +// files created prior to version 4.0 are not inherently seekable, this +// function could take a long time if a forward seek is requested to an +// area that has not been played (or seeked through) yet. + + +int seek_sample3 (WavpackContext *wpc, uint32_t desired_index) +{ + int points_index = desired_index / ((wpc->total_samples >> 8) + 1); + WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3; + + if (desired_index >= wpc->total_samples) + return FALSE; + + while (points_index) + if (wps->index_points [points_index].saved && + wps->index_points [points_index].sample_index <= desired_index) + break; + else + points_index--; + + if (wps->index_points [points_index].saved) + if (wps->index_points [points_index].sample_index > wps->sample_index || + wps->sample_index > desired_index) { + wps->sample_index = wps->index_points [points_index].sample_index; + unpack_restore (wps, wps->unpack_data + points_index * wps->unpack_size, TRUE); + } + + if (desired_index > wps->sample_index) { + int32_t *buffer = (int32_t *) malloc (1024 * (wps->wphdr.flags & MONO_FLAG ? 4 : 8)); + uint32_t samples_to_skip = desired_index - wps->sample_index; + + while (1) { + if (samples_to_skip > 1024) { + if (unpack_samples3 (wpc, buffer, 1024) == 1024) + samples_to_skip -= 1024; + else + break; + } + else { + samples_to_skip -= unpack_samples3 (wpc, buffer, samples_to_skip); + break; + } + } + + free (buffer); + + if (samples_to_skip) + return FALSE; + } + + return TRUE; +} + + +#endif + +// This monster actually unpacks the WavPack bitstream(s) into the specified +// buffer as longs, and serves as an extension to WavpackUnpackSamples(). +// Note that WavPack files created prior to version 4.0 could only contain 16 +// or 24 bit values, and these values are right-justified in the 32-bit values. +// So, if the original file contained 16-bit values, then the range of the +// returned longs would be +/- 32K. 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. + +static int32_t FASTCALL get_word1 (WavpackStream3 *wps, int chan); +static int32_t FASTCALL get_old_word1 (WavpackStream3 *wps, int chan); +static int32_t FASTCALL get_word2 (WavpackStream3 *wps, int chan); +static int32_t FASTCALL get_word3 (WavpackStream3 *wps, int chan); +static int32_t FASTCALL get_word4 (WavpackStream3 *wps, int chan, int32_t *correction); + +int32_t unpack_samples3 (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count) +{ + WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3; + int shift = wps->wphdr.shift, flags = wps->wphdr.flags, min_weight = 0, m = wps->dc.m, tcount; +#ifdef SEEKING + int points_index = wps->sample_index / ((wpc->total_samples >> 8) + 1); +#endif + int32_t min_value, max_value, min_shifted, max_shifted; + int32_t correction [2], crc = wps->dc.crc; + struct decorr_pass *dpp; + int32_t read_word, *bptr; + int32_t sample [2] [2]; + int weight [2] [1]; + uint i; + + if (wps->sample_index + sample_count > wpc->total_samples) + sample_count = wpc->total_samples - wps->sample_index; + + if (!sample_count) + return 0; + + if (!wps->sample_index) { + unpack_init3 (wps); + + bs_open_read3 (&wps->wvbits, wpc->reader, wpc->wv_in); + + if (wpc->wvc_flag) + bs_open_read3 (&wps->wvcbits, wpc->reader, wpc->wvc_in); + } + +#ifdef SEEKING + if (!wps->index_points [points_index].saved) { + + if (!wps->unpack_data) + wps->unpack_data = (uchar *) malloc (256 * (wps->unpack_size = unpack_size (wps))); + + wps->index_points [points_index].sample_index = wps->sample_index; + unpack_save (wps, wps->unpack_data + points_index * wps->unpack_size); + wps->index_points [points_index].saved = TRUE; + } +#endif + + memcpy (sample, wps->dc.sample, sizeof (sample)); + memcpy (weight, wps->dc.weight, sizeof (weight)); + + if (wps->wphdr.bits) { + if (flags & (NEW_DECORR_FLAG | EXTREME_DECORR)) + min_weight = -256; + } + else + if (flags & NEW_DECORR_FLAG) + min_weight = (flags & EXTREME_DECORR) ? -512 : -256; + + if (flags & BYTES_3) { + min_shifted = (min_value = -8388608 >> shift) << shift; + max_shifted = (max_value = 8388607 >> shift) << shift; + } + else { + min_shifted = (min_value = -32768 >> shift) << shift; + max_shifted = (max_value = 32767 >> shift) << shift; + } + + ///////////////// handle version 3 lossless mono data ///////////////////// + + if (wps->wphdr.version == 3 && !wps->wphdr.bits && (flags & MONO_FLAG)) { + if (flags & FAST_FLAG) { + if (flags & OVER_20) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t temp; + + if ((read_word = get_word3 (wps, 0)) == WORD_EOF) + break; + + sample [0] [0] += sample [0] [1] += read_word; + getbits (&temp, 4, &wps->wvbits); + crc = crc * 3 + (temp = (temp & 0xf) + (sample [0] [0] << 4)); + *bptr++ = temp; + } + else + for (bptr = buffer, i = 0; i < sample_count; ++i) { + if ((read_word = get_word3 (wps, 0)) == WORD_EOF) + break; + + crc = crc * 3 + (sample [0] [0] += sample [0] [1] += read_word); + *bptr++ = sample [0] [0] << shift; + } + } + else if (flags & HIGH_FLAG) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t temp; + + if (flags & NEW_HIGH_FLAG) { + if ((read_word = get_word1 (wps, 0)) == WORD_EOF) + break; + } + else { + if ((read_word = get_old_word1 (wps, 0)) == WORD_EOF) + break; + } + + if (flags & EXTREME_DECORR) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam = dpp->samples_A [m]; + + temp = apply_weight_n (9, dpp->weight_A, sam) + read_word; + update_weight_n (9, dpp->weight_A, sam, read_word); + dpp->samples_A [(m + dpp->term) & (MAX_TERM - 1)] = read_word = temp; + } + else + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam = dpp->samples_A [m]; + + temp = apply_weight_n (8, dpp->weight_A, sam) + read_word; + update_weight_n (8, dpp->weight_A, sam, read_word); + dpp->samples_A [(m + dpp->term) & (MAX_TERM - 1)] = read_word = temp; + } + + m = (m + 1) & (MAX_TERM - 1); + + if (flags & OVER_20) { + if (wps->dc.extra_bits_count < 8 || !getbit (&wps->wvbits)) { + getbits (&temp, 4, &wps->wvbits); + + if ((temp &= 0xf) != wps->dc.last_extra_bits) { + wps->dc.last_extra_bits = temp; + wps->dc.extra_bits_count = 0; + } + else + ++wps->dc.extra_bits_count; + } + + crc = crc * 3 + (temp = wps->dc.last_extra_bits + (read_word << 4)); + *bptr++ = temp; + } + else { + crc = crc * 3 + read_word; + *bptr++ = read_word << shift; + } + } + else + for (bptr = buffer, i = 0; i < sample_count; ++i) { + + int32_t temp; + + if ((read_word = get_word3 (wps, 0)) == WORD_EOF) + break; + + temp = sample [0] [0] + ((sample [0] [1] * weight [0] [0] + 128) >> 8) + read_word; + + if ((sample [0] [1] >= 0) == (read_word > 0)) { + if (weight [0] [0]++ == 256) + weight [0] [0]--; + } + else if (weight [0] [0]-- == 0) + weight [0] [0]++; + + sample [0] [0] += (sample [0] [1] = temp - sample [0] [0]); + + if (flags & OVER_20) { + if (wps->dc.extra_bits_count < 8 || !getbit (&wps->wvbits)) { + getbits (&temp, 4, &wps->wvbits); + + if ((temp &= 0xf) != wps->dc.last_extra_bits) { + wps->dc.last_extra_bits = temp; + wps->dc.extra_bits_count = 0; + } + else + ++wps->dc.extra_bits_count; + } + + crc = crc * 3 + (*bptr++ = temp = wps->dc.last_extra_bits + (sample [0] [0] << 4)); + } + else { + crc = crc * 3 + sample [0] [0]; + *bptr++ = sample [0] [0] << shift; + } + } + } + + //////////////// handle version 3 lossless stereo data //////////////////// + + else if (wps->wphdr.version == 3 && !wps->wphdr.bits && !(flags & MONO_FLAG)) { + int32_t left_level = wps->dc.left_level, right_level = wps->dc.right_level; + int32_t sum_level = wps->dc.sum_level, diff_level = wps->dc.diff_level; + + if (flags & FAST_FLAG) { + if (flags & OVER_20) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t sum, diff, temp; + + read_word = get_word3 (wps, 0); + + if (read_word == WORD_EOF) + break; + + sum = (read_word << 1) | ((diff = get_word3 (wps, 1)) & 1); + sample [0] [0] += sample [0] [1] += ((sum + diff) >> 1); + sample [1] [0] += sample [1] [1] += ((sum - diff) >> 1); + getbits (&temp, 8, &wps->wvbits); + crc = crc * 3 + (*bptr++ = (sample [0] [0] << 4) + ((temp >> 4) & 0xf)); + crc = crc * 3 + (*bptr++ = (sample [1] [0] << 4) + (temp & 0xf)); + } + else + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t sum, diff; + + read_word = get_word3 (wps, 0); + + if (read_word == WORD_EOF) + break; + + sum = (read_word << 1) | ((diff = get_word3 (wps, 1)) & 1); + sample [0] [1] += ((sum + diff) >> 1); + sample [1] [1] += ((sum - diff) >> 1); + crc = crc * 3 + (sample [0] [0] += sample [0] [1]); + crc = crc * 3 + (sample [1] [0] += sample [1] [1]); + *bptr++ = sample [0] [0] << shift; + *bptr++ = sample [1] [0] << shift; + } + } + else if (flags & HIGH_FLAG) { + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t sum, left, right, diff, left2, right2, extra_bits, next_word; + + if (flags & CROSS_DECORR) { + left = get_word1 (wps, 0); + + if (left == WORD_EOF) + break; + + right = get_word1 (wps, 1); + } + else { + if (flags & NEW_HIGH_FLAG) { + read_word = get_word1 (wps, 0); + + if (read_word == WORD_EOF) + break; + + next_word = get_word1 (wps, 1); + + if (right_level > left_level) { + if (left_level + right_level < sum_level + diff_level && right_level < diff_level) { + sum = (right = read_word) + (left = next_word); + diff = left - right; + } + else { + diff = read_word; + + if (sum_level < left_level) { + sum = (next_word << 1) | (diff & 1); + left = (sum + diff) >> 1; + right = (sum - diff) >> 1; + } + else + sum = left + (right = (left = next_word) - diff); + } + } + else { + if (left_level + right_level < sum_level + diff_level && left_level < diff_level) { + sum = (left = read_word) + (right = next_word); + diff = left - right; + } + else { + diff = read_word; + + if (sum_level < right_level) { + sum = (next_word << 1) | (diff & 1); + left = (sum + diff) >> 1; + right = (sum - diff) >> 1; + } + else + sum = (left = diff + (right = next_word)) + right; + } + } + } + else { + read_word = get_old_word1 (wps, 0); + + if (read_word == WORD_EOF) + break; + + next_word = get_old_word1 (wps, 1); + + if (sum_level <= right_level && sum_level <= left_level) { + sum = (next_word << 1) | (read_word & 1); + left = (sum + read_word) >> 1; + right = (sum - read_word) >> 1; + } + else if (left_level <= right_level) + sum = left + (right = (left = next_word) - read_word); + else + sum = right + (left = read_word + (right = next_word)); + + diff = left - right; + } + + sum_level = sum_level - (sum_level >> 8) + labs (sum >> 1); + left_level = left_level - (left_level >> 8) + labs (left); + right_level = right_level - (right_level >> 8) + labs (right); + diff_level = diff_level - (diff_level >> 8) + labs (diff); + + if (flags & JOINT_STEREO) { + left = diff; + right = sum >> 1; + } + } + + if (flags & EXTREME_DECORR) { + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0) { + int32_t sam_A = dpp->samples_A [m], sam_B = dpp->samples_B [m]; + int k = (m + dpp->term) & (MAX_TERM - 1); + + left2 = apply_weight_n (9, dpp->weight_A, sam_A) + left; + right2 = apply_weight_n (9, dpp->weight_B, sam_B) + right; + + update_weight_n (9, dpp->weight_A, sam_A, left); + update_weight_n (9, dpp->weight_B, sam_B, right); + + dpp->samples_A [k] = left = left2; + dpp->samples_B [k] = right = right2; + } + else if (dpp->term == -1) { + left2 = left + apply_weight_n (9, dpp->weight_A, dpp->samples_A [0]); + update_weight_n (9, dpp->weight_A, dpp->samples_A [0], left); + left = left2; + right2 = right + apply_weight_n (9, dpp->weight_B, left); + update_weight_n (9, dpp->weight_B, left, right); + dpp->samples_A [0] = right = right2; + } + else { + right2 = right + apply_weight_n (9, dpp->weight_A, dpp->samples_A [0]); + update_weight_n (9, dpp->weight_A, dpp->samples_A [0], right); + right = right2; + left2 = left + apply_weight_n (9, dpp->weight_B, right); + update_weight_n (9, dpp->weight_B, right, left); + dpp->samples_A [0] = left = left2; + } + } + else { + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0) { + int32_t sam_A = dpp->samples_A [m], sam_B = dpp->samples_B [m]; + int k = (m + dpp->term) & (MAX_TERM - 1); + + left2 = apply_weight_n (8, dpp->weight_A, sam_A) + left; + right2 = apply_weight_n (8, dpp->weight_B, sam_B) + right; + + update_weight_n (8, dpp->weight_A, sam_A, left); + update_weight_n (8, dpp->weight_B, sam_B, right); + + dpp->samples_A [k] = left = left2; + dpp->samples_B [k] = right = right2; + } + else if (dpp->term == -1) { + left2 = left + apply_weight_n (8, dpp->weight_A, dpp->samples_A [0]); + update_weight_n (8, dpp->weight_A, dpp->samples_A [0], left); + left = left2; + right2 = right + apply_weight_n (8, dpp->weight_B, left); + update_weight_n (8, dpp->weight_B, left, right); + dpp->samples_A [0] = right = right2; + } + else { + right2 = right + apply_weight_n (8, dpp->weight_A, dpp->samples_A [0]); + update_weight_n (8, dpp->weight_A, dpp->samples_A [0], right); + right = right2; + left2 = left + apply_weight_n (8, dpp->weight_B, right); + update_weight_n (8, dpp->weight_B, right, left); + dpp->samples_A [0] = left = left2; + } + } + + m = (m + 1) & (MAX_TERM - 1); + + if (flags & JOINT_STEREO) { + sum = (right << 1) | ((diff = left) & 1); + right = (sum - diff) >> 1; + left = (sum + diff) >> 1; + } + + if (flags & OVER_20) { + if (wps->dc.extra_bits_count < 8 || !getbit (&wps->wvbits)) { + getbits (&extra_bits, 8, &wps->wvbits); + + if ((extra_bits &= 0xff) != wps->dc.last_extra_bits) { + wps->dc.last_extra_bits = extra_bits; + wps->dc.extra_bits_count = 0; + } + else + ++wps->dc.extra_bits_count; + } + + crc = crc * 3 + (*bptr++ = left = (left << 4) + (wps->dc.last_extra_bits >> 4)); + crc = crc * 3 + (*bptr++ = right = (right << 4) + (wps->dc.last_extra_bits & 0xf)); + } + else { + crc = crc * 9 + left * 3 + right; + *bptr++ = left << shift; + *bptr++ = right << shift; + } + } + } + else + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t sum, left, right, left2, right2, extra_bits; + + read_word = get_word3 (wps, 0); + + if (read_word == WORD_EOF) + break; + + if (sum_level <= right_level && sum_level <= left_level) { + sum = (get_word3 (wps, 1) << 1) | (read_word & 1); + left = (sum + read_word) >> 1; + right = (sum - read_word) >> 1; + } + else if (left_level <= right_level) + sum = left + (right = (left = get_word3 (wps, 1)) - read_word); + else + sum = right + (left = read_word + (right = get_word3 (wps, 1))); + + sum_level = sum_level - (sum_level >> 8) + labs (sum >> 1); + left_level = left_level - (left_level >> 8) + labs (left); + right_level = right_level - (right_level >> 8) + labs (right); + + left2 = sample [0] [0] + ((sample [0] [1] * weight [0] [0] + 128) >> 8) + left; + right2 = sample [1] [0] + ((sample [1] [1] * weight [1] [0] + 128) >> 8) + right; + + if ((sample [0] [1] >= 0) == (left > 0)) { + if (weight [0] [0]++ == 256) + weight [0] [0]--; + } + else if (weight [0] [0]-- == 0) + weight [0] [0]++; + + if ((sample [1] [1] >= 0) == (right > 0)) { + if (weight [1] [0]++ == 256) + weight [1] [0]--; + } + else if (weight [1] [0]-- == 0) + weight [1] [0]++; + + sample [0] [0] += (sample [0] [1] = left2 - sample [0] [0]); + sample [1] [0] += (sample [1] [1] = right2 - sample [1] [0]); + + if (flags & OVER_20) { + if (wps->dc.extra_bits_count < 8 || !getbit (&wps->wvbits)) { + getbits (&extra_bits, 8, &wps->wvbits); + + if ((extra_bits &= 0xff) != wps->dc.last_extra_bits) { + wps->dc.last_extra_bits = extra_bits; + wps->dc.extra_bits_count = 0; + } + else + ++wps->dc.extra_bits_count; + } + + crc = crc * 3 + (*bptr++ = left2 = (sample [0] [0] << 4) + (wps->dc.last_extra_bits >> 4)); + crc = crc * 3 + (*bptr++ = right2 = (sample [1] [0] << 4) + (wps->dc.last_extra_bits & 0xf)); + } + else { + crc = crc * 9 + sample [0] [0] * 3 + sample [1] [0]; + *bptr++ = sample [0] [0] << shift; + *bptr++ = sample [1] [0] << shift; + } + } + + wps->dc.left_level = left_level; + wps->dc.right_level = right_level; + wps->dc.sum_level = sum_level; + wps->dc.diff_level = diff_level; + } + + //////////////// handle version 3 lossy/hybrid mono data ////////////////// + + else if (wps->wphdr.version == 3 && wps->wphdr.bits && (flags & MONO_FLAG)) { + if (flags & FAST_FLAG) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + + if ((read_word = get_word3 (wps, 0)) == WORD_EOF) + break; + + crc = crc * 3 + (sample [0] [0] += sample [0] [1] += read_word); + + if (sample [0] [0] < min_value) + *bptr++ = min_shifted; + else if (sample [0] [0] > max_value) + *bptr++ = max_shifted; + else + *bptr++ = sample [0] [0] << shift; + } + else if (flags & (HIGH_FLAG | NEW_HIGH_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t temp; + + read_word = (flags & NEW_HIGH_FLAG) ? + get_word4 (wps, 0, correction) : get_word3 (wps, 0); + + if (read_word == WORD_EOF) + break; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam = dpp->samples_A [m]; + + temp = apply_weight24 (dpp->weight_A, sam) + read_word; + update_weight2 (dpp->weight_A, sam, read_word); + dpp->samples_A [(m + dpp->term) & (MAX_TERM - 1)] = read_word = temp; + } + + m = (m + 1) & (MAX_TERM - 1); + + if (flags & WVC_FLAG) { + if (flags & LOSSY_SHAPE) { + crc = crc * 3 + (read_word += correction [0] + wps->dc.error [0]); + wps->dc.error [0] = -correction [0]; + } + else + crc = crc * 3 + (read_word += correction [0]); + + *bptr++ = read_word << shift; + } + else { + crc = crc * 3 + read_word; + + if (read_word < min_value) + *bptr++ = min_shifted; + else if (read_word > max_value) + *bptr++ = max_shifted; + else + *bptr++ = read_word << shift; + } + } + else + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t new_sample; + + if ((read_word = get_word3 (wps, 0)) == WORD_EOF) + break; + + new_sample = sample [0] [0] + ((sample [0] [1] * weight [0] [0] + 128) >> 8) + read_word; + + if ((sample [0] [1] >= 0) == (read_word > 0)) { + if (weight [0] [0]++ == 256) + weight [0] [0]--; + } + else if (weight [0] [0]-- == 0) + weight [0] [0]++; + + sample [0] [1] = new_sample - sample [0] [0]; + crc = crc * 3 + (sample [0] [0] = new_sample); + + if (sample [0] [0] < min_value) + *bptr++ = min_shifted; + else if (sample [0] [0] > max_value) + *bptr++ = max_shifted; + else + *bptr++ = sample [0] [0] << shift; + } + } + + //////////////// handle version 3 lossy/hybrid stereo data //////////////// + + else if (wps->wphdr.version == 3 && wps->wphdr.bits && !(flags & MONO_FLAG)) { + if (flags & FAST_FLAG) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + + if ((read_word = get_word3 (wps, 0)) == WORD_EOF) + break; + + crc = crc * 3 + (sample [0] [0] += sample [0] [1] += read_word); + + if (sample [0] [0] < min_value) + *bptr++ = min_shifted; + else if (sample [0] [0] > max_value) + *bptr++ = max_shifted; + else + *bptr++ = sample [0] [0] << shift; + + crc = crc * 3 + (sample [1] [0] += sample [1] [1] += get_word3 (wps, 1)); + + if (sample [1] [0] < min_value) + *bptr++ = min_shifted; + else if (sample [1] [0] > max_value) + *bptr++ = max_shifted; + else + *bptr++ = sample [1] [0] << shift; + } + else if (flags & (HIGH_FLAG | NEW_HIGH_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t left, right, left2, right2, sum, diff; + + if (flags & NEW_HIGH_FLAG) { + left = get_word4 (wps, 0, correction); + right = get_word4 (wps, 1, correction + 1); + } + else { + left = get_word3 (wps, 0); + right = get_word3 (wps, 1); + } + + if (left == WORD_EOF) + break; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam_A = dpp->samples_A [m], sam_B = dpp->samples_B [m]; + int k = (m + dpp->term) & (MAX_TERM - 1); + + left2 = apply_weight24 (dpp->weight_A, sam_A) + left; + update_weight2 (dpp->weight_A, sam_A, left); + dpp->samples_A [k] = left = left2; + + right2 = apply_weight24 (dpp->weight_B, sam_B) + right; + update_weight2 (dpp->weight_B, sam_B, right); + dpp->samples_B [k] = right = right2; + } + + m = (m + 1) & (MAX_TERM - 1); + + if (flags & WVC_FLAG) { + if (flags & LOSSY_SHAPE) { + left += correction [0] + wps->dc.error [0]; + right += correction [1] + wps->dc.error [1]; + wps->dc.error [0] = -correction [0]; + wps->dc.error [1] = -correction [1]; + } + else { + left += correction [0]; + right += correction [1]; + } + } + + if (flags & JOINT_STEREO) { + right = ((sum = (right << 1) | (left & 1)) - (diff = left)) >> 1; + left = (sum + diff) >> 1; + } + + crc = crc * 9 + left * 3 + right; + + if (flags & WVC_FLAG) { + *bptr++ = left << shift; + *bptr++ = right << shift; + } + else { + if (left < min_value) + *bptr++ = min_shifted; + else if (left > max_value) + *bptr++ = max_shifted; + else + *bptr++ = left << shift; + + if (right < min_value) + *bptr++ = min_shifted; + else if (right > max_value) + *bptr++ = max_shifted; + else + *bptr++ = right << shift; + } + } + else + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t new_sample; + + if ((read_word = get_word3 (wps, 0)) == WORD_EOF) + break; + + new_sample = sample [0] [0] + ((sample [0] [1] * weight [0] [0] + 128) >> 8) + read_word; + + if ((sample [0] [1] >= 0) == (read_word > 0)) { + if (weight [0] [0]++ == 256) + weight [0] [0]--; + } + else if (weight [0] [0]-- == 0) + weight [0] [0]++; + + sample [0] [1] = new_sample - sample [0] [0]; + crc = crc * 3 + (sample [0] [0] = new_sample); + + read_word = get_word3 (wps, 1); + new_sample = sample [1] [0] + ((sample [1] [1] * weight [1] [0] + 128) >> 8) + read_word; + + if ((sample [1] [1] >= 0) == (read_word > 0)) { + if (weight [1] [0]++ == 256) + weight [1] [0]--; + } + else if (weight [1] [0]-- == 0) + weight [1] [0]++; + + sample [1] [1] = new_sample - sample [1] [0]; + crc = crc * 3 + (sample [1] [0] = new_sample); + + if (sample [0] [0] < min_value) + *bptr++ = min_shifted; + else if (sample [0] [0] > max_value) + *bptr++ = max_shifted; + else + *bptr++ = sample [0] [0] << shift; + + if (sample [1] [0] < min_value) + *bptr++ = min_shifted; + else if (sample [1] [0] > max_value) + *bptr++ = max_shifted; + else + *bptr++ = sample [1] [0] << shift; + } + } + + //////////////////// finally, handle version 2 data /////////////////////// + + else if (wps->wphdr.version == 2 && (flags & MONO_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + if ((read_word = get_word2 (wps, 0)) == WORD_EOF) + break; + + sample [0] [0] += sample [0] [1] += read_word; + + if (wps->wphdr.bits) { + if (sample [0] [0] < min_value) + sample [0] [0] = min_value; + else if (sample [0] [0] > max_value) + sample [0] [0] = max_value; + } + + *bptr++ = sample [0] [0] << shift; + } + else if (wps->wphdr.version < 3 && !(flags & MONO_FLAG)) + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t sum, diff; + + read_word = get_word2 (wps, 0); + + if (read_word == WORD_EOF) + break; + + sum = (read_word << 1) | ((diff = get_word2 (wps, 1)) & 1); + sample [0] [0] += sample [0] [1] += ((sum + diff) >> 1); + sample [1] [0] += sample [1] [1] += ((sum - diff) >> 1); + + if (wps->wphdr.bits) { + if (sample [0] [0] < min_value) + sample [0] [0] = min_value; + else if (sample [0] [0] > max_value) + sample [0] [0] = max_value; + + if (sample [1] [0] < min_value) + sample [1] [0] = min_value; + else if (sample [1] [0] > max_value) + sample [1] [0] = max_value; + } + + *bptr++ = sample [0] [0] << shift; + *bptr++ = sample [1] [0] << shift; + } + + if (i && (wps->sample_index += i) == wpc->total_samples) { + + if (wps->wphdr.version == 3 && crc != (wpc->wvc_flag ? wps->wphdr.crc2 : wps->wphdr.crc)) + wpc->crc_errors++; + + if (wpc->open_flags & OPEN_WRAPPER) { + uchar *temp = malloc (1024); + uint32_t bcount; + + if (bs_unused_bytes (&wps->wvbits)) { + wpc->wrapper_data = realloc (wpc->wrapper_data, wpc->wrapper_bytes + bs_unused_bytes (&wps->wvbits)); + memcpy (wpc->wrapper_data + wpc->wrapper_bytes, bs_unused_data (&wps->wvbits), bs_unused_bytes (&wps->wvbits)); + wpc->wrapper_bytes += bs_unused_bytes (&wps->wvbits); + } + + while (1) { + bcount = wpc->reader->read_bytes (wpc->wv_in, temp, sizeof (temp)); + + if (!bcount) + break; + + wpc->wrapper_data = realloc (wpc->wrapper_data, wpc->wrapper_bytes + bcount); + memcpy (wpc->wrapper_data + wpc->wrapper_bytes, temp, bcount); + wpc->wrapper_bytes += bcount; + } + + free (temp); + + if (wpc->wrapper_bytes > 16) { + int c; + + for (c = 0; c < 16 && wpc->wrapper_data [c] == 0xff; ++c); + + if (c == 16) { + memcpy (wpc->wrapper_data, wpc->wrapper_data + 16, wpc->wrapper_bytes - 16); + wpc->wrapper_bytes -= 16; + } + else { + free (wpc->wrapper_data); + wpc->wrapper_data = NULL; + wpc->wrapper_bytes = 0; + } + } + } + } + + memcpy (wps->dc.sample, sample, sizeof (sample)); + memcpy (wps->dc.weight, weight, sizeof (weight)); + wps->dc.crc = crc; + wps->dc.m = m; + + return i; +} + +///////////////////////////// local table storage //////////////////////////// + +extern const uint32_t bitset []; +extern const uint32_t bitmask []; +extern const char nbits_table []; + +// This function initializes everything required to receive words with this +// module and must be called BEFORE any other function in this module. + +static void init_words3 (WavpackStream3 *wps) +{ + CLEAR (wps->w1); + CLEAR (wps->w2); + CLEAR (wps->w3); + CLEAR (wps->w4); + + if (wps->wphdr.flags & MONO_FLAG) + wps->w4.bitrate = wps->wphdr.bits - 768; + else + wps->w4.bitrate = (wps->wphdr.bits / 2) - 768; +} + +// This macro counts the number of bits that are required to specify the +// unsigned 32-bit value, counting from the LSB to the most significant bit +// that is set. Return range is 0 - 32. + +#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) \ + ) \ +) + +static int32_t FASTCALL get_word1 (WavpackStream3 *wps, int chan) +{ + uint32_t tmp1, tmp2, avalue; + uint ones_count; + int k; + + if ((wps->wphdr.flags & EXTREME_DECORR) && !(wps->wphdr.flags & OVER_20)) { + if (wps->w1.zeros_acc) { + if (--wps->w1.zeros_acc) + return 0; + } + else if (wps->w1.ave_level [0] [0] < 0x20 && wps->w1.ave_level [0] [1] < 0x20) { + int32_t mask; + int cbits; + + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + wps->w1.zeros_acc = cbits; + else { + for (mask = 1, wps->w1.zeros_acc = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + wps->w1.zeros_acc |= mask; + + wps->w1.zeros_acc |= mask; + } + + if (wps->w1.zeros_acc) + return 0; + } + } + + // count consecutive ones in bitstream, > 25 indicates error (or EOF) + + for (ones_count = 0; ones_count < 25 && getbit (&wps->wvbits); ++ones_count); + + if (ones_count == 25) + return WORD_EOF; + + k = (wps->w1.ave_level [0] [chan] + (wps->w1.ave_level [0] [chan] >> 3) + 0x40) >> 7; + k = count_bits (k); + + if (ones_count == 0) { + getbits (&avalue, k, &wps->wvbits); + avalue &= bitmask [k]; + } + else { + tmp1 = bitset [k]; + k = (wps->w1.ave_level [1] [chan] + (wps->w1.ave_level [1] [chan] >> 4) + 0x20) >> 6; + k = count_bits (k); + + if (ones_count == 1) { + getbits (&avalue, k, &wps->wvbits); + avalue &= bitmask [k]; + } + else { + tmp2 = bitset [k]; + + // If the ones count is exactly 24, then next 24 bits are literal + + if (ones_count == 24) { + getbits (&avalue, 24, &wps->wvbits); + avalue &= 0xffffff; + } + else { + k = (wps->w1.ave_level [2] [chan] + 0x10) >> 5; + k = count_bits (k); + getbits (&avalue, k, &wps->wvbits); + avalue = (avalue & bitmask [k]) + (bitset [k] * (ones_count - 2)); + } + + wps->w1.ave_level [2] [chan] -= ((wps->w1.ave_level [2] [chan] + 0x8) >> 4); + wps->w1.ave_level [2] [chan] += avalue; + avalue += tmp2; + } + + wps->w1.ave_level [1] [chan] -= ((wps->w1.ave_level [1] [chan] + 0x10) >> 5); + wps->w1.ave_level [1] [chan] += avalue; + avalue += tmp1; + } + + wps->w1.ave_level [0] [chan] -= ((wps->w1.ave_level [0] [chan] + 0x20) >> 6); + wps->w1.ave_level [0] [chan] += avalue; + + return (avalue && getbit (&wps->wvbits)) ? -(int32_t)avalue : avalue; +} + +#define NUM_SAMPLES 128 + +static int32_t FASTCALL get_old_word1 (WavpackStream3 *wps, int chan) +{ + uint32_t avalue; + uint bc; + int k; + + if (!wps->w1.index [chan]) { + + int guess_k = (wps->w1.ave_k [chan] + 128) >> 8, ones; + + for (ones = 0; ones < 72 && getbit (&wps->wvbits); ++ones); + + if (ones == 72) + return WORD_EOF; + + if (ones % 3 == 1) + wps->w1.k_value [chan] = guess_k - (ones / 3) - 1; + else + wps->w1.k_value [chan] = guess_k + ones - ((ones + 1) / 3); + + wps->w1.ave_k [chan] -= (wps->w1.ave_k [chan] + 0x10) >> 5; + wps->w1.ave_k [chan] += wps->w1.k_value [chan] << 3; + } + + if (++wps->w1.index [chan] == NUM_SAMPLES) + wps->w1.index [chan] = 0; + + k = wps->w1.k_value [chan]; + getbits (&avalue, k, &wps->wvbits); + + for (bc = 0; bc < 32 && getbit (&wps->wvbits); ++bc); + + if (bc == 32) + return WORD_EOF; + + avalue = (avalue & bitmask [k]) + bitset [k] * bc; + return (avalue && getbit (&wps->wvbits)) ? -(int32_t)avalue : avalue; +} + +static int32_t FASTCALL get_word2 (WavpackStream3 *wps, int chan) +{ + int cbits, delta_dbits, dbits; + int32_t value, mask = 1; + + cbits = 0; + + while (getbit (&wps->wvbits)) + if ((cbits += 2) == 50) + return WORD_EOF; + + if (getbit (&wps->wvbits)) + cbits++; + + if (cbits == 0) + delta_dbits = 0; + else if (cbits & 1) { + delta_dbits = (cbits + 1) / 2; + + if (wps->w2.last_delta_sign [chan] > 0) + delta_dbits *= -1; + + wps->w2.last_delta_sign [chan] = delta_dbits; + } + else { + delta_dbits = cbits / 2; + + if (wps->w2.last_delta_sign [chan] <= 0) + delta_dbits *= -1; + } + + dbits = (wps->w2.last_dbits [chan] += delta_dbits); + + if (dbits < 0 || dbits > 20) + return WORD_EOF; + + if (!dbits) + return 0L; + + if (wps->wphdr.bits) { + for (value = 1L << (dbits - 1); --dbits; mask <<= 1) + if (dbits < wps->wphdr.bits && getbit (&wps->wvbits)) + value |= mask; + } + else + for (value = 1L << (dbits - 1); --dbits; mask <<= 1) + if (getbit (&wps->wvbits)) + value |= mask; + + return getbit (&wps->wvbits) ? -(int32_t)value : value; +} + +static int32_t FASTCALL get_word3 (WavpackStream3 *wps, int chan) +{ + int cbits, delta_dbits, dbits; + int32_t value; + + for (cbits = 0; cbits < 72 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 72) + return WORD_EOF; + + if (cbits || getbit (&wps->wvbits)) + ++cbits; + + if (!((cbits + 1) % 3)) + delta_dbits = (cbits + 1) / 3; + else + delta_dbits = -(cbits - cbits / 3); + + if (chan) { + dbits = (wps->w3.ave_dbits [1] >> 8) + 1 + delta_dbits; + wps->w3.ave_dbits [1] -= (wps->w3.ave_dbits [1] + 0x10) >> 5; + wps->w3.ave_dbits [1] += dbits << 3; + } + else { + dbits = (wps->w3.ave_dbits [0] >> 8) + 1 + delta_dbits; + wps->w3.ave_dbits [0] -= (wps->w3.ave_dbits [0] + 0x10) >> 5; + wps->w3.ave_dbits [0] += dbits << 3; + } + + if (dbits < 0 || dbits > 24) + return WORD_EOF; + + if (!dbits) + return 0L; + + if (wps->wphdr.bits && dbits > wps->wphdr.bits) { + getbits (&value, wps->wphdr.bits, &wps->wvbits); + + if (value & bitset [wps->wphdr.bits - 1]) + return -(int32_t)(value & bitmask [wps->wphdr.bits]) << (dbits - wps->wphdr.bits); + else + return ((value & bitmask [wps->wphdr.bits - 1]) | bitset [wps->wphdr.bits - 1]) << (dbits - wps->wphdr.bits); + } + else { + getbits (&value, dbits, &wps->wvbits); + + if (value & bitset [dbits - 1]) + return -(int32_t)(value & bitmask [dbits]); + else + return (value & bitmask [dbits - 1]) | bitset [dbits - 1]; + } +} + +static int FASTCALL _log2 (uint32_t avalue); + +static int32_t FASTCALL get_word4 (WavpackStream3 *wps, int chan, int32_t *correction) +{ + uint32_t base, ones_count, avalue; + int32_t value, low, mid, high; + int bitcount; + + // count consecutive ones in bitstream, > 25 indicates error (or EOF) + + for (ones_count = 0; ones_count < 25 && getbit (&wps->wvbits); ++ones_count); + + if (ones_count == 25) + return WORD_EOF; + + // if the ones count is exactly 24, then we switch to non-unary method + + if (ones_count == 24) { + int32_t mask; + int cbits; + + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += 24; + } + + if (!chan) { + int slow_log_0, slow_log_1, balance; + + if (wps->wphdr.flags & MONO_FLAG) { + wps->w4.bits_acc [0] += wps->w4.bitrate + _log2 (wps->w4.fast_level [0]) - _log2 (wps->w4.slow_level [0]) + (3 << 8); + + if (wps->w4.bits_acc [0] < 0) + wps->w4.bits_acc [0] = 0; + } + else { + slow_log_0 = _log2 (wps->w4.slow_level [0]); + slow_log_1 = _log2 (wps->w4.slow_level [1]); + + if (wps->wphdr.flags & JOINT_STEREO) + balance = (slow_log_1 - slow_log_0 + 257) >> 1; + else + balance = (slow_log_1 - slow_log_0 + 1) >> 1; + + wps->w4.bits_acc [0] += wps->w4.bitrate - balance + _log2 (wps->w4.fast_level [0]) - slow_log_0 + (3 << 8); + wps->w4.bits_acc [1] += wps->w4.bitrate + balance + _log2 (wps->w4.fast_level [1]) - slow_log_1 + (3 << 8); + + if (wps->w4.bits_acc [0] + wps->w4.bits_acc [1] < 0) + wps->w4.bits_acc [0] = wps->w4.bits_acc [1] = 0; + else if (wps->w4.bits_acc [0] < 0) { + wps->w4.bits_acc [1] += wps->w4.bits_acc [0]; + wps->w4.bits_acc [0] = 0; + } + else if (wps->w4.bits_acc [1] < 0) { + wps->w4.bits_acc [0] += wps->w4.bits_acc [1]; + wps->w4.bits_acc [1] = 0; + } + } + } + + base = (wps->w4.fast_level [chan] + 48) / 96; + bitcount = wps->w4.bits_acc [chan] >> 8; + wps->w4.bits_acc [chan] &= 0xff; + + if (!base) { + if (ones_count) + high = low = mid = (getbit (&wps->wvbits)) ? -(int32_t)ones_count : ones_count; + else + high = low = mid = 0; + } + else { + mid = (ones_count * 2 + 1) * base; + if (getbit (&wps->wvbits)) mid = -mid; + low = mid - base; + high = mid + base - 1; + + while (bitcount--) { + if (getbit (&wps->wvbits)) + mid = (high + (low = mid) + 1) >> 1; + else + mid = ((high = mid - 1) + low + 1) >> 1; + + if (high == low) + break; + } + } + + wps->w4.fast_level [chan] -= ((wps->w4.fast_level [chan] + 0x10) >> 5); + wps->w4.fast_level [chan] += (avalue = labs (mid)); + wps->w4.slow_level [chan] -= ((wps->w4.slow_level [chan] + 0x80) >> 8); + wps->w4.slow_level [chan] += avalue; + + if (bs_is_open (&wps->wvcbits)) { + + if (high != low) { + uint32_t maxcode = high - low; + int bitcount = count_bits (maxcode); + uint32_t extras = (1L << bitcount) - maxcode - 1; + + getbits (&avalue, bitcount - 1, &wps->wvcbits); + avalue &= bitmask [bitcount - 1]; + + if (avalue >= extras) { + avalue = (avalue << 1) - extras; + + if (getbit (&wps->wvcbits)) + ++avalue; + } + + value = (mid < 0) ? high - avalue : avalue + low; + + if (correction) + *correction = value - mid; + } + else if (correction) + *correction = 0; + } + + return mid; +} + +// This function calculates an approximate base-2 logarithm (with 8 bits of +// fraction) from the supplied value. Using logarithms makes comparing +// signal level values and calculating fractional bitrates much easier. + +static int FASTCALL _log2 (uint32_t avalue) +{ + int dbits; + + if ((avalue += avalue >> 9) < (1 << 8)) { + dbits = nbits_table [avalue]; + return (dbits << 8) + ((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) + ((avalue >> (dbits - 9)) & 0xff); + } +} + +#endif diff --git a/Libraries/WavPack/Files/unpack3.h b/Libraries/WavPack/Files/unpack3.h new file mode 100644 index 000000000..d958141b4 --- /dev/null +++ b/Libraries/WavPack/Files/unpack3.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wavpack3.h + +// This header file contains all the additional definitions required for +// decoding old (versions 1, 2 & 3) WavPack files. + +typedef struct { + ushort FormatTag, NumChannels; + uint32_t SampleRate, BytesPerSecond; + ushort BlockAlign, BitsPerSample; +} WaveHeader3; + +#define WaveHeader3Format "SSLLSS" + +typedef struct { + char ckID [4]; + int32_t ckSize; + short version; + short bits; // added for version 2.00 + short flags, shift; // added for version 3.00 + int32_t total_samples, crc, crc2; + char extension [4], extra_bc, extras [3]; +} WavpackHeader3; + +#define WavpackHeader3Format "4LSSSSLLL4L" + +// these flags added for version 3 + +#undef MONO_FLAG // these definitions changed for WavPack 4.0 +#undef CROSS_DECORR +#undef JOINT_STEREO + +#define MONO_FLAG 1 // not stereo +#define FAST_FLAG 2 // non-adaptive predictor and stereo mode +#define RAW_FLAG 4 // raw mode (no .wav header) +#define CALC_NOISE 8 // calc noise in lossy mode (no longer stored) +#define HIGH_FLAG 0x10 // high quality mode (all modes) +#define BYTES_3 0x20 // files have 3-byte samples +#define OVER_20 0x40 // samples are over 20 bits +#define WVC_FLAG 0x80 // create/use .wvc (no longer stored) +#define LOSSY_SHAPE 0x100 // noise shape (lossy mode only) +#define VERY_FAST_FLAG 0x200 // double fast (no longer stored) +#define NEW_HIGH_FLAG 0x400 // new high quality mode (lossless only) +#define CANCEL_EXTREME 0x800 // cancel EXTREME_DECORR +#define CROSS_DECORR 0x1000 // decorrelate chans (with EXTREME_DECORR flag) +#define NEW_DECORR_FLAG 0x2000 // new high-mode decorrelator +#define JOINT_STEREO 0x4000 // joint stereo (lossy and high lossless) +#define EXTREME_DECORR 0x8000 // extra decorrelation (+ enables other flags) + +#define STORED_FLAGS 0xfd77 // these are only flags that affect unpacking +#define NOT_STORED_FLAGS (~STORED_FLAGS & 0xffff) + +// BitStream stuff (bits.c) + +typedef struct bs3 { + void (*wrap)(struct bs3 *bs); + uchar *buf, *end, *ptr; + uint32_t bufsiz, fpos, sr; + stream_reader *reader; + int error, bc; + void *id; +} Bitstream3; + +#define K_DEPTH 3 +#define MAX_NTERMS3 18 + +typedef struct { + WavpackHeader3 wphdr; + Bitstream3 wvbits, wvcbits; + uint32_t sample_index; + int num_terms; + +#ifdef SEEKING + struct index_point { + char saved; + uint32_t sample_index; + } index_points [256]; + + uchar *unpack_data; + uint32_t unpack_size; +#endif + + struct { + int32_t sum_level, left_level, right_level, diff_level; + int last_extra_bits, extra_bits_count, m; + int32_t error [2], crc; + int32_t sample [2] [2]; + int weight [2] [1]; + } dc; + + struct decorr_pass decorr_passes [MAX_NTERMS3]; + + struct { + uint index [2], k_value [2], ave_k [2]; + uint32_t zeros_acc, ave_level [K_DEPTH] [2]; + } w1; + + struct { int last_dbits [2], last_delta_sign [2], bit_limit; } w2; + + struct { int ave_dbits [2], bit_limit; } w3; + + struct { + uint32_t fast_level [2], slow_level [2]; + int bits_acc [2], bitrate; + } w4; +} WavpackStream3; diff --git a/Libraries/WavPack/Files/utils.c b/Libraries/WavPack/Files/utils.c new file mode 100644 index 000000000..36d25e612 --- /dev/null +++ b/Libraries/WavPack/Files/utils.c @@ -0,0 +1,640 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// utils.c + +// This module provides general purpose utilities for the WavPack command-line +// utilities and the self-extraction module. + +#if defined(WIN32) +#include +#include +#include +#elif defined(__GNUC__) +#include +#endif + +#ifndef WIN32 +#include +#include +#endif + +#include +#include +#include +#include + +#include "wavpack.h" + + +#ifdef WIN32 + +int copy_timestamp (const char *src_filename, const char *dst_filename) +{ + FILETIME last_modified; + HANDLE src, dst; + int res = TRUE; + + if (*src_filename == '-' || *dst_filename == '-') + return res; + + src = CreateFile (src_filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + + dst = CreateFile (dst_filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + + if (src == INVALID_HANDLE_VALUE || dst == INVALID_HANDLE_VALUE || + !GetFileTime (src, NULL, NULL, &last_modified) || + !SetFileTime (dst, NULL, NULL, &last_modified)) + res = FALSE; + + if (src != INVALID_HANDLE_VALUE) + CloseHandle (src); + + if (dst != INVALID_HANDLE_VALUE) + CloseHandle (dst); + + return res; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// This function parses a filename (with or without full path) and returns // +// a pointer to the extension (including the "."). If no extension is found // +// then NULL is returned. Extensions with more than 3 letters don't count. // +////////////////////////////////////////////////////////////////////////////// + +#if defined(WIN32) + +static int is_second_byte (char *filespec, char *pos); + +char *filespec_ext (char *filespec) +{ + char *cp = filespec + strlen (filespec); + LANGID langid = GetSystemDefaultLangID (); + + while (--cp >= filespec) { + + if (langid == 0x411 && is_second_byte (filespec, cp)) + --cp; + + if (*cp == '\\' || *cp == ':') + return NULL; + + if (*cp == '.') { + if (strlen (cp) > 1 && strlen (cp) <= 4) + return cp; + else + return NULL; + } + } + + return NULL; +} + +#else + +char *filespec_ext (char *filespec) +{ + char *cp = filespec + strlen (filespec); + + while (--cp >= filespec) { + + if (*cp == '\\' || *cp == ':') + return NULL; + + if (*cp == '.') { + if (strlen (cp) > 1 && strlen (cp) <= 4) + return cp; + else + return NULL; + } + } + + return NULL; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// This function determines if the specified filespec is a valid pathname. // +// If not, NULL is returned. If it is in the format of a pathname, then the // +// original pointer is returned. If the format is ambiguous, then a lookup // +// is performed to determine if it is in fact a valid path, and if so a "\" // +// is appended so that the pathname can be used and the original pointer is // +// returned. // +////////////////////////////////////////////////////////////////////////////// + +#if defined(__GNUC__) && !defined(WIN32) + +char *filespec_path (char *filespec) +{ + char *cp = filespec + strlen (filespec); + glob_t globs; + struct stat fstats; + + if (cp == filespec || filespec_wild (filespec)) + return NULL; + + if (*--cp == '\\' || *cp == ':') + return filespec; + + if (*cp == '.' && cp == filespec) + return strcat (filespec, "\\"); + + if (glob (filespec, GLOB_MARK|GLOB_NOSORT, NULL, &globs) == 0 && + globs.gl_pathc > 0) + { + /* test if the file is a directory */ + if (stat(globs.gl_pathv[0], &fstats) == 0 && (fstats.st_mode & S_IFDIR)) { + globfree(&globs); + filespec[0] = '\0'; + return strcat (filespec, globs.gl_pathv[0]); + } + } + globfree(&globs); + + return NULL; +} + +#else + +char *filespec_path (char *filespec) +{ + char *cp = filespec + strlen (filespec); + LANGID langid = GetSystemDefaultLangID (); + struct _finddata_t finddata; + int32_t file; + + if (cp == filespec || filespec_wild (filespec)) + return NULL; + + --cp; + + if (langid == 0x411 && is_second_byte (filespec, cp)) + --cp; + + if (*cp == '\\' || *cp == ':') + return filespec; + + if (*cp == '.' && cp == filespec) + return strcat (filespec, "\\"); + + if ((file = _findfirst (filespec, &finddata)) != -1L && + (finddata.attrib & _A_SUBDIR)) { + _findclose (file); + return strcat (filespec, "\\"); + } + if (file != -1L) + _findclose(file); + + return NULL; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// This function returns non-NULL if the specified filename spec has any // +// wildcard characters. // +////////////////////////////////////////////////////////////////////////////// + +char *filespec_wild (char *filespec) +{ + return strpbrk (filespec, "*?"); +} + +////////////////////////////////////////////////////////////////////////////// +// This function parses a filename (with or without full path) and returns // +// a pointer to the actual filename, or NULL if no filename can be found. // +////////////////////////////////////////////////////////////////////////////// + +#if defined(WIN32) + +char *filespec_name (char *filespec) +{ + char *cp = filespec + strlen (filespec); + LANGID langid = GetSystemDefaultLangID (); + + while (--cp >= filespec) { + if (langid == 0x411 && is_second_byte (filespec, cp)) + --cp; + + if (*cp == '\\' || *cp == ':') + break; + } + + if (strlen (cp + 1)) + return cp + 1; + else + return NULL; +} + +#else + +char *filespec_name (char *filespec) +{ + char *cp = filespec + strlen (filespec); + + while (--cp >= filespec) + if (*cp == '\\' || *cp == ':') + break; + + if (strlen (cp + 1)) + return cp + 1; + else + return NULL; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// This function returns TRUE if "pos" is pointing to the second byte of a // +// double-byte character in the string "filespec" which is assumed to be // +// shift-JIS. // +////////////////////////////////////////////////////////////////////////////// + +#if defined(WIN32) + +static int is_second_byte (char *filespec, char *pos) +{ + uchar *cp = pos; + + while (cp > filespec && ((cp [-1] >= 0x81 && cp [-1] <= 0x9f) || + (cp [-1] >= 0xe0 && cp [-1] <= 0xfc))) + cp--; + + return ((int) pos - (int) cp) & 1; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// This function allows the user to type 'y', 'n', or 'a' (with Enter) in // +// response to a system query. The return value is the key typed as // +// lowercase (regardless of the typed case). // +////////////////////////////////////////////////////////////////////////////// + +static int waiting_input; + +char yna (void) +{ + char choice = 0; + int key; + + waiting_input = 1; + + while (1) { +#if defined(WIN32) + key = getch (); +#else + key = fgetc(stdin); +#endif + if (key == 3) { + fprintf (stderr, "^C\n"); + exit (1); + } + else if (key == '\r' || key == '\n') { + if (choice) { + fprintf (stderr, "\r\n"); + break; + } + else + fprintf (stderr, "%c", 7); + } + else if (key == 'Y' || key == 'y') { + fprintf (stderr, "%c\b", key); + choice = 'y'; + } + else if (key == 'N' || key == 'n') { + fprintf (stderr, "%c\b", key); + choice = 'n'; + } + else if (key == 'A' || key == 'a') { + fprintf (stderr, "%c\b", key); + choice = 'a'; + } + else + fprintf (stderr, "%c", 7); + } + + waiting_input = 0; + + return choice; +} + +////////////////////////////////////////////////////////////////////////////// +// Display the specified message on the console through stderr. Note that // +// the cursor may start anywhere in the line and all text already on the // +// line is erased. A terminating newline is not needed and function works // +// with printf strings and args. // +////////////////////////////////////////////////////////////////////////////// + +void error_line (char *error, ...) +{ + char error_msg [512]; + va_list argptr; + + error_msg [0] = '\r'; + va_start (argptr, error); + vsprintf (error_msg + 1, error, argptr); + va_end (argptr); + fputs (error_msg, stderr); + finish_line (); +#if 0 + { + FILE *error_log = fopen ("c:\\wavpack.log", "a+"); + + if (error_log) { + fputs (error_msg + 1, error_log); + fputc ('\n', error_log); + fclose (error_log); + } + } +#endif +} + +////////////////////////////////////////////////////////////////////////////// +// Function to intercept ^C or ^Break typed at the console. // +////////////////////////////////////////////////////////////////////////////// +#if defined(WIN32) +static int break_flag; + +BOOL WINAPI ctrl_handler (DWORD ctrl) +{ + if (ctrl == CTRL_C_EVENT) { + break_flag = TRUE; + return TRUE; + } + + if (ctrl == CTRL_BREAK_EVENT) { + + if (waiting_input) { +#ifdef __BORLANDC__ + fprintf (stderr, "^C\n"); +#endif + return FALSE; + } + else { + break_flag = TRUE; + return TRUE; + } + } + + return FALSE; +} + +////////////////////////////////////////////////////////////////////////////// +// Function to initialize console for intercepting ^C and ^Break. // +////////////////////////////////////////////////////////////////////////////// + +void setup_break (void) +{ + HANDLE hConIn = GetStdHandle (STD_INPUT_HANDLE); + + SetConsoleMode (hConIn, ENABLE_PROCESSED_INPUT); + FlushConsoleInputBuffer (hConIn); + SetConsoleCtrlHandler (ctrl_handler, TRUE); + break_flag = 0; +} + +////////////////////////////////////////////////////////////////////////////// +// Function to determine whether ^C or ^Break has been issued by user. // +////////////////////////////////////////////////////////////////////////////// + +int check_break (void) +{ + return break_flag; +} + +////////////////////////////////////////////////////////////////////////////// +// Function to clear the stderr console to the end of the current line (and // +// go to the beginning next line). // +////////////////////////////////////////////////////////////////////////////// + +void finish_line (void) +{ + HANDLE hConIn = GetStdHandle (STD_ERROR_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO coninfo; + + if (hConIn && GetConsoleScreenBufferInfo (hConIn, &coninfo)) { + char spaces = coninfo.dwSize.X - coninfo.dwCursorPosition.X; + + while (spaces--) + fputc (' ', stderr); + } + else + fputc ('\n', stderr); +} +#else +////////////////////////////////////////////////////////////////////////////// +// Function to clear the stderr console to the end of the current line (and // +// go to the beginning next line). // +////////////////////////////////////////////////////////////////////////////// + +void finish_line (void) +{ +/* char spaces = 1; + + while (spaces--) + putc (' ', stderr); + else*/ + fputc ('\n', stderr); +} + +////////////////////////////////////////////////////////////////////////////// +// Function to initialize console for intercepting ^C and ^Break. // +////////////////////////////////////////////////////////////////////////////// + +void setup_break (void) +{ +} + +////////////////////////////////////////////////////////////////////////////// +// Function to determine whether ^C or ^Break has been issued by user. // +////////////////////////////////////////////////////////////////////////////// + +int check_break (void) +{ + return 0; +} + +#endif + +//////////////////////////// File I/O Wrapper //////////////////////////////// + +int DoReadFile (FILE *hFile, void *lpBuffer, uint32_t nNumberOfBytesToRead, uint32_t *lpNumberOfBytesRead) +{ + uint32_t bcount; + + *lpNumberOfBytesRead = 0; + + while (nNumberOfBytesToRead) { + bcount = fread ((uchar *) lpBuffer + *lpNumberOfBytesRead, 1, nNumberOfBytesToRead, hFile); + + if (bcount) { + *lpNumberOfBytesRead += bcount; + nNumberOfBytesToRead -= bcount; + } + else + break; + } + + return !ferror (hFile); +} + +int DoWriteFile (FILE *hFile, void *lpBuffer, uint32_t nNumberOfBytesToWrite, uint32_t *lpNumberOfBytesWritten) +{ + uint32_t bcount; + + *lpNumberOfBytesWritten = 0; + + while (nNumberOfBytesToWrite) { + bcount = fwrite ((uchar *) lpBuffer + *lpNumberOfBytesWritten, 1, nNumberOfBytesToWrite, hFile); + + if (bcount) { + *lpNumberOfBytesWritten += bcount; + nNumberOfBytesToWrite -= bcount; + } + else + break; + } + + return !ferror (hFile); +} + +uint32_t DoGetFileSize (FILE *hFile) +{ + struct stat statbuf; + + if (!hFile || fstat (fileno (hFile), &statbuf) || !(statbuf.st_mode & S_IFREG)) + return 0; + + return statbuf.st_size; +} + +uint32_t DoGetFilePosition (FILE *hFile) +{ + return ftell (hFile); +} + +int DoSetFilePositionAbsolute (FILE *hFile, uint32_t pos) +{ + return fseek (hFile, pos, SEEK_SET); +} + +int DoSetFilePositionRelative (FILE *hFile, int32_t pos, int mode) +{ + return fseek (hFile, pos, mode); +} + +// if ungetc() is not available, a seek of -1 is fine also because we do not +// change the byte read. + +int DoUngetc (int c, FILE *hFile) +{ + return ungetc (c, hFile); +} + +int DoCloseHandle (FILE *hFile) +{ + return hFile ? !fclose (hFile) : 0; +} + +int DoTruncateFile (FILE *hFile) +{ + if (hFile) { + fflush (hFile); +#if defined(WIN32) + return !chsize (fileno (hFile), 0); +#else + return !ftruncate(fileno (hFile), 0); +#endif + } + + return 0; +} + +int DoDeleteFile (char *filename) +{ + return !remove (filename); +} + +// Convert the Unicode wide-format string into a UTF-8 string using no more +// than the specified buffer length. The wide-format string must be NULL +// terminated and the resulting string will be NULL terminated. The actual +// number of characters converted (not counting terminator) is returned, which +// may be less than the number of characters in the wide string if the buffer +// length is exceeded. + +static int WideCharToUTF8 (const ushort *Wide, uchar *pUTF8, int len) +{ + const ushort *pWide = Wide; + int outndx = 0; + + while (*pWide) { + if (*pWide < 0x80 && outndx + 1 < len) + pUTF8 [outndx++] = (uchar) *pWide++; + else if (*pWide < 0x800 && outndx + 2 < len) { + pUTF8 [outndx++] = (uchar) (0xc0 | ((*pWide >> 6) & 0x1f)); + pUTF8 [outndx++] = (uchar) (0x80 | (*pWide++ & 0x3f)); + } + else if (outndx + 3 < len) { + pUTF8 [outndx++] = (uchar) (0xe0 | ((*pWide >> 12) & 0xf)); + pUTF8 [outndx++] = (uchar) (0x80 | ((*pWide >> 6) & 0x3f)); + pUTF8 [outndx++] = (uchar) (0x80 | (*pWide++ & 0x3f)); + } + else + break; + } + + pUTF8 [outndx] = 0; + return pWide - Wide; +} + +// Convert a Ansi string into its Unicode UTF-8 format equivalent. The +// conversion is done in-place so the maximum length of the string buffer must +// be specified because the string may become longer or shorter. If the +// resulting string will not fit in the specified buffer size then it is +// truncated. + +void AnsiToUTF8 (char *string, int len) +{ + int max_chars = strlen (string); +#if defined(WIN32) + ushort *temp = (ushort *) malloc ((max_chars + 1) * 2); + + MultiByteToWideChar (CP_ACP, 0, string, -1, temp, max_chars + 1); + WideCharToUTF8 (temp, (uchar *) string, len); +#else + char *temp = malloc (len); +// memset(temp, 0, len); + char *outp = temp; + const char *inp = string; + size_t insize = max_chars; + size_t outsize = len - 1; + int err = 0; + char *old_locale; + + memset(temp, 0, len); + old_locale = setlocale (LC_CTYPE, ""); + iconv_t converter = iconv_open ("UTF-8", ""); + err = iconv (converter, &inp, &insize, &outp, &outsize); + iconv_close (converter); + setlocale (LC_CTYPE, old_locale); + + if (err == -1) { + free(temp); + return; + } + + memmove (string, temp, len); +#endif + free (temp); +} diff --git a/Libraries/WavPack/Files/wavpack.c b/Libraries/WavPack/Files/wavpack.c new file mode 100644 index 000000000..50ec1b34c --- /dev/null +++ b/Libraries/WavPack/Files/wavpack.c @@ -0,0 +1,1448 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wavpack.c + +// This is the main module for the WavPack command-line compressor. + +#if defined(WIN32) +#include +#include +#else +#include +#include +#endif + +#include +#include +#include +#include + +#include "wavpack.h" +#include "md5.h" + +#ifdef __BORLANDC__ +#include +#else +#if defined (__GNUC__) && !defined(WIN32) +#include +#include +#include +#else +#include +#endif +#endif + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +static char *strdup (const char *s) + { char *d = malloc (strlen (s) + 1); return strcpy (d, s); } +#endif + +///////////////////////////// local variable storage ////////////////////////// + +static const char *sign_on = "\n" +" WAVPACK Hybrid Lossless Wavefile Compressor %s Version %s %s\n" +" Copyright (c) 1998 - 2005 Conifer Software. All Rights Reserved.\n\n"; + +static const char *usage = +" Usage: WAVPACK [-options] [@]infile[.wav]|- [[@]outfile[.wv]|outpath|-]\n" +" (default is lossless; infile may contain wildcards: ?,*)\n\n" +" Options: -a = Adobe Audition (CoolEdit) mode for 32-bit floats\n" +" -bn = enable hybrid compression, n = 2.0 to 23.9 bits/sample, or\n" +" n = 24-9600 kbits/second (kbps)\n" +" -c = create correction file (.wvc) for hybrid mode (=lossless)\n" +" -cc = maximum hybrid compression (hurts lossy quality & decode speed)\n" +" -d = delete source file if successful (use with caution!)\n" +#if defined (WIN32) +" -e = create self-extracting executable (needs wvselfx.exe)\n" +#endif +" -f = fast mode (fast, but some compromise in compression ratio)\n" +" -h = high quality (best compression in all modes, but slower)\n" +" -i = ignore length in wav header (no pipe output allowed)\n" +" -jn = joint-stereo override (0 = left/right, 1 = mid/side)\n" +" -m = compute & store MD5 signature of raw audio data\n" +" -n = calculate average and peak quantization noise (hybrid only)\n" +" -p = practical float storage (also 32-bit ints, not lossless)\n" +" -q = quiet (keep console output to a minimum)\n" +" -sn = noise shaping override (hybrid only, n = -1.0 to 1.0, 0 = off)\n" +#if defined (WIN32) +" -t = copy input file's time stamp to output file(s)\n" +#endif +" -w \"Field=Value\" = write specified metadata to APEv2 tag\n" +" -x[n] = extra encode processing (optional n = 1-6 for less/more)\n" +" -y = yes to all warnings (use with caution!)\n\n" +" Web: Visit www.wavpack.com for latest version and info\n"; + +static int overwrite_all, num_files, file_index; + +#if defined (WIN32) +static char *wvselfx_image; +static uint32_t wvselfx_size; +#endif + +/////////////////////////// local function declarations /////////////////////// + +static int pack_file (char *infilename, char *outfilename, char *out2filename, const WavpackConfig *config); +static int pack_audio (WavpackContext *wpc, FILE *infile); +static void display_progress (double file_progress); + +#define NO_ERROR 0L +#define SOFT_ERROR 1 +#define HARD_ERROR 2 + +////////////////////////////////////////////////////////////////////////////// +// The "main" function for the command-line WavPack compressor. // +////////////////////////////////////////////////////////////////////////////// + +int main (argc, argv) int argc; char **argv; +{ + int delete_source = 0, usage_error = 0, filelist = 0, tag_next_arg = 0; + char *infilename = NULL, *outfilename = NULL, *out2filename = NULL; + char **matches = NULL; + WavpackConfig config; + int result, i; + +#if defined (WIN32) + char *selfname = malloc (strlen (*argv) + PATH_MAX); + struct _finddata_t _finddata_t; + + strcpy (selfname, *argv); +#else + glob_t globs; + struct stat fstats; +#endif + + CLEAR (config); + +#if 0 + { + char **argv_t = argv; + int argc_t = argc; + + while (--argc_t) + error_line ("%d: %s", argc - argc_t, *++argv_t); + } +#endif + + // loop through command-line arguments + + while (--argc) +#if defined (WIN32) + if ((**++argv == '-' || **argv == '/') && (*argv)[1]) +#else + if ((**++argv == '-') && (*argv)[1]) +#endif + while (*++*argv) + switch (**argv) { + + case 'Y': case 'y': + overwrite_all = 1; + break; + + case 'D': case 'd': + delete_source = 1; + break; + + case 'C': case 'c': + if (config.flags & CONFIG_CREATE_WVC) + config.flags |= CONFIG_OPTIMIZE_WVC; + else + config.flags |= CONFIG_CREATE_WVC; + + break; + + case 'X': case 'x': + config.xmode = strtol (++*argv, argv, 10); + + if (config.xmode < 0 || config.xmode > 6) { + error_line ("extra mode only goes from 1 to 6!"); + usage_error = 1; + } + else + config.flags |= CONFIG_EXTRA_MODE; + + --*argv; + break; + + case 'F': case 'f': + if (config.flags & CONFIG_FAST_FLAG) + config.flags |= CONFIG_VERY_FAST_FLAG; + else + config.flags |= CONFIG_FAST_FLAG; + + break; + + case 'H': case 'h': + if (config.flags & CONFIG_HIGH_FLAG) + config.flags |= CONFIG_VERY_HIGH_FLAG; + else + config.flags |= CONFIG_HIGH_FLAG; + + break; + + case 'N': case 'n': + config.flags |= CONFIG_CALC_NOISE; + break; + + case 'A': case 'a': + config.flags |= CONFIG_ADOBE_MODE; + break; +#if defined (WIN32) + case 'E': case 'e': + config.flags |= CONFIG_CREATE_EXE; + break; + + case 'T': case 't': + config.flags |= CONFIG_COPY_TIME; + break; +#endif + case 'P': case 'p': + config.flags |= CONFIG_SKIP_WVX; + break; + + case 'Q': case 'q': + config.flags |= CONFIG_QUIET_MODE; + break; + + case 'M': case 'm': + config.flags |= CONFIG_MD5_CHECKSUM; + break; + + case 'I': case 'i': + config.flags |= CONFIG_IGNORE_LENGTH; + break; + + case 'K': case 'k': + config.block_samples = strtol (++*argv, argv, 10); + --*argv; + break; + + case 'B': case 'b': + config.flags |= CONFIG_HYBRID_FLAG; + config.bitrate = strtod (++*argv, argv); + --*argv; + + if (config.bitrate < 2.0 || config.bitrate > 9600.0) { + error_line ("hybrid spec must be 2.0 to 9600!"); + usage_error = 1; + } + + if (config.bitrate >= 24.0) + config.flags |= CONFIG_BITRATE_KBPS; + + break; + + case 'J': case 'j': + switch (strtol (++*argv, argv, 10)) { + + case 0: + config.flags |= CONFIG_JOINT_OVERRIDE; + config.flags &= ~CONFIG_JOINT_STEREO; + break; + + case 1: + config.flags |= (CONFIG_JOINT_OVERRIDE | CONFIG_JOINT_STEREO); + break; + + default: + error_line ("-j0 or -j1 only!"); + usage_error = 1; + } + + --*argv; + break; + + case 'S': case 's': + config.shaping_weight = strtod (++*argv, argv); + + if (!config.shaping_weight) { + config.flags |= CONFIG_SHAPE_OVERRIDE; + config.flags &= ~CONFIG_HYBRID_SHAPE; + } + else if (config.shaping_weight >= -1.0 && config.shaping_weight <= 1.0) + config.flags |= (CONFIG_HYBRID_SHAPE | CONFIG_SHAPE_OVERRIDE); + else { + error_line ("-s-1.00 to -s1.00 only!"); + usage_error = 1; + } + + --*argv; + break; + + case 'W': case 'w': + tag_next_arg = 1; + break; + + default: + error_line ("illegal option: %c !", **argv); + usage_error = 1; + } + else { + if (tag_next_arg) { + char *cp = strchr (*argv, '='), *string = NULL; + + tag_next_arg = 0; + + if (cp && cp > *argv) { + int item_len = cp - *argv; + + if (cp [1] == '@') { + FILE *file = fopen (cp+2, "rb"); + + if (file) { + uint32_t bcount, file_len; + + file_len = DoGetFileSize (file); + + if (file_len < 1048576 && (string = malloc (item_len + file_len + 2)) != NULL) { + memcpy (string, *argv, item_len + 1); + + if (!DoReadFile (file, string + item_len + 1, file_len, &bcount) || bcount != file_len) { + free (string); + string = NULL; + } + else + string [item_len + file_len + 1] = 0; + } + + DoCloseHandle (file); + } + } + else + string = *argv; + } + + if (!string) { + error_line ("error in tag spec: %s !", *argv); + usage_error = 1; + } + else if (cp [1]) { + config.tag_strings = realloc (config.tag_strings, ++config.num_tag_strings * sizeof (*config.tag_strings)); + config.tag_strings [config.num_tag_strings - 1] = string; + } + } + else if (!infilename) { + infilename = malloc (strlen (*argv) + PATH_MAX); + strcpy (infilename, *argv); + } + else if (!outfilename) { + outfilename = malloc (strlen (*argv) + PATH_MAX); + strcpy (outfilename, *argv); + } + else if (!out2filename) { + out2filename = malloc (strlen (*argv) + PATH_MAX); + strcpy (out2filename, *argv); + } + else { + error_line ("extra unknown argument: %s !", *argv); + usage_error = 1; + } + } + + setup_break (); // set up console and detect ^C and ^Break + + // check for various command-line argument problems + + if (!(~config.flags & (CONFIG_HIGH_FLAG | CONFIG_FAST_FLAG))) { + error_line ("high and fast modes are mutually exclusive!"); + usage_error = 1; + } + + if ((config.flags & CONFIG_IGNORE_LENGTH) && outfilename && *outfilename == '-') { + error_line ("can't ignore length in header when using stdout!"); + usage_error = 1; + } + + if (config.flags & CONFIG_HYBRID_FLAG) { + if ((config.flags & CONFIG_CREATE_WVC) && outfilename && *outfilename == '-') { + error_line ("can't create correction file when using stdout!"); + usage_error = 1; + } + } + else { + if (config.flags & (CONFIG_CALC_NOISE | CONFIG_SHAPE_OVERRIDE | CONFIG_CREATE_WVC)) { + error_line ("-s, -n and -c options are for hybrid mode (-b) only!"); + usage_error = 1; + } + } + + if (!(config.flags & CONFIG_QUIET_MODE) && !usage_error) + fprintf (stderr, sign_on, VERSION_OS, VERSION_STR, DATE_STR); + + if (!infilename) { + printf ("%s", usage); + return 0; + } + + if (usage_error) { + free(infilename); + return 0; + } + + // If we are trying to create self-extracting .exe files, this is where + // we read the wvselfx.exe file into memory in preparation for pre-pending + // it to the WavPack files. + +#if defined (WIN32) + if (config.flags & CONFIG_CREATE_EXE) { + FILE *wvselfx_file; + uint32_t bcount; + + strcpy (filespec_name (selfname), "wvselfx.exe"); + + wvselfx_file = fopen (selfname, "rb"); + + if (!wvselfx_file) { + _searchenv ("wvselfx.exe", "PATH", selfname); + wvselfx_file = fopen (selfname, "rb"); + } + + if (wvselfx_file) { + wvselfx_size = DoGetFileSize (wvselfx_file); + + if (wvselfx_size && wvselfx_size != 26624 && wvselfx_size < 49152) { + wvselfx_image = malloc (wvselfx_size); + + if (!DoReadFile (wvselfx_file, wvselfx_image, wvselfx_size, &bcount) || bcount != wvselfx_size) { + free (wvselfx_image); + wvselfx_image = NULL; + } + } + + DoCloseHandle (wvselfx_file); + } + + if (!wvselfx_image) { + error_line ("wvselfx.exe file is not readable or is outdated!"); + free (wvselfx_image); + exit (1); + } + } +#endif + + // If the infile specification begins with a '@', then it actually points + // to a file that contains the names of the files to be converted. This + // was included for use by Wim Speekenbrink's frontends, but could be used + // for other purposes. + + if (infilename [0] == '@') { + FILE *list = fopen (infilename+1, "rt"); + int c; + + if (list == NULL) { + error_line ("file %s not found!", infilename+1); + free (infilename); + return 1; + } + + while ((c = getc (list)) != EOF) { + + while (c == '\n') + c = getc (list); + + if (c != EOF) { + char *fname = malloc (PATH_MAX); + int ci = 0; + + do + fname [ci++] = c; + while ((c = getc (list)) != '\n' && c != EOF && ci < PATH_MAX); + + fname [ci++] = '\0'; + matches = realloc (matches, ++num_files * sizeof (*matches)); + matches [num_files - 1] = realloc (fname, ci); + } + } + + fclose (list); + free (infilename); + infilename = NULL; + filelist = 1; + } + else if (*infilename != '-') { // skip this if infile is stdin (-) + if (!(config.flags & CONFIG_RAW_FLAG) && !filespec_ext (infilename)) + strcat (infilename, ".wav"); + +#ifdef NO_WILDCARDS + matches = malloc (sizeof (*matches)); + matches [num_files++] = infilename; + filelist = 1; +#else + // search for and store any filenames that match the user supplied spec + +#ifdef __BORLANDC__ + if (findfirst (infilename, &ffblk, 0) == 0) { + do { + matches = realloc (matches, ++num_files * sizeof (*matches)); + matches [num_files - 1] = strdup (ffblk.ff_name); + } while (findnext (&ffblk) == 0); + } +#elif defined (WIN32) + if ((i = _findfirst (infilename, &_finddata_t)) != -1L) { + do { + if (!(_finddata_t.attrib & _A_SUBDIR)) { + matches = realloc (matches, ++num_files * sizeof (*matches)); + matches [num_files - 1] = strdup (_finddata_t.name); + } + } while (_findnext (i, &_finddata_t) == 0); + + _findclose (i); + } +#else + i = 0; + if (glob(infilename, 0, NULL, &globs) == 0 && globs.gl_pathc > 0) { + do { + if (stat(globs.gl_pathv[i], &fstats) == 0 && !(fstats.st_mode & S_IFDIR)) { + matches = realloc (matches, ++num_files * sizeof (*matches)); + matches [num_files - 1] = strdup (globs.gl_pathv[i]); + } + } while (i++ < globs.gl_pathc); + } + globfree(&globs); +#endif +#endif + } + else { // handle case of stdin (-) + matches = malloc (sizeof (*matches)); + matches [num_files++] = infilename; + } + + // If the outfile specification begins with a '@', then it actually points + // to a file that contains the output specification. This was included for + // use by Wim Speekenbrink's frontends because certain filenames could not + // be passed on the command-line, but could be used for other purposes. + + if (outfilename && outfilename [0] == '@') { + FILE *list = fopen (outfilename+1, "rt"); + int c; + + if (list == NULL) { + error_line ("file %s not found!", outfilename+1); + free(outfilename); + return 1; + } + + while ((c = getc (list)) == '\n'); + + if (c != EOF) { + int ci = 0; + + do + outfilename [ci++] = c; + while ((c = getc (list)) != '\n' && c != EOF && ci < PATH_MAX); + + outfilename [ci] = '\0'; + } + else { + error_line ("output spec file is empty!"); + free(outfilename); + fclose (list); + return 1; + } + + fclose (list); + } + + if (out2filename && (num_files > 1 || !(config.flags & CONFIG_CREATE_WVC))) { + error_line ("extra unknown argument: %s !", out2filename); + return 1; + } + + // if we found any files to process, this is where we start + + if (num_files) { + char outpath, addext; + int soft_errors = 0; + + if (outfilename && *outfilename != '-') { + outpath = (filespec_path (outfilename) != NULL); + + if (num_files > 1 && !outpath) { + error_line ("%s is not a valid output path", outfilename); + free(outfilename); + return 1; + } + } + else + outpath = 0; + + addext = !outfilename || outpath || !filespec_ext (outfilename); + + // loop through and process files in list + + for (file_index = 0; file_index < num_files; ++file_index) { + if (check_break ()) + break; + + // get input filename from list + + if (filelist) + infilename = matches [file_index]; + else if (*infilename != '-') { + *filespec_name (infilename) = '\0'; + strcat (infilename, matches [file_index]); + } + + // generate output filename + + if (outpath) { + strcat (outfilename, filespec_name (matches [file_index])); + + if (filespec_ext (outfilename)) + *filespec_ext (outfilename) = '\0'; + } + else if (!outfilename) { + outfilename = malloc (strlen (infilename) + 10); + strcpy (outfilename, infilename); + + if (filespec_ext (outfilename)) + *filespec_ext (outfilename) = '\0'; + } + + if (addext && *outfilename != '-') + strcat (outfilename, (config.flags & CONFIG_CREATE_EXE) ? ".exe" : ".wv"); + + // if "correction" file is desired, generate name for that + + if (config.flags & CONFIG_CREATE_WVC) { + if (!out2filename) { + out2filename = malloc (strlen (outfilename) + 10); + strcpy (out2filename, outfilename); + } + else { + char *temp = malloc (strlen (outfilename) + PATH_MAX); + + strcpy (temp, outfilename); + strcpy (filespec_name (temp), filespec_name (out2filename)); + strcpy (out2filename, temp); + free (temp); + } + + if (filespec_ext (out2filename)) + *filespec_ext (out2filename) = '\0'; + + strcat (out2filename, ".wvc"); + } + else + out2filename = NULL; + + if (num_files > 1) + fprintf (stderr, "\n%s:\n", infilename); + + result = pack_file (infilename, outfilename, out2filename, &config); + + if (result == HARD_ERROR) + break; + else if (result == SOFT_ERROR) + ++soft_errors; + + // delete source file if that option is enabled + + if (result == NO_ERROR && delete_source) + error_line ("%s source file %s", DoDeleteFile (infilename) ? + "deleted" : "can't delete", infilename); + + // clean up in preparation for potentially another file + + if (outpath) + *filespec_name (outfilename) = '\0'; + else if (*outfilename != '-') { + free (outfilename); + outfilename = NULL; + } + + if (out2filename) { + free (out2filename); + out2filename = NULL; + } + + free (matches [file_index]); + } + + if (num_files > 1) { + if (soft_errors) + fprintf (stderr, "\n **** warning: errors occurred in %d of %d files! ****\n", soft_errors, num_files); + else if (!(config.flags & CONFIG_QUIET_MODE)) + fprintf (stderr, "\n **** %d files successfully processed ****\n", num_files); + } + + free (matches); + } + else + error_line (filespec_wild (infilename) ? "nothing to do!" : + "file %s not found!", infilename); + + if (outfilename) + free(outfilename); + +#ifdef DEBUG_ALLOC + error_line ("malloc_count = %d", dump_alloc ()); +#endif + + return 0; +} + +// This structure and function are used to write completed WavPack blocks in +// a device independent way. + +typedef struct { + uint32_t bytes_written, first_block_size; + FILE *file; + int error; +} write_id; + +static int write_block (void *id, void *data, int32_t length) +{ + write_id *wid = (write_id *) id; + uint32_t bcount; + + if (wid->error) + return FALSE; + + if (wid && wid->file && data && length) { + if (!DoWriteFile (wid->file, data, length, &bcount) || bcount != length) { + DoTruncateFile (wid->file); + DoCloseHandle (wid->file); + wid->file = NULL; + wid->error = 1; + return FALSE; + } + else { + wid->bytes_written += length; + + if (!wid->first_block_size) + wid->first_block_size = bcount; + } + } + + return TRUE; +} + +// This function packs a single file "infilename" and stores the result at +// "outfilename". If "out2filename" is specified, then the "correction" +// file would go there. The files are opened and closed in this function +// and the "config" structure specifies the mode of compression. + +static int pack_file (char *infilename, char *outfilename, char *out2filename, const WavpackConfig *config) +{ + uint32_t total_samples = 0, wrapper_size = 0, bcount; + WavpackConfig loc_config = *config; + RiffChunkHeader riff_chunk_header; + write_id wv_file, wvc_file; + ChunkHeader chunk_header; + WaveHeader WaveHeader; + WavpackContext *wpc; + double dtime; + FILE *infile; + int result; + +#ifdef __BORLANDC__ + struct time time1, time2; +#elif defined(WIN32) + struct _timeb time1, time2; +#else + struct timeval time1, time2; + struct timezone timez; +#endif + + CLEAR (wv_file); + CLEAR (wvc_file); + wpc = WavpackOpenFileOutput (write_block, &wv_file, out2filename ? &wvc_file : NULL); + + // open the source file for reading + + if (*infilename == '-') { + infile = stdin; +#if defined(WIN32) + setmode (fileno (stdin), O_BINARY); +#endif + } + else if ((infile = fopen (infilename, "rb")) == NULL) { + error_line ("can't open file %s!", infilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + + // check both output files for overwrite warning required + + if (*outfilename != '-' && !overwrite_all && (wv_file.file = fopen (outfilename, "rb")) != NULL) { + DoCloseHandle (wv_file.file); + fprintf (stderr, "overwrite %s (yes/no/all)? ", FN_FIT (outfilename)); + SetConsoleTitle ("overwrite?"); + + switch (yna ()) { + case 'n': + DoCloseHandle (infile); + WavpackCloseFile (wpc); + return SOFT_ERROR; + + case 'a': + overwrite_all = 1; + } + } + + if (out2filename && !overwrite_all && (wvc_file.file = fopen (out2filename, "rb")) != NULL) { + DoCloseHandle (wvc_file.file); + fprintf (stderr, "overwrite %s (yes/no/all)? ", FN_FIT (out2filename)); + SetConsoleTitle ("overwrite?"); + + switch (yna ()) { + + case 'n': + DoCloseHandle (infile); + WavpackCloseFile (wpc); + return SOFT_ERROR; + + case 'a': + overwrite_all = 1; + } + } + +#ifdef __BORLANDC__ + gettime (&time1); +#elif defined(WIN32) + _ftime (&time1); +#else + gettimeofday(&time1,&timez); +#endif + + // open output file for writing + + if (*outfilename == '-') { + wv_file.file = stdout; +#if defined(WIN32) + setmode (fileno (stdout), O_BINARY); +#endif + } + else if ((wv_file.file = fopen (outfilename, "w+b")) == NULL) { + error_line ("can't create file %s!", outfilename); + DoCloseHandle (infile); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + + if (!(loc_config.flags & CONFIG_QUIET_MODE)) { + if (*outfilename == '-') + fprintf (stderr, "packing %s to stdout,", *infilename == '-' ? "stdin" : FN_FIT (infilename)); + else if (out2filename) + fprintf (stderr, "creating %s (+%s),", FN_FIT (outfilename), filespec_ext (out2filename)); + else + fprintf (stderr, "creating %s,", FN_FIT (outfilename)); + } + +#if defined (WIN32) + if (loc_config.flags & CONFIG_CREATE_EXE) + if (!DoWriteFile (wv_file.file, wvselfx_image, wvselfx_size, &bcount) || bcount != wvselfx_size) { + error_line ("can't write WavPack data, disk probably full!"); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } +#endif + + // if not in "raw" mode, read (and copy to output) initial RIFF form header + + if (!(loc_config.flags & CONFIG_RAW_FLAG)) { + if ((!DoReadFile (infile, &riff_chunk_header, sizeof (RiffChunkHeader), &bcount) || + bcount != sizeof (RiffChunkHeader) || strncmp (riff_chunk_header.ckID, "RIFF", 4) || + strncmp (riff_chunk_header.formType, "WAVE", 4))) { + error_line ("%s is not a valid .WAV file!", infilename); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + else if (!WavpackAddWrapper (wpc, &riff_chunk_header, sizeof (RiffChunkHeader))) { + error_line ("%s", wpc->error_message); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + + wrapper_size += sizeof (RiffChunkHeader); + } + + // if not in "raw" mode, loop through all elements of the RIFF wav header + // (until the data chuck) and copy them to the output file + + while (!(loc_config.flags & CONFIG_RAW_FLAG)) { + + if (!DoReadFile (infile, &chunk_header, sizeof (ChunkHeader), &bcount) || + bcount != sizeof (ChunkHeader)) { + error_line ("%s is not a valid .WAV file!", infilename); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + else if (!WavpackAddWrapper (wpc, &chunk_header, sizeof (ChunkHeader))) { + error_line ("%s", wpc->error_message); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + + wrapper_size += sizeof (ChunkHeader); + little_endian_to_native (&chunk_header, ChunkHeaderFormat); + + // if it's the format chunk, we want to get some info out of there and + // make sure it's a .wav file we can handle + + if (!strncmp (chunk_header.ckID, "fmt ", 4)) { + int supported = TRUE, format; + + if (chunk_header.ckSize < 16 || chunk_header.ckSize > sizeof (WaveHeader) || + !DoReadFile (infile, &WaveHeader, chunk_header.ckSize, &bcount) || + bcount != chunk_header.ckSize) { + error_line ("%s is not a valid .WAV file!", infilename); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + else if (!WavpackAddWrapper (wpc, &WaveHeader, chunk_header.ckSize)) { + error_line ("%s", wpc->error_message); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + + wrapper_size += chunk_header.ckSize; + little_endian_to_native (&WaveHeader, WaveHeaderFormat); + +#if 0 + error_line ("format tag size = %d", chunk_header.ckSize); + error_line ("FormatTag = %x, NumChannels = %d, BitsPerSample = %d", + WaveHeader.FormatTag, WaveHeader.NumChannels, WaveHeader.BitsPerSample); + error_line ("BlockAlign = %d, SampleRate = %d, BytesPerSecond = %d", + WaveHeader.BlockAlign, WaveHeader.SampleRate, WaveHeader.BytesPerSecond); + + if (chunk_header.ckSize > 16) + error_line ("cbSize = %d, ValidBitsPerSample = %d", WaveHeader.cbSize, + WaveHeader.ValidBitsPerSample); + + if (chunk_header.ckSize > 20) + error_line ("ChannelMask = %x, SubFormat = %d", + WaveHeader.ChannelMask, WaveHeader.SubFormat); +#endif + + if (chunk_header.ckSize > 16 && WaveHeader.cbSize == 2) + loc_config.flags |= CONFIG_ADOBE_MODE; + + format = (WaveHeader.FormatTag == 0xfffe && chunk_header.ckSize == 40) ? + WaveHeader.SubFormat : WaveHeader.FormatTag; + + loc_config.bits_per_sample = chunk_header.ckSize == 40 ? + WaveHeader.ValidBitsPerSample : WaveHeader.BitsPerSample; + + if (format != 1 && format != 3) + supported = FALSE; + + if (!WaveHeader.NumChannels || + WaveHeader.BlockAlign / WaveHeader.NumChannels > 4) + supported = FALSE; + + if (loc_config.bits_per_sample < 1 || loc_config.bits_per_sample > 32) + supported = FALSE; + + if (!supported) { + error_line ("%s is an unsupported .WAV format!", infilename); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + + if (chunk_header.ckSize < 40) { + if (WaveHeader.NumChannels <= 2) + loc_config.channel_mask = 0x5 - WaveHeader.NumChannels; + else + loc_config.channel_mask = (1 << WaveHeader.NumChannels) - 1; + } + else + loc_config.channel_mask = WaveHeader.ChannelMask; + + if (format == 3) + loc_config.float_norm_exp = 127; + else if ((loc_config.flags & CONFIG_ADOBE_MODE) && + WaveHeader.BlockAlign / WaveHeader.NumChannels == 4) { + if (WaveHeader.BitsPerSample == 24) + loc_config.float_norm_exp = 127 + 23; + else if (WaveHeader.BitsPerSample == 32) + loc_config.float_norm_exp = 127 + 15; + } + } + else if (!strncmp (chunk_header.ckID, "data", 4)) { + + // on the data chunk, get size and exit loop + + total_samples = chunk_header.ckSize / WaveHeader.BlockAlign; + break; + } + else { // just copy unknown chunks to output file + + int bytes_to_copy = (chunk_header.ckSize + 1) & ~1L; + char *buff = malloc (bytes_to_copy); +#if 0 + error_line ("extra unknown chunk \"%c%c%c%c\" of %d bytes", + chunk_header.ckID [0], chunk_header.ckID [1], chunk_header.ckID [2], + chunk_header.ckID [3], chunk_header.ckSize); +#endif + if (!DoReadFile (infile, buff, bytes_to_copy, &bcount) || + bcount != bytes_to_copy || !WavpackAddWrapper (wpc, buff, bytes_to_copy)) { + error_line ("%s", wpc->error_message); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + free (buff); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + + wrapper_size += bytes_to_copy; + free (buff); + } + } + + loc_config.bytes_per_sample = WaveHeader.BlockAlign / WaveHeader.NumChannels; + loc_config.num_channels = WaveHeader.NumChannels; + loc_config.sample_rate = WaveHeader.SampleRate; + + WavpackSetConfiguration (wpc, &loc_config, total_samples); + + // if we are creating a "correction" file, open it now for writing + + if (out2filename) { + if ((wvc_file.file = fopen (out2filename, "w+b")) == NULL) { + error_line ("can't create correction file!"); + DoCloseHandle (infile); + DoCloseHandle (wv_file.file); + DoDeleteFile (outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + } + + // pack the audio portion of the file now + + result = pack_audio (wpc, infile); + + // if everything went well (and we're not ignoring length) try to read + // anything else that might be appended to the audio data and write that + // to the WavPack metadata as "wrapper" + + if (result == NO_ERROR && !(loc_config.flags & CONFIG_IGNORE_LENGTH)) { + uchar buff [16]; + + while (DoReadFile (infile, buff, sizeof (buff), &bcount) && bcount) + if (!WavpackAddWrapper (wpc, buff, bcount)) { + error_line ("%s", wpc->error_message); + result = HARD_ERROR; + break; + } + } + + DoCloseHandle (infile); // we're now done with input file, so close + + // we're now done with any WavPack blocks, so flush any remaining data + + if (result == NO_ERROR && !WavpackFlushSamples (wpc)) { + error_line ("%s", wpc->error_message); + result = HARD_ERROR; + } + + // if still no errors, check to see if we need to create & write a tag + // (which is NOT stored in regular WavPack blocks) + + if (result == NO_ERROR && config->num_tag_strings) { + int i; + + for (i = 0; i < config->num_tag_strings; ++i) { + int item_len = strchr (config->tag_strings [i], '=') - config->tag_strings [i]; + int value_len = strlen (config->tag_strings [i]) - item_len - 1; + + if (value_len) { + char *item = malloc (item_len + 1); + char *value = malloc (value_len * 2 + 1); + + strncpy (item, config->tag_strings [i], item_len); + item [item_len] = 0; + strcpy (value, config->tag_strings [i] + item_len + 1); + AnsiToUTF8 (value, value_len * 2 + 1); + WavpackAppendTagItem (wpc, item, value); + free (value); + free (item); + } + } + + if (!WavpackWriteTag (wpc)) { + error_line ("%s", wpc->error_message); + result = HARD_ERROR; + } + } + + // At this point we're done writing to the output files. However, in some + // situations we might have to back up and re-write the initial blocks. + // Currently the only case is if we're ignoring length. + + if (result == NO_ERROR && WavpackGetNumSamples (wpc) != WavpackGetSampleIndex (wpc)) { + if (loc_config.flags & CONFIG_IGNORE_LENGTH) { + char *block_buff = malloc (wv_file.first_block_size); + + if (block_buff && !DoSetFilePositionAbsolute (wv_file.file, 0) && + DoReadFile (wv_file.file, block_buff, wv_file.first_block_size, &bcount) && + bcount == wv_file.first_block_size && !strncmp (block_buff, "wvpk", 4)) { + + WavpackUpdateNumSamples (wpc, block_buff); + + if (WavpackGetWrapperLocation (block_buff)) { + RiffChunkHeader *riffhdr = WavpackGetWrapperLocation (block_buff); + ChunkHeader *datahdr = (ChunkHeader *)((char *) riffhdr + wrapper_size - sizeof (ChunkHeader)); + uint32_t data_size = WavpackGetSampleIndex (wpc) * WavpackGetNumChannels (wpc) * WavpackGetBytesPerSample (wpc); + + if (!strncmp (riffhdr->ckID, "RIFF", 4)) { + little_endian_to_native (riffhdr, ChunkHeaderFormat); + riffhdr->ckSize = wrapper_size + data_size; + native_to_little_endian (riffhdr, ChunkHeaderFormat); + } + + if (!strncmp (datahdr->ckID, "data", 4)) { + little_endian_to_native (datahdr, ChunkHeaderFormat); + datahdr->ckSize = data_size; + native_to_little_endian (datahdr, ChunkHeaderFormat); + } + } + + if (DoSetFilePositionAbsolute (wv_file.file, 0) || + !DoWriteFile (wv_file.file, block_buff, wv_file.first_block_size, &bcount) || + bcount != wv_file.first_block_size) { + error_line ("couldn't update WavPack header with actual length!!"); + result = SOFT_ERROR; + } + + free (block_buff); + } + else { + error_line ("couldn't update WavPack header with actual length!!"); + result = SOFT_ERROR; + } + + if (result == NO_ERROR && wvc_file.file) { + block_buff = malloc (wvc_file.first_block_size); + + if (block_buff && !DoSetFilePositionAbsolute (wvc_file.file, 0) && + DoReadFile (wvc_file.file, block_buff, wvc_file.first_block_size, &bcount) && + bcount == wvc_file.first_block_size && !strncmp (block_buff, "wvpk", 4)) { + + WavpackUpdateNumSamples (wpc, block_buff); + + if (DoSetFilePositionAbsolute (wvc_file.file, 0) || + !DoWriteFile (wvc_file.file, block_buff, wvc_file.first_block_size, &bcount) || + bcount != wvc_file.first_block_size) { + error_line ("couldn't update WavPack header with actual length!!"); + result = SOFT_ERROR; + } + } + else { + error_line ("couldn't update WavPack header with actual length!!"); + result = SOFT_ERROR; + } + + free (block_buff); + } + + if (result == NO_ERROR) + error_line ("warning: length was %s by %d samples, corrected", + WavpackGetSampleIndex (wpc) < total_samples ? "short" : "long", + abs (total_samples - WavpackGetSampleIndex (wpc))); + } + else { + error_line ("couldn't read all samples, file may be corrupt!!"); + result = SOFT_ERROR; + } + } + + // at this point we're done with the files, so close 'em whether there + // were any other errors or not + + if (!DoCloseHandle (wv_file.file)) { + error_line ("can't close WavPack file!"); + + if (result == NO_ERROR) + result = SOFT_ERROR; + } + + if (out2filename && !DoCloseHandle (wvc_file.file)) { + error_line ("can't close correction file!"); + + if (result == NO_ERROR) + result = SOFT_ERROR; + } + + // if there were any errors, delete the output files, close the context, + // and return the error + + if (result != NO_ERROR) { + DoDeleteFile (outfilename); + + if (out2filename) + DoDeleteFile (out2filename); + + WavpackCloseFile (wpc); + return result; + } + +#if defined (WIN32) + if (result == NO_ERROR && (loc_config.flags & CONFIG_COPY_TIME)) + if (!copy_timestamp (infilename, outfilename) || + (out2filename && !copy_timestamp (infilename, out2filename))) + error_line ("failure copying time stamp!"); +#endif + + // compute and display the time consumed along with some other details of + // the packing operation, and then return NO_ERROR + +#ifdef __BORLANDC__ + gettime (&time2); + dtime = time2.ti_sec * 100.0 + time2.ti_hund + time2.ti_min * 6000.0 + time2.ti_hour * 360000.00; + dtime -= time1.ti_sec * 100.0 + time1.ti_hund + time1.ti_min * 6000.0 + time1.ti_hour * 360000.00; + + if ((dtime /= 100.0) < 0.0) + dtime += 86400.0; +#elif defined(WIN32) + _ftime (&time2); + dtime = time2.time + time2.millitm / 1000.0; + dtime -= time1.time + time1.millitm / 1000.0; +#else + gettimeofday(&time2,&timez); + dtime = time2.tv_sec + time2.tv_usec / 1000000.0; + dtime -= time1.tv_sec + time1.tv_usec / 1000000.0; +#endif + + if ((loc_config.flags & CONFIG_CALC_NOISE) && pack_noise (wpc, NULL) > 0.0) { + int full_scale_bits = WavpackGetBitsPerSample (wpc); + double full_scale_rms = 0.5, sum, peak; + + while (full_scale_bits--) + full_scale_rms *= 2.0; + + full_scale_rms = full_scale_rms * (full_scale_rms - 1.0) * 0.5; + sum = pack_noise (wpc, &peak); + + error_line ("ave noise = %.2f dB, peak noise = %.2f dB", + log10 (sum / WavpackGetNumSamples (wpc) / full_scale_rms) * 10, + log10 (peak / full_scale_rms) * 10); + } + + if (!(loc_config.flags & CONFIG_QUIET_MODE)) { + char *file, *fext, *oper, *cmode, cratio [16] = ""; + + if (outfilename && *outfilename != '-') { + file = FN_FIT (outfilename); + fext = wvc_file.bytes_written ? " (+.wvc)" : ""; + oper = "created"; + } + else { + file = (*infilename == '-') ? "stdin" : FN_FIT (infilename); + fext = ""; + oper = "packed"; + } + + if (WavpackLossyBlocks (wpc)) { + cmode = "lossy"; + + if (WavpackGetAverageBitrate (wpc, TRUE) != 0.0) + sprintf (cratio, ", %d kbps", (int) (WavpackGetAverageBitrate (wpc, TRUE) / 1000.0)); + } + else { + cmode = "lossless"; + + if (WavpackGetRatio (wpc) != 0.0) + sprintf (cratio, ", %.2f%%", 100.0 - WavpackGetRatio (wpc) * 100.0); + } + + error_line ("%s %s%s in %.2f secs (%s%s)", oper, file, fext, dtime, cmode, cratio); + } + + WavpackCloseFile (wpc); + return NO_ERROR; +} + +// This function handles the actual audio data compression. It assumes that the +// input file is positioned at the beginning of the audio data and that the +// WavPack configuration has been set. This is where the conversion from RIFF +// little-endian standard the executing processor's format is done and where +// (if selected) the MD5 sum is calculated and displayed. + +#define INPUT_SAMPLES 65536 + +static int pack_audio (WavpackContext *wpc, FILE *infile) +{ + uint32_t samples_remaining, samples_read = 0; + double progress = -1.0; + int bytes_per_sample; + int32_t *sample_buffer; + uchar *input_buffer; + MD5_CTX md5_context; + + if (wpc->config.flags & CONFIG_MD5_CHECKSUM) + MD5Init (&md5_context); + + WavpackPackInit (wpc); + bytes_per_sample = WavpackGetBytesPerSample (wpc) * WavpackGetNumChannels (wpc); + input_buffer = malloc (INPUT_SAMPLES * bytes_per_sample); + sample_buffer = malloc (INPUT_SAMPLES * sizeof (int32_t) * WavpackGetNumChannels (wpc)); + samples_remaining = WavpackGetNumSamples (wpc); + + while (1) { + uint32_t bytes_to_read, bytes_read = 0; + uint sample_count; + + if ((wpc->config.flags & CONFIG_IGNORE_LENGTH) || samples_remaining > INPUT_SAMPLES) + bytes_to_read = INPUT_SAMPLES * bytes_per_sample; + else + bytes_to_read = samples_remaining * bytes_per_sample; + + samples_remaining -= bytes_to_read / bytes_per_sample; + DoReadFile (infile, input_buffer, bytes_to_read, &bytes_read); + samples_read += sample_count = bytes_read / bytes_per_sample; + + if (wpc->config.flags & CONFIG_MD5_CHECKSUM) + MD5Update (&md5_context, input_buffer, bytes_read); + + if (!sample_count) + break; + + if (sample_count) { + uint cnt = sample_count * WavpackGetNumChannels (wpc); + uchar *sptr = input_buffer; + int32_t *dptr = sample_buffer; + + switch (WavpackGetBytesPerSample (wpc)) { + + case 1: + while (cnt--) + *dptr++ = *sptr++ - 128; + + break; + + case 2: + while (cnt--) { + *dptr++ = sptr [0] | ((int32_t)(char) sptr [1] << 8); + sptr += 2; + } + + break; + + case 3: + while (cnt--) { + *dptr++ = sptr [0] | ((int32_t) sptr [1] << 8) | ((int32_t)(char) sptr [2] << 16); + sptr += 3; + } + + break; + + case 4: + while (cnt--) { + *dptr++ = sptr [0] | ((int32_t) sptr [1] << 8) | ((int32_t) sptr [2] << 16) | ((int32_t)(char) sptr [3] << 24); + sptr += 4; + } + + break; + } + } + + if (!WavpackPackSamples (wpc, sample_buffer, sample_count)) { + error_line ("%s", wpc->error_message); + free (sample_buffer); + free (input_buffer); + return HARD_ERROR; + } + + if (check_break ()) { + fprintf (stderr, "^C\n"); + free (sample_buffer); + free (input_buffer); + return SOFT_ERROR; + } + + if (WavpackGetProgress (wpc) != -1.0 && + progress != floor (WavpackGetProgress (wpc) * 100.0 + 0.5)) { + int nobs = progress == -1.0; + + progress = WavpackGetProgress (wpc); + display_progress (progress); + progress = floor (progress * 100.0 + 0.5); + + if (!(wpc->config.flags & CONFIG_QUIET_MODE)) + fprintf (stderr, "%s%3d%% done...", + nobs ? " " : "\b\b\b\b\b\b\b\b\b\b\b\b", (int) progress); + } + } + + free (sample_buffer); + free (input_buffer); + + if (!WavpackFlushSamples (wpc)) { + error_line ("%s", wpc->error_message); + return HARD_ERROR; + } + + if (wpc->config.flags & CONFIG_MD5_CHECKSUM) { + char md5_string [] = "original md5 signature: 00000000000000000000000000000000"; + uchar md5_digest [16]; + int i; + + MD5Final (md5_digest, &md5_context); + + for (i = 0; i < 16; ++i) + sprintf (md5_string + 24 + (i * 2), "%02x", md5_digest [i]); + + if (!(wpc->config.flags & CONFIG_QUIET_MODE)) + error_line (md5_string); + + WavpackStoreMD5Sum (wpc, md5_digest); + } + + return NO_ERROR; +} + +////////////////////////////////////////////////////////////////////////////// +// This function displays the progress status on the title bar of the DOS // +// window that WavPack is running in. The "file_progress" argument is for // +// the current file only and ranges from 0 - 1; this function takes into // +// account the total number of files to generate a batch progress number. // +////////////////////////////////////////////////////////////////////////////// + +static void display_progress (double file_progress) +{ + char title [40]; + + file_progress = (file_index + file_progress) / num_files; + sprintf (title, "%d%% (WavPack)", (int) ((file_progress * 100.0) + 0.5)); + SetConsoleTitle (title); +} diff --git a/Libraries/WavPack/Files/wavpack.h b/Libraries/WavPack/Files/wavpack.h new file mode 100644 index 000000000..e2c4274ac --- /dev/null +++ b/Libraries/WavPack/Files/wavpack.h @@ -0,0 +1,656 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wavpack.h + +#ifndef WAVPACK_H +#define WAVPACK_H + +#if defined(WIN32) +#define FASTCALL __fastcall +#else +#define FASTCALL +#define SetConsoleTitle(x) +#endif + +#include + +// This header file contains all the definitions required by WavPack. + +#if defined(_WIN32) && !defined(__MINGW32__) +#include +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +typedef float float32_t; +#else +#include +#endif + +typedef unsigned char uchar; + +#if !defined(__GNUC__) || defined(WIN32) +typedef unsigned short ushort; +typedef unsigned int uint; +#endif + +#ifndef PATH_MAX +#ifdef MAX_PATH +#define PATH_MAX MAX_PATH +#elif defined (MAXPATHLEN) +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX 1024 +#endif +#endif + +// This structure is used to access the individual fields of 32-bit ieee +// floating point numbers. This will not be compatible with compilers that +// allocate bit fields from the most significant bits, although I'm not sure +// how common that is. + +typedef struct { + unsigned mantissa : 23; + unsigned exponent : 8; + unsigned sign : 1; +} f32; + +#include + +#define FALSE 0 +#define TRUE 1 + +#if defined(WIN32) +#undef VERSION_OS +#define VERSION_OS "Win32" +#endif +#define VERSION_STR "4.2 " +#define DATE_STR "2005-04-02" + +// ID3v1 and APEv2 TAG formats (may occur at the end of WavPack files) + +typedef struct { + uchar tag_id [3], title [30], artist [30], album [30]; + uchar year [4], comment [30], genre; +} ID3_Tag; + +typedef struct { + char ID [8]; + int32_t version, length, item_count, flags; + char res [8]; +} APE_Tag_Hdr; + +#define APE_Tag_Hdr_Format "8LLLL" + +typedef struct { + ID3_Tag id3_tag; + APE_Tag_Hdr ape_tag_hdr; + char *ape_tag_data; +} M_Tag; + +// RIFF / wav header formats (these occur at the beginning of both wav files +// and pre-4.0 WavPack files that are not in the "raw" mode) + +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; + ushort cbSize, ValidBitsPerSample; + int32_t ChannelMask; + ushort SubFormat; + char GUID [14]; +} WaveHeader; + +#define WaveHeaderFormat "SSLLSSSSLS" + +////////////////////////////// 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 IGNORED_FLAGS 0x18000000 // reserved, but ignore if encountered +#define NEW_SHAPING 0x20000000 // use IIR filter for negative shaping +#define UNKNOWN_FLAGS 0xC0000000 // also reserved, but refuse decode if + // encountered + +//////////////////////////// WavPack Metadata ///////////////////////////////// + +// This is an internal representation of metadata. + +typedef struct { + 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 { + float bitrate, shaping_weight; + int bits_per_sample, bytes_per_sample; + int qmode, flags, xmode, num_channels, float_norm_exp; + int32_t block_samples, extra_flags, sample_rate, channel_mask; + uchar md5_checksum [16], md5_read; + int num_tag_strings; + char **tag_strings; +} WavpackConfig; + +#define CONFIG_BYTES_STORED 3 // 1-4 bytes/sample +#define CONFIG_MONO_FLAG 4 // not stereo +#define CONFIG_HYBRID_FLAG 8 // hybrid mode +#define CONFIG_JOINT_STEREO 0x10 // joint stereo +#define CONFIG_CROSS_DECORR 0x20 // no-delay cross decorrelation +#define CONFIG_HYBRID_SHAPE 0x40 // noise shape (hybrid mode only) +#define CONFIG_FLOAT_DATA 0x80 // ieee 32-bit floating point data + +#define CONFIG_ADOBE_MODE 0x100 // "adobe" mode for 32-bit floats +#define CONFIG_FAST_FLAG 0x200 // fast mode +#define CONFIG_VERY_FAST_FLAG 0x400 // double fast +#define CONFIG_HIGH_FLAG 0x800 // high quality mode +#define CONFIG_VERY_HIGH_FLAG 0x1000 // double high (not used yet) +#define CONFIG_BITRATE_KBPS 0x2000 // bitrate is kbps, not bits / sample +#define CONFIG_AUTO_SHAPING 0x4000 // automatic noise shaping +#define CONFIG_SHAPE_OVERRIDE 0x8000 // shaping mode specified +#define CONFIG_JOINT_OVERRIDE 0x10000 // joint-stereo mode specified +#define CONFIG_COPY_TIME 0x20000 // copy file-time from source +#define CONFIG_CREATE_EXE 0x40000 // create executable +#define CONFIG_CREATE_WVC 0x80000 // create correction file +#define CONFIG_OPTIMIZE_WVC 0x100000 // maximize bybrid compression +#define CONFIG_QUALITY_MODE 0x200000 // psychoacoustic quality mode +#define CONFIG_RAW_FLAG 0x400000 // raw mode (not implemented yet) +#define CONFIG_CALC_NOISE 0x800000 // calc noise in hybrid mode +#define CONFIG_LOSSY_MODE 0x1000000 // obsolete (for information) +#define CONFIG_EXTRA_MODE 0x2000000 // extra processing mode +#define CONFIG_SKIP_WVX 0x4000000 // no wvx stream w/ floats & big ints +#define CONFIG_MD5_CHECKSUM 0x8000000 // compute & store MD5 signature +#define CONFIG_QUIET_MODE 0x10000000 // don't report progress % +#define CONFIG_IGNORE_LENGTH 0x20000000 // ignore length in wav header + +#define EXTRA_SCAN_ONLY 1 +#define EXTRA_STEREO_MODES 2 +//#define EXTRA_CHECK_TERMS 4 +#define EXTRA_TRY_DELTAS 8 +#define EXTRA_ADJUST_DELTAS 16 +#define EXTRA_SORT_FIRST 32 +#define EXTRA_BRANCHES 0x1c0 +#define EXTRA_SKIP_8TO16 512 +#define EXTRA_TERMS 0x3c00 +#define EXTRA_DUMP_TERMS 16384 +#define EXTRA_SORT_LAST 32768 + +//////////////////////////////// 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 struct bs { + uchar *buf, *end, *ptr; + void (*wrap)(struct bs *bs); + int error, bc; + uint32_t sr; +} Bitstream; + +#define MAX_STREAMS 8 +#define MAX_NTERMS 16 +#define MAX_TERM 8 + +struct decorr_pass { + int term, delta, weight_A, weight_B; + int32_t samples_A [MAX_TERM], samples_B [MAX_TERM]; + int32_t aweight_A, aweight_B; +#ifdef PACK + int32_t sum_A, sum_B, min, max; +#endif +}; + +typedef struct { + WavpackHeader wphdr; + + uchar *blockbuff, *blockend; + uchar *block2buff, *block2end; + int32_t *sample_buffer; + + uint32_t sample_index, crc, crc_x, crc_wvx; + Bitstream wvbits, wvcbits, wvxbits; + int bits, num_terms, mute_error; + float delta_decay; + + uchar int32_sent_bits, int32_zeros, int32_ones, int32_dups; + uchar float_flags, float_shift, float_max_exp, float_norm_exp; + + struct { + int32_t shaping_acc [2], shaping_delta [2], error [2]; + double noise_sum, noise_ave, noise_max; + } dc; + + struct decorr_pass decorr_passes [MAX_NTERMS]; + + struct { + uint32_t bitrate_delta [2], bitrate_acc [2]; + uint32_t median [3] [2], slow_level [2], error_limit [2]; + uint32_t pend_data, holding_one, zeros_acc; + int holding_zero, pend_count; + } w; +} WavpackStream; + +// flags for float_flags: + +#define FLOAT_SHIFT_ONES 1 // bits left-shifted into float = '1' +#define FLOAT_SHIFT_SAME 2 // bits left-shifted into float are the same +#define FLOAT_SHIFT_SENT 4 // bits shifted into float are sent literally +#define FLOAT_ZEROS_SENT 8 // "zeros" are not all real zeros +#define FLOAT_NEG_ZEROS 0x10 // contains negative zeros +#define FLOAT_EXCEPTIONS 0x20 // contains exceptions (inf, nan, etc.) + +/////////////////////////////// WavPack Context /////////////////////////////// + +// This internal structure holds everything required to encode or decode WavPack +// files. It is recommended that direct access to this structure be minimized +// and the provided utilities used instead. + +typedef struct { + int32_t (*read_bytes)(void *id, void *data, int32_t bcount); + uint32_t (*get_pos)(void *id); + int (*set_pos_abs)(void *id, uint32_t pos); + int (*set_pos_rel)(void *id, int32_t delta, int mode); + int (*push_back_byte)(void *id, int c); + uint32_t (*get_length)(void *id); + int (*can_seek)(void *id); +} stream_reader; + +typedef int (*blockout)(void *id, void *data, int32_t bcount); + +typedef struct { + WavpackConfig config; + + WavpackMetadata *metadata; + uint32_t metabytes; + int metacount; + + uchar *wrapper_data; + uint32_t wrapper_bytes; + + blockout blockout; + void *wv_out, *wvc_out; + + stream_reader *reader; + void *wv_in, *wvc_in; + + uint32_t filelen, file2len, filepos, file2pos, total_samples, crc_errors, first_flags; + int wvc_flag, open_flags, norm_offset, reduced_channels, lossy_blocks, close_files; + int block_samples, max_samples, acc_samples; + M_Tag m_tag; + + int current_stream, num_streams; + WavpackStream *streams [8]; + void *stream3; + + char error_message [80]; +} WavpackContext; + +//////////////////////// function prototypes and macros ////////////////////// + +#define CLEAR(destin) memset (&destin, 0, sizeof (destin)); + +// these macros implement the weight application and update operations +// that are at the heart of the decorrelation loops + +#define apply_weight_i(weight, sample) ((weight * sample + 512) >> 10) + +#define apply_weight_f(weight, sample) (((((sample & 0xffff) * weight) >> 9) + \ + (((sample & ~0xffff) >> 9) * weight) + 1) >> 1) + +#if 1 // 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 1 // PERFCOND +#define update_weight(weight, delta, source, result) \ + if (source && result) weight -= ((((source ^ result) >> 30) & 2) - 1) * delta; +#else +#define update_weight(weight, delta, source, result) \ + if (source && result) (source ^ result) < 0 ? (weight -= delta) : (weight += delta); +#endif + +#define update_weight_d1(weight, delta, source, result) \ + if (source && result) weight -= (((source ^ result) >> 30) & 2) - 1; + +#define update_weight_d2(weight, delta, source, result) \ + if (source && result) weight -= (((source ^ result) >> 29) & 4) - 2; + +#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; + +#define update_weight_clip_d1(weight, delta, source, result) \ + if (source && result && abs (weight -= (((source ^ result) >> 30) & 2) - 1) > 1024) \ + weight = weight < 0 ? -1024 : 1024; + +#define update_weight_clip_d2(weight, delta, source, result) \ + if (source && result && abs (weight -= (((source ^ result) >> 29) & 4) - 2) > 1024) \ + weight = weight < 0 ? -1024 : 1024; + +// bits.c + +void bs_open_read (Bitstream *bs, uchar *buffer_start, uchar *buffer_end); +void bs_open_write (Bitstream *bs, uchar *buffer_start, uchar *buffer_end); +uint32_t bs_close_read (Bitstream *bs); +uint32_t bs_close_write (Bitstream *bs); + +int DoReadFile (FILE *hFile, void *lpBuffer, uint32_t nNumberOfBytesToRead, uint32_t *lpNumberOfBytesRead); +int DoWriteFile (FILE *hFile, void *lpBuffer, uint32_t nNumberOfBytesToWrite, uint32_t *lpNumberOfBytesWritten); +uint32_t DoGetFileSize (FILE *hFile), DoGetFilePosition (FILE *hFile); +int DoSetFilePositionRelative (FILE *hFile, int32_t pos, int mode); +int DoSetFilePositionAbsolute (FILE *hFile, uint32_t pos); +int DoUngetc (int c, FILE *hFile), DoDeleteFile (char *filename); +int DoCloseHandle (FILE *hFile), DoTruncateFile (FILE *hFile); + +#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; \ + (bs)->sr >>= (nbits); \ + (bs)->bc -= (nbits); \ +} + +#define putbit(bit, bs) { if (bit) (bs)->sr |= (1 << (bs)->bc); \ + if (++((bs)->bc) == 8) { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr = (bs)->bc = 0; \ + if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \ + }} + +#define putbit_0(bs) { \ + if (++((bs)->bc) == 8) { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr = (bs)->bc = 0; \ + if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \ + }} + +#define putbit_1(bs) { (bs)->sr |= (1 << (bs)->bc); \ + if (++((bs)->bc) == 8) { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr = (bs)->bc = 0; \ + if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \ + }} + +#define putbits(value, nbits, bs) { \ + (bs)->sr |= (int32_t)(value) << (bs)->bc; \ + if (((bs)->bc += (nbits)) >= 8) \ + do { \ + *((bs)->ptr) = (bs)->sr; \ + (bs)->sr >>= 8; \ + if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \ + } while (((bs)->bc -= 8) >= 8); \ +} + +void little_endian_to_native (void *data, char *format); +void native_to_little_endian (void *data, char *format); + +// pack.c + +void pack_init (WavpackContext *wpc); +int pack_block (WavpackContext *wpc, int32_t *buffer); +double pack_noise (WavpackContext *wpc, double *peak); + +// unpack.c + +int unpack_init (WavpackContext *wpc); +int init_wv_bitstream (WavpackStream *wps, WavpackMetadata *wpmd); +int init_wvc_bitstream (WavpackStream *wps, WavpackMetadata *wpmd); +int init_wvx_bitstream (WavpackStream *wps, 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_shaping_info (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); +int read_wrapper_data (WavpackContext *wpc, WavpackMetadata *wpmd); +int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); +int check_crc_error (WavpackContext *wpc); + +// unpack3.c + +WavpackContext *open_file3 (WavpackContext *wpc, char *error); +int32_t unpack_samples3 (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count); +int seek_sample3 (WavpackContext *wpc, uint32_t desired_index); +uint32_t get_sample_index3 (WavpackContext *wpc); +void free_stream3 (WavpackContext *wpc); +int get_version3 (WavpackContext *wpc); + +// utils.c + +int copy_timestamp (const char *src_filename, const char *dst_filename); +char *filespec_ext (char *filespec), *filespec_path (char *filespec); +char *filespec_name (char *filespec), *filespec_wild (char *filespec); +void error_line (char *error, ...), finish_line (void); +void setup_break (void); +int check_break (void); +char yna (void); +void AnsiToUTF8 (char *string, int len); + +#define FN_FIT(fn) ((strlen (fn) > 30) ? filespec_name (fn) : fn) + +// metadata.c stuff + +int read_metadata_buff (WavpackMetadata *wpmd, uchar *blockbuff, uchar **buffptr); +int write_metadata_block (WavpackContext *wpc); +int copy_metadata (WavpackMetadata *wpmd, uchar *buffer_start, uchar *buffer_end); +int add_to_metadata (WavpackContext *wpc, void *data, uint32_t bcount, uchar id); +int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd); +void free_metadata (WavpackMetadata *wpmd); + +// words.c stuff + +void init_words (WavpackStream *wps); +void word_set_bitrate (WavpackStream *wps); +void write_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd); +void write_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd); +int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd); +int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd); +int32_t FASTCALL send_word (WavpackStream *wps, int32_t value, int chan); +void FASTCALL send_word_lossless (WavpackStream *wps, int32_t value, int chan); +int32_t FASTCALL get_word (WavpackStream *wps, int chan, int32_t *correction); +int32_t FASTCALL get_word_lossless (WavpackStream *wps, int chan); +void flush_word (WavpackStream *wps); +int32_t nosend_word (WavpackStream *wps, int32_t value, int chan); +void scan_word (WavpackStream *wps, int32_t *samples, uint32_t num_samples, int dir); + +int log2s (int32_t value); +int32_t exp2s (int log); +uint32_t log2buffer (int32_t *samples, uint32_t num_samples); + +char store_weight (int weight); +int restore_weight (char weight); + +#define WORD_EOF (1L << 31) + +// float.c + +void write_float_info (WavpackStream *wps, WavpackMetadata *wpmd); +int scan_float_data (WavpackStream *wps, f32 *values, int32_t num_values); +void send_float_data (WavpackStream *wps, f32 *values, int32_t num_values); +int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd); +void float_values (WavpackStream *wps, int32_t *values, int32_t num_values); +void float_normalize (int32_t *values, int32_t num_values, int delta_exp); + +// analyze?.c + +void analyze_stereo (WavpackContext *wpc, int32_t *samples); +void analyze_mono (WavpackContext *wpc, int32_t *samples); + +// wputils.c + +WavpackContext *WavpackOpenFileInputEx (stream_reader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset); +WavpackContext *WavpackOpenFileInput (const char *infilename, char *error, int flags, int norm_offset); + +#define OPEN_WVC 0x1 // open/read "correction" file +#define OPEN_TAGS 0x2 // read ID3v1 / APEv2 tags (seekable file) +#define OPEN_WRAPPER 0x4 // make audio wrapper available (i.e. RIFF) +#define OPEN_2CH_MAX 0x8 // open multichannel as stereo (no downmix) +#define OPEN_NORMALIZE 0x10 // normalize floating point data to +/- 1.0 +#define OPEN_STREAMING 0x20 // "streaming" mode blindly unpacks blocks + // w/o regard to header file position info + +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 +#define MODE_EXTRA 0x80 +#define MODE_APETAG 0x100 +#define MODE_SFX 0x200 + +int WavpackGetVersion (WavpackContext *wpc); +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); +int WavpackSeekSample (WavpackContext *wpc, uint32_t sample); +WavpackContext *WavpackCloseFile (WavpackContext *wpc); +uint32_t WavpackGetSampleRate (WavpackContext *wpc); +int WavpackGetBitsPerSample (WavpackContext *wpc); +int WavpackGetBytesPerSample (WavpackContext *wpc); +int WavpackGetNumChannels (WavpackContext *wpc); +int WavpackGetReducedChannels (WavpackContext *wpc); +int WavpackGetMD5Sum (WavpackContext *wpc, uchar data [16]); +uint32_t WavpackGetWrapperBytes (WavpackContext *wpc); +uchar *WavpackGetWrapperData (WavpackContext *wpc); +void WavpackFreeWrapper (WavpackContext *wpc); +double WavpackGetProgress (WavpackContext *wpc); +uint32_t WavpackGetFileSize (WavpackContext *wpc); +double WavpackGetRatio (WavpackContext *wpc); +double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc); +double WavpackGetInstantBitrate (WavpackContext *wpc); +int WavpackGetTagItem (WavpackContext *wpc, const char *item, char *value, int size); +int WavpackAppendTagItem (WavpackContext *wpc, const char *item, const char *value); +int WavpackWriteTag (WavpackContext *wpc); + +WavpackContext *WavpackOpenFileOutput (blockout blockout, void *wv_id, void *wvc_id); +int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, uint32_t total_samples); +int WavpackAddWrapper (WavpackContext *wpc, void *data, uint32_t bcount); +int WavpackStoreMD5Sum (WavpackContext *wpc, uchar data [16]); +int WavpackPackInit (WavpackContext *wpc); +int WavpackPackSamples (WavpackContext *wpc, int32_t *sample_buffer, uint32_t sample_count); +int WavpackFlushSamples (WavpackContext *wpc); +void WavpackUpdateNumSamples (WavpackContext *wpc, void *first_block); +void *WavpackGetWrapperLocation (void *first_block); + +#endif diff --git a/Libraries/WavPack/Files/wavpack.pc.in b/Libraries/WavPack/Files/wavpack.pc.in new file mode 100644 index 000000000..553127ed7 --- /dev/null +++ b/Libraries/WavPack/Files/wavpack.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: wavpack +Description: wavpack library +Version: @PACKAGE_VERSION@ +Requires: +Conflicts: +Libs: -L${libdir} -lwavpack @ICONV@ +Cflags: -I${includedir} -DPACK -DUNPACK -DUSE_FSTREAMS -DTAGS -DSEEKING -DVER3 diff --git a/Libraries/WavPack/Files/words.c b/Libraries/WavPack/Files/words.c new file mode 100644 index 000000000..4b8fde6c3 --- /dev/null +++ b/Libraries/WavPack/Files/words.c @@ -0,0 +1,1437 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +//////////////////////////////////////////////////////////////////////////// + +// words.c + +// This module provides entropy word encoding and decoding functions using +// a variation on the Rice method. This was introduced in version 3.93 +// because it allows splitting the data into a "lossy" stream and a +// "correction" stream in a very efficient manner and is therefore ideal +// for the "hybrid" mode. For 4.0, the efficiency of this method was +// significantly improved by moving away from the normal Rice restriction of +// using powers of two for the modulus divisions and now the method can be +// used for both hybrid and pure lossless encoding. + +// Samples are divided by median probabilities at 5/7 (71.43%), 10/49 (20.41%), +// and 20/343 (5.83%). Each zone has 3.5 times fewer samples than the +// previous. Using standard Rice coding on this data would result in 1.4 +// bits per sample average (not counting sign bit). However, there is a +// very simple encoding that is over 99% efficient with this data and +// results in about 1.22 bits per sample. + +#include "wavpack.h" + +#include +#include + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +#endif + +// #define DEBUG_WORDS // debug module by sending all 32 bits literally + +//////////////////////////////// local macros ///////////////////////////////// + +#define LIMIT_ONES 16 // maximum consecutive 1s sent for "div" data + +// these control the time constant "slow_level" which is used for hybrid mode +// that controls bitrate as a function of residual level (HYBRID_BITRATE). +#define SLS 8 +#define SLO ((1 << (SLS - 1))) + +// these control the time constant of the 3 median level breakpoints +#define DIV0 128 // 5/7 of samples +#define DIV1 64 // 10/49 of samples +#define DIV2 32 // 20/343 of samples + +// this macro retrieves the specified median breakpoint (without frac; min = 1) +#define GET_MED(med) (((wps->w.median [med] [chan]) >> 4) + 1) + +// These macros update the specified median breakpoints. Note that the median +// is incremented when the sample is higher than the median, else decremented. +// They are designed so that the median will never drop below 1 and the value +// is essentially stationary if there are 2 increments for every 5 decrements. + +#define INC_MED0() (wps->w.median [0] [chan] += ((wps->w.median [0] [chan] + DIV0) / DIV0) * 5) +#define DEC_MED0() (wps->w.median [0] [chan] -= ((wps->w.median [0] [chan] + (DIV0-2)) / DIV0) * 2) +#define INC_MED1() (wps->w.median [1] [chan] += ((wps->w.median [1] [chan] + DIV1) / DIV1) * 5) +#define DEC_MED1() (wps->w.median [1] [chan] -= ((wps->w.median [1] [chan] + (DIV1-2)) / DIV1) * 2) +#define INC_MED2() (wps->w.median [2] [chan] += ((wps->w.median [2] [chan] + DIV2) / DIV2) * 5) +#define DEC_MED2() (wps->w.median [2] [chan] -= ((wps->w.median [2] [chan] + (DIV2-2)) / DIV2) * 2) + +#define count_bits(av) ( \ + (av) < (1 << 8) ? nbits_table [av] : \ + ( \ + (av) < (1L << 16) ? nbits_table [(av) >> 8] + 8 : \ + ((av) < (1L << 24) ? nbits_table [(av) >> 16] + 16 : nbits_table [(av) >> 24] + 24) \ + ) \ +) + +///////////////////////////// local table storage //////////////////////////// + +const uint32_t bitset [] = { + 1L << 0, 1L << 1, 1L << 2, 1L << 3, + 1L << 4, 1L << 5, 1L << 6, 1L << 7, + 1L << 8, 1L << 9, 1L << 10, 1L << 11, + 1L << 12, 1L << 13, 1L << 14, 1L << 15, + 1L << 16, 1L << 17, 1L << 18, 1L << 19, + 1L << 20, 1L << 21, 1L << 22, 1L << 23, + 1L << 24, 1L << 25, 1L << 26, 1L << 27, + 1L << 28, 1L << 29, 1L << 30, 1L << 31 +}; + +const uint32_t bitmask [] = { + (1L << 0) - 1, (1L << 1) - 1, (1L << 2) - 1, (1L << 3) - 1, + (1L << 4) - 1, (1L << 5) - 1, (1L << 6) - 1, (1L << 7) - 1, + (1L << 8) - 1, (1L << 9) - 1, (1L << 10) - 1, (1L << 11) - 1, + (1L << 12) - 1, (1L << 13) - 1, (1L << 14) - 1, (1L << 15) - 1, + (1L << 16) - 1, (1L << 17) - 1, (1L << 18) - 1, (1L << 19) - 1, + (1L << 20) - 1, (1L << 21) - 1, (1L << 22) - 1, (1L << 23) - 1, + (1L << 24) - 1, (1L << 25) - 1, (1L << 26) - 1, (1L << 27) - 1, + (1L << 28) - 1, (1L << 29) - 1, (1L << 30) - 1, 0x7fffffff +}; + +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 //////////////////////////////// + +static int FASTCALL mylog2 (uint32_t avalue); + +// Initialize entropy encoder for the specified stream. In lossless mode there +// are no parameters to select; in hybrid mode the bitrate mode and value need +// be initialized. + +#ifdef PACK + +void init_words (WavpackStream *wps) +{ + CLEAR (wps->w); + + if (wps->wphdr.flags & HYBRID_FLAG) + word_set_bitrate (wps); +} + +// Set up parameters for hybrid mode based on header flags and "bits" field. +// This is currently only set up for the HYBRID_BITRATE mode in which the +// allowed error varies with the residual level (from "slow_level"). The +// simpler mode (which is not used yet) has the error level directly +// controlled from the metadata. + +void word_set_bitrate (WavpackStream *wps) +{ + int bitrate_0, bitrate_1; + + if (wps->wphdr.flags & HYBRID_BITRATE) { + bitrate_0 = wps->bits < 568 ? 0 : wps->bits - 568; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + + if (wps->wphdr.flags & HYBRID_BALANCE) + bitrate_1 = (wps->wphdr.flags & JOINT_STEREO) ? 256 : 0; + else { + bitrate_1 = bitrate_0; + + if (wps->wphdr.flags & JOINT_STEREO) { + if (bitrate_0 < 128) { + bitrate_1 += bitrate_0; + bitrate_0 = 0; + } + else { + bitrate_0 -= 128; + bitrate_1 += 128; + } + } + } + } + } + else + bitrate_0 = bitrate_1 = 0; + + wps->w.bitrate_acc [0] = (int32_t) bitrate_0 << 16; + wps->w.bitrate_acc [1] = (int32_t) bitrate_1 << 16; +} + +// Allocates the correct space in the metadata structure and writes the +// current median values to it. Values are converted from 32-bit unsigned +// to our internal 16-bit mylog2 values, and read_entropy_vars () is called +// to read the values back because we must compensate for the loss through +// the log function. + +void write_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd) +{ + uchar *byteptr; + int temp; + + byteptr = wpmd->data = malloc (12); + wpmd->id = ID_ENTROPY_VARS; + + *byteptr++ = temp = mylog2 (wps->w.median [0] [0]); + *byteptr++ = temp >> 8; + *byteptr++ = temp = mylog2 (wps->w.median [1] [0]); + *byteptr++ = temp >> 8; + *byteptr++ = temp = mylog2 (wps->w.median [2] [0]); + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + *byteptr++ = temp = mylog2 (wps->w.median [0] [1]); + *byteptr++ = temp >> 8; + *byteptr++ = temp = mylog2 (wps->w.median [1] [1]); + *byteptr++ = temp >> 8; + *byteptr++ = temp = mylog2 (wps->w.median [2] [1]); + *byteptr++ = temp >> 8; + } + + wpmd->byte_length = byteptr - (uchar *) wpmd->data; + read_entropy_vars (wps, wpmd); +} + +// Allocates enough space in the metadata structure and writes the current +// high word of the bitrate accumulator and the slow_level values to it. The +// slow_level values are converted from 32-bit unsigned to our internal 16-bit +// mylog2 values. Afterward, read_entropy_vars () is called to read the values +// back because we must compensate for the loss through the log function and +// the truncation of the bitrate. + +void write_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd) +{ + uchar *byteptr; + int temp; + + word_set_bitrate (wps); + byteptr = wpmd->data = malloc (512); + wpmd->id = ID_HYBRID_PROFILE; + + if (wps->wphdr.flags & HYBRID_BITRATE) { + *byteptr++ = temp = log2s (wps->w.slow_level [0]); + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + *byteptr++ = temp = log2s (wps->w.slow_level [1]); + *byteptr++ = temp >> 8; + } + } + + *byteptr++ = temp = wps->w.bitrate_acc [0] >> 16; + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + *byteptr++ = temp = wps->w.bitrate_acc [1] >> 16; + *byteptr++ = temp >> 8; + } + + if (wps->w.bitrate_delta [0] | wps->w.bitrate_delta [1]) { + *byteptr++ = temp = log2s (wps->w.bitrate_delta [0]); + *byteptr++ = temp >> 8; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + *byteptr++ = temp = log2s (wps->w.bitrate_delta [1]); + *byteptr++ = temp >> 8; + } + } + + wpmd->byte_length = byteptr - (uchar *) wpmd->data; + read_hybrid_profile (wps, wpmd); +} + +#endif + +// Read the median log2 values from the specifed metadata structure, convert +// them back to 32-bit unsigned values and store them. If length is not +// exactly correct then we flag and return an error. + +int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd) +{ + uchar *byteptr = wpmd->data; + + if (wpmd->byte_length != ((wps->wphdr.flags & MONO_FLAG) ? 6 : 12)) + return FALSE; + + wps->w.median [0] [0] = exp2s (byteptr [0] + (byteptr [1] << 8)); + wps->w.median [1] [0] = exp2s (byteptr [2] + (byteptr [3] << 8)); + wps->w.median [2] [0] = exp2s (byteptr [4] + (byteptr [5] << 8)); + + if (!(wps->wphdr.flags & MONO_FLAG)) { + wps->w.median [0] [1] = exp2s (byteptr [6] + (byteptr [7] << 8)); + wps->w.median [1] [1] = exp2s (byteptr [8] + (byteptr [9] << 8)); + wps->w.median [2] [1] = exp2s (byteptr [10] + (byteptr [11] << 8)); + } + + return TRUE; +} + +// Read the hybrid related values from the specifed metadata structure, convert +// them back to their internal formats and store them. The extended profile +// stuff is not implemented yet, so return an error if we get more data than +// we know what to do with. + +int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd) +{ + uchar *byteptr = wpmd->data; + uchar *endptr = byteptr + wpmd->byte_length; + + if (wps->wphdr.flags & HYBRID_BITRATE) { + wps->w.slow_level [0] = exp2s (byteptr [0] + (byteptr [1] << 8)); + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + wps->w.slow_level [1] = exp2s (byteptr [0] + (byteptr [1] << 8)); + byteptr += 2; + } + } + + wps->w.bitrate_acc [0] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16; + byteptr += 2; + + if (!(wps->wphdr.flags & MONO_FLAG)) { + 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_FLAG)) { + wps->w.bitrate_delta [1] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + byteptr += 2; + } + + if (byteptr < endptr) + return FALSE; + } + else + wps->w.bitrate_delta [0] = wps->w.bitrate_delta [1] = 0; + + return TRUE; +} + +// This function is called during both encoding and decoding of hybrid data to +// update the "error_limit" variable which determines the maximum sample error +// allowed in the main bitstream. In the HYBRID_BITRATE mode (which is the only +// currently implemented) this is calculated from the slow_level values and the +// bitrate accumulators. Note that the bitrate accumulators can be changing. + +static void update_error_limit (WavpackStream *wps) +{ + int bitrate_0 = (wps->w.bitrate_acc [0] += wps->w.bitrate_delta [0]) >> 16; + + if (wps->wphdr.flags & MONO_FLAG) { + if (wps->wphdr.flags & HYBRID_BITRATE) { + int slow_log_0 = (wps->w.slow_level [0] + SLO) >> SLS; + + if (slow_log_0 - bitrate_0 > -0x100) + wps->w.error_limit [0] = exp2s (slow_log_0 - bitrate_0 + 0x100); + else + wps->w.error_limit [0] = 0; + } + else + wps->w.error_limit [0] = exp2s (bitrate_0); + } + else { + int bitrate_1 = (wps->w.bitrate_acc [1] += wps->w.bitrate_delta [1]) >> 16; + + if (wps->wphdr.flags & HYBRID_BITRATE) { + int slow_log_0 = (wps->w.slow_level [0] + SLO) >> SLS; + int slow_log_1 = (wps->w.slow_level [1] + SLO) >> SLS; + + if (wps->wphdr.flags & HYBRID_BALANCE) { + int balance = (slow_log_1 - slow_log_0 + bitrate_1 + 1) >> 1; + + if (balance > bitrate_0) { + bitrate_1 = bitrate_0 * 2; + bitrate_0 = 0; + } + else if (-balance > bitrate_0) { + bitrate_0 = bitrate_0 * 2; + bitrate_1 = 0; + } + else { + bitrate_1 = bitrate_0 + balance; + bitrate_0 = bitrate_0 - balance; + } + } + + if (slow_log_0 - bitrate_0 > -0x100) + wps->w.error_limit [0] = exp2s (slow_log_0 - bitrate_0 + 0x100); + else + wps->w.error_limit [0] = 0; + + if (slow_log_1 - bitrate_1 > -0x100) + wps->w.error_limit [1] = exp2s (slow_log_1 - bitrate_1 + 0x100); + else + wps->w.error_limit [1] = 0; + } + else { + wps->w.error_limit [0] = exp2s (bitrate_0); + wps->w.error_limit [1] = exp2s (bitrate_1); + } + } +} + +#ifdef PACK + +// This function writes the specified word to the open bitstream "wvbits" and, +// if the bitstream "wvcbits" is open, writes any correction data there. This +// function will work for either lossless or hybrid but because a version +// optimized for lossless exits below, it would normally be used for the hybrid +// mode only. The return value is the actual value stored to the stream (even +// if a correction file is being created) and is used as feedback to the +// predictor. + +int32_t FASTCALL send_word (WavpackStream *wps, int32_t value, int chan) +{ + uint32_t ones_count, low, mid, high; + int sign = (value < 0) ? 1 : 0; + +#ifdef DEBUG_WORDS + mid = value; + ones_count = 32; + while (ones_count--) { + putbit (value & 1, &wps->wvbits); + value >>= 1; + } + return mid; +#endif + + if (!(wps->w.median [0] [0] & ~1) && !wps->w.holding_zero && !(wps->w.median [0] [1] & ~1)) { + if (wps->w.zeros_acc) { + if (value) + flush_word (wps); + else { + wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS; + wps->w.zeros_acc++; + return 0; + } + } + else if (value) { + putbit_0 (&wps->wvbits); + } + else { + wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS; + CLEAR (wps->w.median); + wps->w.zeros_acc = 1; + return 0; + } + } + + if (sign) + value = ~value; + + if ((wps->wphdr.flags & HYBRID_FLAG) && !chan) + update_error_limit (wps); + + if (value < GET_MED (0)) { + ones_count = low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + ones_count = 1; + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + ones_count = 2; + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + ones_count = 2 + (value - low) / GET_MED (2); + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + mid = (high + low + 1) >> 1; + + if (wps->w.holding_zero) { + if (ones_count) + wps->w.holding_one++; + + flush_word (wps); + + if (ones_count) { + wps->w.holding_zero = 1; + ones_count--; + } + else + wps->w.holding_zero = 0; + } + else + wps->w.holding_zero = 1; + + wps->w.holding_one = ones_count * 2; + + if (!wps->w.error_limit [chan]) { + if (high != low) { + uint32_t maxcode = high - low, code = value - low; + int bitcount = count_bits (maxcode); + uint32_t extras = bitset [bitcount] - maxcode - 1; + + if (code < extras) { + wps->w.pend_data |= code << wps->w.pend_count; + wps->w.pend_count += bitcount - 1; + } + else { + wps->w.pend_data |= ((code + extras) >> 1) << wps->w.pend_count; + wps->w.pend_count += bitcount - 1; + wps->w.pend_data |= ((code + extras) & 1) << wps->w.pend_count++; + } + } + + mid = value; + } + else + while (high - low > wps->w.error_limit [chan]) + if (value < mid) { + mid = ((high = mid - 1) + low + 1) >> 1; + wps->w.pend_count++; + } + else { + mid = (high + (low = mid) + 1) >> 1; + wps->w.pend_data |= bitset [wps->w.pend_count++]; + } + + wps->w.pend_data |= ((int32_t) sign << wps->w.pend_count++); + + if (!wps->w.holding_zero) + flush_word (wps); + + if (bs_is_open (&wps->wvcbits) && wps->w.error_limit [chan]) { + uint32_t code = value - low, maxcode = high - low; + int bitcount = count_bits (maxcode); + uint32_t extras = bitset [bitcount] - maxcode - 1; + + if (bitcount) { + if (code < extras) { + putbits (code, bitcount - 1, &wps->wvcbits); + } + else { + putbits ((code + extras) >> 1, bitcount - 1, &wps->wvcbits); + putbit ((code + extras) & 1, &wps->wvcbits); + } + } + } + + if (wps->wphdr.flags & HYBRID_BITRATE) { + wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS; + wps->w.slow_level [chan] += mylog2 (mid); + } + + return sign ? ~mid : mid; +} + +// This function is an optimized version of send_word() that only handles +// lossless (error_limit == 0). It does not return a value because it always +// encodes the exact value passed. + +void FASTCALL send_word_lossless (WavpackStream *wps, int32_t value, int chan) +{ + int sign = (value < 0) ? 1 : 0; + uint32_t ones_count, low, high; + +#ifdef DEBUG_WORDS + ones_count = 32; + while (ones_count--) { + putbit (value & 1, &wps->wvbits); + value >>= 1; + } + return; +#endif + + if (!(wps->w.median [0] [0] & ~1) && !wps->w.holding_zero && !(wps->w.median [0] [1] & ~1)) { + if (wps->w.zeros_acc) { + if (value) + flush_word (wps); + else { + wps->w.zeros_acc++; + return; + } + } + else if (value) { + putbit_0 (&wps->wvbits); + } + else { + CLEAR (wps->w.median); + wps->w.zeros_acc = 1; + return; + } + } + + if (sign) + value = ~value; + + if (value < GET_MED (0)) { + ones_count = low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + ones_count = 1; + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + ones_count = 2; + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + ones_count = 2 + (value - low) / GET_MED (2); + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + if (wps->w.holding_zero) { + if (ones_count) + wps->w.holding_one++; + + flush_word (wps); + + if (ones_count) { + wps->w.holding_zero = 1; + ones_count--; + } + else + wps->w.holding_zero = 0; + } + else + wps->w.holding_zero = 1; + + wps->w.holding_one = ones_count * 2; + + if (high != low) { + uint32_t maxcode = high - low, code = value - low; + int bitcount = count_bits (maxcode); + uint32_t extras = bitset [bitcount] - maxcode - 1; + + if (code < extras) { + wps->w.pend_data |= code << wps->w.pend_count; + wps->w.pend_count += bitcount - 1; + } + else { + wps->w.pend_data |= ((code + extras) >> 1) << wps->w.pend_count; + wps->w.pend_count += bitcount - 1; + wps->w.pend_data |= ((code + extras) & 1) << wps->w.pend_count++; + } + } + + wps->w.pend_data |= ((int32_t) sign << wps->w.pend_count++); + + if (!wps->w.holding_zero) + flush_word (wps); +} + +// Used by send_word() and send_word_lossless() to actually send most the +// accumulated data onto the bitstream. This is also called directly from +// clients when all words have been sent. + +void flush_word (WavpackStream *wps) +{ + if (wps->w.zeros_acc) { + int cbits = count_bits (wps->w.zeros_acc); + + while (cbits--) { + putbit_1 (&wps->wvbits); + } + + putbit_0 (&wps->wvbits); + + while (wps->w.zeros_acc > 1) { + putbit (wps->w.zeros_acc & 1, &wps->wvbits); + wps->w.zeros_acc >>= 1; + } + + wps->w.zeros_acc = 0; + } + + if (wps->w.holding_one) { +#ifdef LIMIT_ONES + if (wps->w.holding_one >= LIMIT_ONES) { + int cbits; + + putbits ((1L << LIMIT_ONES) - 1, LIMIT_ONES + 1, &wps->wvbits); + wps->w.holding_one -= LIMIT_ONES; + cbits = count_bits (wps->w.holding_one); + + while (cbits--) { + putbit_1 (&wps->wvbits); + } + + putbit_0 (&wps->wvbits); + + while (wps->w.holding_one > 1) { + putbit (wps->w.holding_one & 1, &wps->wvbits); + wps->w.holding_one >>= 1; + } + + wps->w.holding_zero = 0; + } + else + putbits (bitmask [wps->w.holding_one], wps->w.holding_one, &wps->wvbits); + + wps->w.holding_one = 0; +#else + do { + putbit_1 (&wps->wvbits); + } while (--wps->w.holding_one); +#endif + } + + if (wps->w.holding_zero) { + putbit_0 (&wps->wvbits); + wps->w.holding_zero = 0; + } + + if (wps->w.pend_count) { + + while (wps->w.pend_count > 24) { + putbit (wps->w.pend_data & 1, &wps->wvbits); + wps->w.pend_data >>= 1; + wps->w.pend_count--; + } + + putbits (wps->w.pend_data, wps->w.pend_count, &wps->wvbits); + wps->w.pend_data = wps->w.pend_count = 0; + } +} + +// This function is similar to send_word() except that no data is actually +// written to any stream, but it does return the value that would have been +// sent to a hybrid stream. It is used to determine beforehand how much noise +// will be added to samples. + +int32_t nosend_word (WavpackStream *wps, int32_t value, int chan) +{ + uint32_t ones_count, low, mid, high; + int sign = (value < 0) ? 1 : 0; + + if (sign) + value = ~value; + + if ((wps->wphdr.flags & HYBRID_FLAG) && !chan) + update_error_limit (wps); + + if (value < GET_MED (0)) { + low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + ones_count = 2 + (value - low) / GET_MED (2); + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + mid = (high + low + 1) >> 1; + + if (!wps->w.error_limit [chan]) + mid = value; + else + while (high - low > wps->w.error_limit [chan]) + if (value < mid) + mid = ((high = mid - 1) + low + 1) >> 1; + else + mid = (high + (low = mid) + 1) >> 1; + + wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS; + wps->w.slow_level [chan] += mylog2 (mid); + + return sign ? ~mid : mid; +} + +// This function is used to scan some number of samples to set the variables +// "slow_level" and the "median" array. In pure symetrical encoding mode this +// would not be needed because these values would simply be continued from the +// previous block. However, in the -X modes and the 32-bit modes we cannot do +// this because parameters may change between blocks and the variables might +// not apply. This function can work in mono or stereo and can scan a block +// in either direction. + +void scan_word (WavpackStream *wps, int32_t *samples, uint32_t num_samples, int dir) +{ + uint32_t flags = wps->wphdr.flags, value, low; + int chan; + + CLEAR (wps->w.slow_level); + CLEAR (wps->w.median); + + if (flags & MONO_FLAG) { + if (dir < 0) { + samples += (num_samples - 1); + dir = -1; + } + else + dir = 1; + } + else { + if (dir < 0) { + samples += (num_samples - 1) * 2; + dir = -2; + } + else + dir = 2; + } + + while (num_samples--) { + + value = labs (samples [chan = 0]); + + if (flags & HYBRID_BITRATE) { + wps->w.slow_level [0] -= (wps->w.slow_level [0] + SLO) >> SLS; + wps->w.slow_level [0] += mylog2 (value); + } + + if (value < GET_MED (0)) { + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + DEC_MED2 (); + } + else { + INC_MED2 (); + } + } + } + + if (!(flags & MONO_FLAG)) { + value = labs (samples [chan = 1]); + + if (wps->wphdr.flags & HYBRID_BITRATE) { + wps->w.slow_level [1] -= (wps->w.slow_level [1] + SLO) >> SLS; + wps->w.slow_level [1] += mylog2 (value); + } + + if (value < GET_MED (0)) { + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (value - low < GET_MED (1)) { + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (value - low < GET_MED (2)) { + DEC_MED2 (); + } + else { + INC_MED2 (); + } + } + } + } + + samples += dir; + } +} + +#endif + +#ifdef UNPACK + +static uint32_t FASTCALL 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 FASTCALL get_word (WavpackStream *wps, int chan, int32_t *correction) +{ + uint32_t ones_count, low, mid, high; + int next8, sign; + int32_t value; + + if (correction) + *correction = 0; + +#ifdef DEBUG_WORDS + ones_count = 32; + while (ones_count--) + value = getbit (&wps->wvbits) ? (value >> 1) | 0x80000000 : (value >> 1) & 0x7fffffff; + return value; +#endif + + if (!(wps->w.median [0] [0] & ~1) && !wps->w.holding_zero && !wps->w.holding_one && !(wps->w.median [0] [1] & ~1)) { + uint32_t mask; + int cbits; + + if (wps->w.zeros_acc) { + if (--wps->w.zeros_acc) { + wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS; + return 0; + } + } + else { + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + wps->w.zeros_acc = cbits; + else { + for (mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + wps->w.zeros_acc |= mask; + + wps->w.zeros_acc |= mask; + } + + if (wps->w.zeros_acc) { + wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS; + CLEAR (wps->w.median); + return 0; + } + } + } + + if (wps->w.holding_zero) + ones_count = wps->w.holding_zero = 0; + else { + if (wps->wvbits.bc < 8) { + if (++(wps->wvbits.ptr) == wps->wvbits.end) + wps->wvbits.wrap (&wps->wvbits); + + next8 = (wps->wvbits.sr |= *(wps->wvbits.ptr) << wps->wvbits.bc) & 0xff; + wps->wvbits.bc += 8; + } + else + next8 = wps->wvbits.sr & 0xff; + + if (next8 == 0xff) { + wps->wvbits.bc -= 8; + wps->wvbits.sr >>= 8; + + for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count); + + if (ones_count == (LIMIT_ONES + 1)) + return WORD_EOF; + + if (ones_count == LIMIT_ONES) { + uint32_t mask; + int cbits; + + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else { + wps->wvbits.bc -= (ones_count = ones_count_table [next8]) + 1; + wps->wvbits.sr >>= ones_count + 1; + } + + if (wps->w.holding_one) { + wps->w.holding_one = ones_count & 1; + ones_count = (ones_count >> 1) + 1; + } + else { + wps->w.holding_one = ones_count & 1; + ones_count >>= 1; + } + + wps->w.holding_zero = ~wps->w.holding_one & 1; + } + + if ((wps->wphdr.flags & HYBRID_FLAG) && !chan) + update_error_limit (wps); + + if (ones_count == 0) { + low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (ones_count == 1) { + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (ones_count == 2) { + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + mid = (high + low + 1) >> 1; + + if (!wps->w.error_limit [chan]) + mid = read_code (&wps->wvbits, high - low) + low; + else while (high - low > wps->w.error_limit [chan]) { + if (getbit (&wps->wvbits)) + mid = (high + (low = mid) + 1) >> 1; + else + mid = ((high = mid - 1) + low + 1) >> 1; + } + + sign = getbit (&wps->wvbits); + + if (bs_is_open (&wps->wvcbits) && wps->w.error_limit [chan]) { + value = read_code (&wps->wvcbits, high - low) + low; + + if (correction) + *correction = sign ? (mid - value) : (value - mid); + } + + if (wps->wphdr.flags & HYBRID_BITRATE) { + wps->w.slow_level [chan] -= (wps->w.slow_level [chan] + SLO) >> SLS; + wps->w.slow_level [chan] += mylog2 (mid); + } + + return sign ? ~mid : mid; +} + +// This is an optimized version of get_word() that is used for lossless only +// (error_limit == 0). There are two versions of an internal section; they +// are identical from a functional standpoint, but one may be faster than the +// other under different compilers / processors. + +int32_t FASTCALL get_word_lossless (WavpackStream *wps, int chan) +{ + uint32_t ones_count, low, high; + int next8; + +#ifdef DEBUG_WORDS + ones_count = 32; + while (ones_count--) + low = getbit (&wps->wvbits) ? (low >> 1) | 0x80000000 : (low >> 1) & 0x7fffffff; + return low; +#endif + + if (!(wps->w.median [0] [0] & ~1) && !wps->w.holding_zero && !wps->w.holding_one && !(wps->w.median [0] [1] & ~1)) { + uint32_t mask; + int cbits; + + if (wps->w.zeros_acc) { + if (--wps->w.zeros_acc) + return 0; + } + else { + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + wps->w.zeros_acc = cbits; + else { + for (mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + wps->w.zeros_acc |= mask; + + wps->w.zeros_acc |= mask; + } + + if (wps->w.zeros_acc) { + CLEAR (wps->w.median); + return 0; + } + } + } + + if (wps->w.holding_zero) + ones_count = wps->w.holding_zero = 0; + else { + if (wps->wvbits.bc < 8) { + if (++(wps->wvbits.ptr) == wps->wvbits.end) + wps->wvbits.wrap (&wps->wvbits); + + next8 = (wps->wvbits.sr |= *(wps->wvbits.ptr) << wps->wvbits.bc) & 0xff; + wps->wvbits.bc += 8; + } + else + next8 = wps->wvbits.sr & 0xff; + + if (next8 == 0xff) { + wps->wvbits.bc -= 8; + wps->wvbits.sr >>= 8; + + for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count); + + if (ones_count == (LIMIT_ONES + 1)) + return WORD_EOF; + + if (ones_count == LIMIT_ONES) { + uint32_t mask; + int cbits; + + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else { + wps->wvbits.bc -= (ones_count = ones_count_table [next8]) + 1; + wps->wvbits.sr >>= ones_count + 1; + } + + if (wps->w.holding_one) { + wps->w.holding_one = ones_count & 1; + ones_count = (ones_count >> 1) + 1; + } + else { + wps->w.holding_one = ones_count & 1; + ones_count >>= 1; + } + + wps->w.holding_zero = ~wps->w.holding_one & 1; + } + + if (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 (); + } + } + } + + low += read_code (&wps->wvbits, high - low); + return (getbit (&wps->wvbits)) ? ~low : low; +} + +// 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 FASTCALL read_code (Bitstream *bs, uint32_t maxcode) +{ + int bitcount = count_bits (maxcode); + uint32_t extras = bitset [bitcount] - maxcode - 1, code; + + if (!bitcount) + return 0; + + getbits (&code, bitcount - 1, bs); + code &= bitmask [bitcount - 1]; + + if (code >= extras) { + code = (code << 1) - extras; + + if (getbit (bs)) + ++code; + } + + return code; +} + +#endif + +// 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 FASTCALL 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 scans a buffer of longs and accumulates the total log2 value +// of all the samples. This is useful for determining maximum compression +// because the bitstream storage required for entropy coding is proportional +// to the base 2 log of the samples. + +uint32_t log2buffer (int32_t *samples, uint32_t num_samples) +{ + uint32_t result = 0, avalue; + int dbits; + + while (num_samples--) { + avalue = abs (*samples++); + + if ((avalue += avalue >> 9) < (1 << 8)) { + dbits = nbits_table [avalue]; + result += (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; + + result += (dbits << 8) + log2_table [(avalue >> (dbits - 9)) & 0xff]; + } + } + + return result; +} + +// 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. + +char store_weight (int weight) +{ + if (weight > 1024) + weight = 1024; + else if (weight < -1024) + weight = -1024; + + if (weight > 0) + weight -= (weight + 64) >> 7; + + return (weight + 4) >> 3; +} + +int restore_weight (char weight) +{ + int result; + + if ((result = (int) weight << 3) > 0) + result += (result + 64) >> 7; + + return result; +} diff --git a/Libraries/WavPack/Files/wputils.c b/Libraries/WavPack/Files/wputils.c new file mode 100644 index 000000000..3c2dbbad7 --- /dev/null +++ b/Libraries/WavPack/Files/wputils.c @@ -0,0 +1,2107 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wputils.c + +// This module provides a high-level interface to reading and writing WavPack +// files. WavPack input files can be opened as standard "C" streams using a +// provided filename. However, an alternate entry uses stream-reading +// callbacks to make using another file I/O method easy. Note that in this +// case the user application is responsible for finding and opening the .wvc +// file if the use of them is desired. + +// For writing WavPack files there are no I/O routines used; a callback for +// writing completed blocks is provided. + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#endif + +#include "wavpack.h" + +#if !defined(WIN32) +#define stricmp(x,y) strcasecmp(x,y) +#endif + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +#endif + +static void free_streams (WavpackContext *wpc); + +///////////////////////////// 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 //////////////////////////////// + +#ifdef TAGS +static int load_tag (WavpackContext *wpc); +static int valid_tag (M_Tag *m_tag); +static void free_tag (M_Tag *m_tag); +#endif + +#if defined(UNPACK) || defined(INFO_ONLY) + +static uint32_t read_next_header (stream_reader *reader, void *id, WavpackHeader *wphdr); +static uint32_t seek_final_index (stream_reader *reader, void *id); + +// This code provides an interface between the reader callback mechanism that +// WavPack uses internally and the standard fstream C library. + +#ifdef USE_FSTREAMS + +static int32_t read_bytes (void *id, void *data, int32_t bcount) +{ + return fread (data, 1, bcount, (FILE*) id); +} + +static uint32_t get_pos (void *id) +{ + return ftell ((FILE*) id); +} + +static int set_pos_abs (void *id, uint32_t pos) +{ + return fseek (id, pos, SEEK_SET); +} + +static int set_pos_rel (void *id, int32_t delta, int mode) +{ + return fseek (id, delta, mode); +} + +static int push_back_byte (void *id, int c) +{ + return ungetc (c, id); +} + +static uint32_t get_length (void *id) +{ + FILE *file = id; + struct stat statbuf; + + if (!file || fstat (fileno (file), &statbuf) || !(statbuf.st_mode & S_IFREG)) + return 0; + + return statbuf.st_size; +} + +static int can_seek (void *id) +{ + FILE *file = id; + struct stat statbuf; + + return file && !fstat (fileno (file), &statbuf) && (statbuf.st_mode & S_IFREG); +} + +static stream_reader freader = { + read_bytes, get_pos, set_pos_abs, set_pos_rel, push_back_byte, get_length, can_seek +}; + +// This function attempts to open the specified WavPack file for reading. If +// this fails for any reason 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). A +// filename beginning with "-" is assumed to be stdin. The "flags" argument +// has the following bit mask values to specify details of the open operation: + +// OPEN_WVC: attempt to open/read "correction" file +// OPEN_TAGS: attempt to read ID3v1 / APEv2 tags (requires seekable file) +// OPEN_WRAPPER: make audio wrapper available (i.e. RIFF) to caller +// OPEN_2CH_MAX: open only first stream of multichannel file (usually L/R) +// OPEN_NORMALIZE: normalize floating point data to +/- 1.0 (w/ offset exp) +// OPEN_STREAMING: blindly unpacks blocks w/o regard to header file position + +// Version 4.2 of the WavPack library adds the OPEN_STREAMING flag. This is +// essentially a "raw" mode where the library will simply decode any blocks +// fed it through the reader callback, regardless of where those blocks came +// from in a stream. The only requirement is that complete WavPack blocks are +// fed to the decoder (and this may require multiple blocks in multichannel +// mode) and that complete blocks are decoded (even if all samples are not +// actually required). All the blocks must contain the same number of channels +// and bit resolution, and the correction data must be either present or not. +// All other parameters may change from block to block (like lossy/lossless). +// Obviously, in this mode any seeking must be performed by the application +// (and again, decoding must start at the beginning of the block containing +// the seek sample). + +WavpackContext *WavpackOpenFileInput (const char *infilename, char *error, int flags, int norm_offset) +{ + FILE *wv_id, *wvc_id; + WavpackContext *wpc; + + if (*infilename == '-') { + wv_id = stdin; +#if defined(WIN32) + setmode (fileno (stdin), O_BINARY); +#endif + } + else if ((wv_id = fopen (infilename, "rb")) == NULL) { + strcpy (error, "can't open file"); + return NULL; + } + + if (wv_id != stdin && (flags & OPEN_WVC)) { + char *in2filename = malloc (strlen (infilename) + 10); + + strcpy (in2filename, infilename); + strcat (in2filename, "c"); + wvc_id = fopen (in2filename, "rb"); + } + else + wvc_id = NULL; + + wpc = WavpackOpenFileInputEx (&freader, wv_id, wvc_id, error, flags, norm_offset); + + if (!wpc) { + if (wv_id) + fclose (wv_id); + + if (wvc_id) + fclose (wvc_id); + } + else + wpc->close_files = TRUE; + + return wpc; +} + +#endif + +// This function is identical to WavpackOpenFileInput() except that instead +// of providing a filename to open, the caller provides a pointer to a set of +// reader callbacks and instances of up to two streams. The first of these +// streams is required and contains the regular WavPack data stream; the second +// contains the "correction" file if desired. Unlike the standard open +// function which handles the correction file transparently, in this case it +// is the responsibility of the caller to be aware of correction files. + +WavpackContext *WavpackOpenFileInputEx (stream_reader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset) +{ + WavpackContext *wpc = malloc (sizeof (WavpackContext)); + WavpackStream *wps; + uchar first_byte; + uint32_t bcount; + + if (!wpc) { + strcpy (error, "can't allocate memory"); + return NULL; + } + + CLEAR (*wpc); + wpc->wv_in = wv_id; + wpc->wvc_in = wvc_id; + wpc->reader = reader; + wpc->total_samples = (uint32_t) -1; + wpc->norm_offset = norm_offset; + wpc->open_flags = flags; + + wpc->filelen = wpc->reader->get_length (wpc->wv_in); + +#ifdef TAGS + if ((flags & OPEN_TAGS) && wpc->reader->can_seek (wpc->wv_in)) { + load_tag (wpc); + wpc->reader->set_pos_abs (wpc->wv_in, 0); + } +#endif + +#ifdef VER3 + if (wpc->reader->read_bytes (wpc->wv_in, &first_byte, 1) != 1) { + strcpy (error, "can't read all of WavPack file!"); + return WavpackCloseFile (wpc); + } + + wpc->reader->push_back_byte (wpc->wv_in, first_byte); + + if (first_byte == 'R') + return open_file3 (wpc, error); +#endif + + wpc->streams [0] = wps = malloc (sizeof (WavpackStream)); + wpc->num_streams = 1; + CLEAR (*wps); + + while (!wps->wphdr.block_samples) { + + wpc->filepos = wpc->reader->get_pos (wpc->wv_in); + bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) { + strcpy (error, "not compatible with this version of WavPack file!"); + return WavpackCloseFile (wpc); + } + + wpc->filepos += bcount; + wps->blockbuff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->blockbuff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != wps->wphdr.ckSize - 24) { + strcpy (error, "can't read all of WavPack file!"); + return WavpackCloseFile (wpc); + } + + if ((wps->wphdr.flags & UNKNOWN_FLAGS) || wps->wphdr.version < 0x402 || wps->wphdr.version > 0x40f) { + strcpy (error, "not compatible with this version of WavPack file!"); + return WavpackCloseFile (wpc); + } + + if (wps->wphdr.block_samples && !(flags & OPEN_STREAMING)) { + if (wps->wphdr.total_samples == (uint32_t) -1 && wpc->reader->can_seek (wpc->wv_in)) { + uint32_t pos_save = wpc->reader->get_pos (wpc->wv_in); + uint32_t final_index = seek_final_index (wpc->reader, wpc->wv_in); + + if (final_index != (uint32_t) -1) + wpc->total_samples = final_index - wps->wphdr.block_index; + + wpc->reader->set_pos_abs (wpc->wv_in, pos_save); + } + else + wpc->total_samples = wps->wphdr.total_samples; + } + + if (wpc->wvc_in && wps->wphdr.block_samples && (wps->wphdr.flags & HYBRID_FLAG)) { + wpc->file2len = wpc->reader->get_length (wpc->wvc_in); + wpc->wvc_flag = TRUE; + } + + if (wpc->wvc_flag) { + wpc->file2pos = wpc->reader->get_pos (wpc->wvc_in); + bcount = read_next_header (wpc->reader, wpc->wvc_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) { + strcpy (error, "problem with correction file"); + return WavpackCloseFile (wpc); + } + + wpc->file2pos += bcount; + wps->block2buff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->block2buff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + strcpy (error, "can't read all of WavPack file!"); + return WavpackCloseFile (wpc); + } + } + + if (!unpack_init (wpc)) { + strcpy (error, wpc->error_message [0] ? wpc->error_message : + "not compatible with this version of WavPack file!"); + + return WavpackCloseFile (wpc); + } + } + + wpc->config.flags &= ~0xff; + wpc->config.flags |= wps->wphdr.flags & 0xff; + wpc->config.bytes_per_sample = (wps->wphdr.flags & BYTES_STORED) + 1; + wpc->config.float_norm_exp = wps->float_norm_exp; + + wpc->config.bits_per_sample = (wpc->config.bytes_per_sample * 8) - + ((wps->wphdr.flags & SHIFT_MASK) >> SHIFT_LSB); + + if (!wpc->config.sample_rate) { + if (!wps || !wps->wphdr.block_samples || (wps->wphdr.flags & SRATE_MASK) == SRATE_MASK) + wpc->config.sample_rate = 44100; + else + wpc->config.sample_rate = sample_rates [(wps->wphdr.flags & SRATE_MASK) >> SRATE_LSB]; + } + + if (!wpc->config.num_channels) { + wpc->config.num_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2; + wpc->config.channel_mask = 0x5 - wpc->config.num_channels; + } + + if ((flags & OPEN_2CH_MAX) && !(wps->wphdr.flags & FINAL_BLOCK)) + wpc->reduced_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2; + + return wpc; +} + +// This function obtains general information about an open file and returns +// a mask with the following bit values: + +// MODE_WVC: a .wvc file has been found and will be used for lossless +// MODE_LOSSLESS: file is lossless (either pure or hybrid) +// MODE_HYBRID: file is hybrid mode (either lossy or lossless) +// MODE_FLOAT: audio data is 32-bit ieee floating point +// MODE_VALID_TAG: file conatins a valid ID3v1 or APEv2 tag +// MODE_HIGH: file was created in "high" mode (information only) +// MODE_FAST: file was created in "fast" mode (information only) +// MODE_EXTRA: file was created using "extra" mode (information only) +// MODE_APETAG: file contains a valid APEv2 tag +// MODE_SFX: file was created as a "self-extracting" executable + +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->wvc_flag) + mode |= (MODE_LOSSLESS | MODE_WVC); + + 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; + + if (wpc->config.flags & CONFIG_EXTRA_MODE) + mode |= MODE_EXTRA; + + if (wpc->config.flags & CONFIG_CREATE_EXE) + mode |= MODE_SFX; + +#ifdef TAGS + if (valid_tag (&wpc->m_tag)) { + mode |= MODE_VALID_TAG; + + if (valid_tag (&wpc->m_tag) == 'A') + mode |= MODE_APETAG; + } +#endif + } + + return mode; +} + +// This function returns the major version number of the WavPack program +// (or library) that created the open file. Currently, this can be 1 to 4. +// Minor versions are not recorded in WavPack files. + +int WavpackGetVersion (WavpackContext *wpc) +{ + if (wpc) { +#ifdef VER3 + if (wpc->stream3) + return get_version3 (wpc); +#endif + return 4; + } + + return 0; +} + +#endif + +#ifdef UNPACK + +// 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 or even more for multichannel files, so the +// required memory at "buffer" is 4 * samples * num_channels bytes. The +// audio data is returned right-justified in 32-bit longs in the endian +// mode native to the executing processor. So, if the original data was +// 16-bit, then the values returned would be +/-32k. Floating point data +// can also be returned if the source was floating point data (and this +// can be optionally normalized to +/-1.0 by using the appropriate flag +// in the call to WavpackOpenFileInput ()). 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->streams [wpc->current_stream = 0]; + uint32_t bcount, samples_unpacked = 0, samples_to_unpack; + int num_channels = wpc->config.num_channels; + +#ifdef VER3 + if (wpc->stream3) + return unpack_samples3 (wpc, buffer, samples); +#endif + + while (samples) { + if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || + wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples) { + free_streams (wpc); + wpc->filepos = wpc->reader->get_pos (wpc->wv_in); + bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) + break; + + if (wpc->open_flags & OPEN_STREAMING) + wps->wphdr.block_index = wps->sample_index = 0; + + wpc->filepos += bcount; + wps->blockbuff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->blockbuff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + strcpy (wpc->error_message, "can't read all of WavPack file!"); + break; + } + + if (wps->wphdr.version < 0x402 || wps->wphdr.version > 0x40f) { + strcpy (wpc->error_message, "not compatible with this version of WavPack file!"); + break; + } + + if (wps->wphdr.block_samples && wpc->wvc_flag) { + wpc->file2pos = wpc->reader->get_pos (wpc->wvc_in); + bcount = read_next_header (wpc->reader, wpc->wvc_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) + break; + + if (wpc->open_flags & OPEN_STREAMING) + wps->wphdr.block_index = wps->sample_index = 0; + + wpc->file2pos += bcount; + wps->block2buff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->block2buff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) + 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; + + if (!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) { + int32_t *temp_buffer = malloc (samples_to_unpack * 8), *src, *dst; + int offset = 0; + uint32_t samcnt; + + while (1) { + if (wpc->current_stream == wpc->num_streams) { + wps = wpc->streams [wpc->num_streams++] = malloc (sizeof (WavpackStream)); + CLEAR (*wps); + bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) + break; + + if (wpc->open_flags & OPEN_STREAMING) + wps->wphdr.block_index = wps->sample_index = 0; + + wps->blockbuff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->blockbuff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + strcpy (wpc->error_message, "can't read all of WavPack file!"); + break; + } + + if (wpc->wvc_flag) { + bcount = read_next_header (wpc->reader, wpc->wvc_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) + break; + + if (wpc->open_flags & OPEN_STREAMING) + wps->wphdr.block_index = wps->sample_index = 0; + + wps->block2buff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->block2buff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) + break; + } + + if (!unpack_init (wpc)) + break; + } + else + wps = wpc->streams [wpc->current_stream]; + + unpack_samples (wpc, src = temp_buffer, samples_to_unpack); + samcnt = samples_to_unpack; + dst = buffer + offset; + + if (wps->wphdr.flags & MONO_FLAG) { + while (samcnt--) { + dst [0] = *src++; + dst += num_channels; + } + + offset++; + } + else { + while (samcnt--) { + dst [0] = *src++; + dst [1] = *src++; + dst += num_channels; + } + + offset += 2; + } + + if (wps->wphdr.flags & FINAL_BLOCK) + break; + else + wpc->current_stream++; + } + + wps = wpc->streams [wpc->current_stream = 0]; + free (temp_buffer); + } + else + 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) && wps->blockbuff) { + + if (wpc->reader->can_seek (wpc->wv_in)) { + int32_t rseek = ((WavpackHeader *) wps->blockbuff)->ckSize / 3; + wpc->reader->set_pos_rel (wpc->wv_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR); + } + + if (wpc->wvc_flag && wps->block2buff && wpc->reader->can_seek (wpc->wvc_in)) { + int32_t rseek = ((WavpackHeader *) wps->block2buff)->ckSize / 3; + wpc->reader->set_pos_rel (wpc->wvc_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR); + } + + wpc->crc_errors++; + } + } + + if (wps->sample_index == wpc->total_samples) + break; + } + + return samples_unpacked; +} + +#ifdef SEEKING + +static uint32_t find_sample (WavpackContext *wpc, void *infile, uint32_t header_pos, uint32_t sample); + +// Seek to the specifed sample index, returning TRUE on success. Note that +// files generated with version 4.0 or newer will seek almost immediately. +// Older files can take quite long if required to seek through unplayed +// portions of the file, but will create a seek map so that reverse seeks +// (or forward seeks to already scanned areas) will be very fast. + +int WavpackSeekSample (WavpackContext *wpc, uint32_t sample) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream = 0]; + uint32_t bcount, samples_to_skip; + int32_t *buffer; + + if (wpc->total_samples == (uint32_t) -1 || sample >= wpc->total_samples || + !wpc->reader->can_seek (wpc->wv_in) || (wpc->open_flags & OPEN_STREAMING) || + (wpc->wvc_flag && !wpc->reader->can_seek (wpc->wvc_in))) + return FALSE; + +#ifdef VER3 + if (wpc->stream3) + return seek_sample3 (wpc, sample); +#endif + + if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || sample < wps->wphdr.block_index || + sample >= wps->wphdr.block_index + wps->wphdr.block_samples) { + + free_streams (wpc); + wpc->filepos = find_sample (wpc, wpc->wv_in, wpc->filepos, sample); + + if (wpc->filepos == (uint32_t) -1) + return FALSE; + + if (wpc->wvc_flag) { + wpc->file2pos = find_sample (wpc, wpc->wvc_in, 0, sample); + + if (wpc->file2pos == (uint32_t) -1) + return FALSE; + } + } + + if (!wps->blockbuff) { + wpc->reader->set_pos_abs (wpc->wv_in, wpc->filepos); + wpc->reader->read_bytes (wpc->wv_in, &wps->wphdr, sizeof (WavpackHeader)); + little_endian_to_native (&wps->wphdr, WavpackHeaderFormat); + wps->blockbuff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader)); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + free_streams (wpc); + return FALSE; + } + + if (wpc->wvc_flag) { + wpc->reader->set_pos_abs (wpc->wvc_in, wpc->file2pos); + wpc->reader->read_bytes (wpc->wvc_in, &wps->wphdr, sizeof (WavpackHeader)); + little_endian_to_native (&wps->wphdr, WavpackHeaderFormat); + wps->block2buff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader)); + + if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + free_streams (wpc); + return FALSE; + } + } + + if (!unpack_init (wpc)) { + free_streams (wpc); + return FALSE; + } + } + + while (!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) { + if (++wpc->current_stream == wpc->num_streams) { + wps = wpc->streams [wpc->num_streams++] = malloc (sizeof (WavpackStream)); + CLEAR (*wps); + bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) + break; + + wps->blockbuff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->blockbuff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) { + strcpy (wpc->error_message, "can't read all of WavPack file!"); + break; + } + + if (wpc->wvc_flag) { + bcount = read_next_header (wpc->reader, wpc->wvc_in, &wps->wphdr); + + if (bcount == (uint32_t) -1) + break; + + wps->block2buff = malloc (wps->wphdr.ckSize + 8); + memcpy (wps->block2buff, &wps->wphdr, 32); + + if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + 32, wps->wphdr.ckSize - 24) != + wps->wphdr.ckSize - 24) + break; + } + + if (!unpack_init (wpc)) + break; + } + else + wps = wpc->streams [wpc->current_stream]; + } + + if (sample < wps->sample_index) + for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++) + if (!unpack_init (wpc)) + return FALSE; + + samples_to_skip = sample - wps->sample_index; + + if (samples_to_skip) { + buffer = malloc (samples_to_skip * 8); + + for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++) + unpack_samples (wpc, buffer, samples_to_skip); + + free (buffer); + } + + wpc->current_stream = 0; + return TRUE; +} + +#endif + +#ifdef TAGS + +// Attempt to get the specified item from the specified ID3v1 or APEv2 tag. +// The "size" parameter specifies the amount of space available at "value", +// if the desired item will not fit in this space then ellipses (...) will +// be appended and the string terminated. Only text data are supported. + +static void tagcpy (char *dest, char *src, int tag_size); + +int WavpackGetTagItem (WavpackContext *wpc, const char *item, char *value, int size) +{ + M_Tag *m_tag = &wpc->m_tag; + char *lvalue = NULL; + + if (value) + *value = 0; + + if (m_tag->ape_tag_hdr.ID [0] == 'A') { + char *p = m_tag->ape_tag_data; + char *q = p + m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr); + int i; + + for (i = 0; i < m_tag->ape_tag_hdr.item_count; ++i) { + int vsize, flags, isize; + + vsize = * (int32_t *) p; p += 4; + flags = * (int32_t *) p; p += 4; + isize = strlen (p); + + little_endian_to_native (&vsize, "L"); + little_endian_to_native (&flags, "L"); + + if (p + isize + vsize + 1 > q) + break; + + if (isize && vsize && !stricmp (item, p) && !(flags & 6)) { + + if ((lvalue = malloc (vsize + 1)) != NULL) { + strncpy (lvalue, p + isize + 1, vsize); + lvalue [vsize] = 0; + } + + break; + } + else + p += isize + vsize + 1; + } + } + else if (m_tag->id3_tag.tag_id [0] == 'T') { + if ((lvalue = malloc (128)) == NULL) + return FALSE; + + lvalue [0] = 0; + + if (!stricmp (item, "title")) + tagcpy (lvalue, m_tag->id3_tag.title, sizeof (m_tag->id3_tag.title)); + else if (!stricmp (item, "artist")) + tagcpy (lvalue, m_tag->id3_tag.artist, sizeof (m_tag->id3_tag.artist)); + else if (!stricmp (item, "album")) + tagcpy (lvalue, m_tag->id3_tag.album, sizeof (m_tag->id3_tag.album)); + else if (!stricmp (item, "year")) + tagcpy (lvalue, m_tag->id3_tag.year, sizeof (m_tag->id3_tag.year)); + else if (!stricmp (item, "comment")) + tagcpy (lvalue, m_tag->id3_tag.comment, sizeof (m_tag->id3_tag.title)); + + if (!lvalue [0]) { + free (lvalue); + return FALSE; + } + } + else + return FALSE; + + if (lvalue) { + if (value && size >= 4) { + if (strlen (lvalue) >= size) { + lvalue [size - 4] = lvalue [size - 3] = lvalue [size - 2] = '.'; + lvalue [size - 1] = 0; + } + + strcpy (value, lvalue); + } + + free (lvalue); + return TRUE; + } + else + return FALSE; +} + +#endif + +#endif + +#ifdef PACK + +// Open context for writing WavPack files. The returned context pointer is used +// in all following calls to the library. The "blockout" function will be used +// to store the actual completed WavPack blocks and will be called with the id +// pointers containing user defined data (one for the wv file and one for the +// wvc file). A return value of NULL indicates that memory could not be +// allocated for the context. + +WavpackContext *WavpackOpenFileOutput (blockout blockout, void *wv_id, void *wvc_id) +{ + WavpackContext *wpc = malloc (sizeof (WavpackContext)); + + if (!wpc) + return NULL; + + CLEAR (*wpc); + wpc->blockout = blockout; + wpc->wv_out = wv_id; + wpc->wvc_out = wvc_id; + return wpc; +} + +// Set configuration for writing WavPack files. This must be done before +// sending any actual samples, however it is okay to send wrapper or other +// metadata before calling this. The "config" structure contains the following +// required information: + +// config->bytes_per_sample see WavpackGetBytesPerSample() for info +// config->bits_per_sample see WavpackGetBitsPerSample() for info +// config->channel_mask Microsoft standard (mono = 4, stereo = 3) +// config->num_channels self evident +// config->sample_rate self evident + +// In addition, the following fields and flags may be set: + +// config->flags: +// -------------- +// o CONFIG_HYBRID_FLAG select hybrid mode (must set bitrate) +// o CONFIG_JOINT_STEREO select joint stereo (must set override also) +// o CONFIG_JOINT_OVERRIDE override default joint stereo selection +// o CONFIG_HYBRID_SHAPE select hybrid noise shaping (set override & +// shaping_weight != 0.0) +// o CONFIG_SHAPE_OVERRIDE override default hybrid noise shaping +// (set CONFIG_HYBRID_SHAPE and shaping_weight) +// o CONFIG_FAST_FLAG "fast" compression mode +// o CONFIG_HIGH_FLAG "high" compression mode +// o CONFIG_BITRATE_KBPS hybrid bitrate is kbps, not bits / sample +// o CONFIG_CREATE_WVC create correction file +// o CONFIG_OPTIMIZE_WVC maximize bybrid compression (-cc option) +// o CONFIG_CALC_NOISE calc noise in hybrid mode +// o CONFIG_EXTRA_MODE extra processing mode (slow!) +// o CONFIG_SKIP_WVX no wvx stream for floats & large ints + +// config->bitrate hybrid bitrate in either bits/sample or kbps +// config->shaping_weight hybrid noise shaping coefficient override +// config->block_samples force samples per WavPack block (0 = use deflt) +// config->float_norm_exp select floating-point data (127 for +/-1.0) +// config->xmode extra mode processing value override + +// If the number of samples to be written is known then it should be passed +// here. If the duration is not known then pass -1. In the case that the size +// is not known (or the writing is terminated early) then it is suggested that +// the application retrieve the first block written and let the library update +// the total samples indication. A function is provided to do this update and +// it should be done to the "correction" file also. If this cannot be done +// (because a pipe is being used, for instance) then a valid WavPack will still +// be created, but when applications want to access that file they will have +// to seek all the way to the end to determine the actual duration. Also, if +// a RIFF header has been included then it should be updated as well or the +// WavPack file will not be directly unpackable to a valid wav file (although +// it will still be usable by itself). A return of FALSE indicates an error. + +static const uint32_t xtable [] = { 123, 3, 27, 59, 123, 187, 251 }; +static const uint32_t f_xtable [] = { 251, 3, 27, 59, 123, 187, 251 }; +static const uint32_t h_xtable [] = { 91, 3, 27, 91, 123, 187, 251 }; + +int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, uint32_t total_samples) +{ + uint32_t flags = (config->bytes_per_sample - 1), bps = 0, shift = 0; + uint32_t chan_mask = config->channel_mask; + int num_chans = config->num_channels; + int i; + + wpc->total_samples = total_samples; + wpc->config.sample_rate = config->sample_rate; + wpc->config.num_channels = config->num_channels; + wpc->config.channel_mask = config->channel_mask; + wpc->config.bits_per_sample = config->bits_per_sample; + wpc->config.bytes_per_sample = config->bytes_per_sample; + wpc->config.block_samples = config->block_samples; + wpc->config.flags = config->flags; + + if (config->float_norm_exp) { + wpc->config.float_norm_exp = config->float_norm_exp; + flags |= FLOAT_DATA; + } + else + shift = (config->bytes_per_sample * 8) - config->bits_per_sample; + + for (i = 0; i < 15; ++i) + if (wpc->config.sample_rate == sample_rates [i]) + break; + + flags |= i << SRATE_LSB; + flags |= shift << SHIFT_LSB; + + if (config->flags & CONFIG_HYBRID_FLAG) { + flags |= HYBRID_FLAG | HYBRID_BITRATE | HYBRID_BALANCE; + + if (!(wpc->config.flags & CONFIG_SHAPE_OVERRIDE)) { + wpc->config.flags |= CONFIG_HYBRID_SHAPE | CONFIG_AUTO_SHAPING; + flags |= HYBRID_SHAPE | NEW_SHAPING; + } + else if (wpc->config.flags & CONFIG_HYBRID_SHAPE) { + wpc->config.shaping_weight = config->shaping_weight; + flags |= HYBRID_SHAPE | NEW_SHAPING; + } + + if (wpc->config.flags & CONFIG_OPTIMIZE_WVC) + flags |= CROSS_DECORR; + + if (config->flags & CONFIG_BITRATE_KBPS) { + bps = floor (config->bitrate * 256000.0 / config->sample_rate / config->num_channels + 0.5); + + if (bps > (64 << 8)) + bps = 64 << 8; + } + else + bps = floor (config->bitrate * 256.0 + 0.5); + } + else + flags |= CROSS_DECORR; + + if (!(config->flags & CONFIG_JOINT_OVERRIDE) || (config->flags & CONFIG_JOINT_STEREO)) + flags |= JOINT_STEREO; + + if (config->flags & CONFIG_CREATE_WVC) + wpc->wvc_flag = TRUE; + + for (wpc->current_stream = 0; num_chans; wpc->current_stream++) { + WavpackStream *wps = malloc (sizeof (WavpackStream)); + uint32_t stereo_mask, mono_mask; + int pos, chans; + + wpc->streams [wpc->current_stream] = wps; + CLEAR (*wps); + + for (pos = 1; pos <= 18; ++pos) { + stereo_mask = 3 << (pos - 1); + mono_mask = 1 << (pos - 1); + + if ((chan_mask & stereo_mask) == stereo_mask && (mono_mask & 0x251)) { + chan_mask &= ~stereo_mask; + chans = 2; + break; + } + else if (chan_mask & mono_mask) { + chan_mask &= ~mono_mask; + chans = 1; + break; + } + } + + if (pos == 19) + chans = num_chans > 1 ? 2 : 1; + + num_chans -= chans; + + if (num_chans && wpc->current_stream == MAX_STREAMS - 1) + break; + + memcpy (wps->wphdr.ckID, "wvpk", 4); + wps->wphdr.ckSize = sizeof (WavpackHeader) - 8; + wps->wphdr.total_samples = wpc->total_samples; + wps->wphdr.version = 0x403; + wps->wphdr.flags = flags; + wps->bits = bps; + + if (!wpc->current_stream) + wps->wphdr.flags |= INITIAL_BLOCK; + + if (!num_chans) + wps->wphdr.flags |= FINAL_BLOCK; + + if (chans == 1) { + wps->wphdr.flags &= ~(JOINT_STEREO | CROSS_DECORR | HYBRID_BALANCE); + wps->wphdr.flags |= MONO_FLAG; + } + } + + wpc->num_streams = wpc->current_stream; + wpc->current_stream = 0; + + if (num_chans) { + strcpy (wpc->error_message, "too many channels!"); + return FALSE; + } + + if (config->flags & CONFIG_EXTRA_MODE) { + + if (config->flags & CONFIG_HIGH_FLAG) + wpc->config.extra_flags = h_xtable [config->xmode]; + else if (config->flags & CONFIG_FAST_FLAG) + wpc->config.extra_flags = f_xtable [config->xmode]; + else + wpc->config.extra_flags = xtable [config->xmode]; + + if (config->flags & CONFIG_JOINT_OVERRIDE) + wpc->config.extra_flags &= ~EXTRA_STEREO_MODES; + } + + return TRUE; +} + +// Prepare to actually pack samples by determining the size of the WavPack +// blocks and allocating sample buffers and initializing each stream. Call +// after WavpackSetConfiguration() and before WavpackPackSamples(). A return +// of FALSE indicates an error. + +int WavpackPackInit (WavpackContext *wpc) +{ + if (wpc->metabytes > 4096) + write_metadata_block (wpc); + + if (wpc->config.block_samples) + wpc->block_samples = wpc->config.block_samples; + else { + if (wpc->config.flags & CONFIG_HIGH_FLAG) + wpc->block_samples = wpc->config.sample_rate; +// else if ((wpc->config.flags & CONFIG_FAST_FLAG) && !(wpc->config.sample_rate % 4)) +// wpc->block_samples = wpc->config.sample_rate / 4; + else if (!(wpc->config.sample_rate % 2)) + wpc->block_samples = wpc->config.sample_rate / 2; + else + wpc->block_samples = wpc->config.sample_rate; + + while (wpc->block_samples * wpc->config.num_channels > 100000) + wpc->block_samples /= 2; + + while (wpc->block_samples * wpc->config.num_channels < 40000) + wpc->block_samples *= 2; + } + + wpc->max_samples = wpc->block_samples + (wpc->block_samples >> 1); + + for (wpc->current_stream = 0; wpc->streams [wpc->current_stream]; wpc->current_stream++) { + WavpackStream *wps = wpc->streams [wpc->current_stream]; + + wps->sample_buffer = malloc (wpc->max_samples * (wps->wphdr.flags & MONO_FLAG ? 4 : 8)); + pack_init (wpc); + } + + return TRUE; +} + +// Pack the specified samples. Samples must be stored in longs in the native +// endian format of the executing processor. The number of samples specified +// indicates composite samples (sometimes called "frames"). So, the actual +// number of data points would be this "sample_count" times the number of +// channels. Note that samples are accumulated here until enough exist to +// create a complete WavPack block (or several blocks for multichannel audio). +// If an application wants to break a block at a specific sample, then it must +// simply call WavpackFlushSamples() to force an early termination. Completed +// WavPack blocks are send to the function provided in the initial call to +// WavpackOpenFileOutput(). A return of FALSE indicates an error. + +static int pack_streams (WavpackContext *wpc, uint32_t block_samples); + +int WavpackPackSamples (WavpackContext *wpc, int32_t *sample_buffer, uint32_t sample_count) +{ + int nch = wpc->config.num_channels; + + while (sample_count) { + int32_t *source_pointer = sample_buffer; + uint samples_to_copy; + + if (wpc->acc_samples + sample_count > wpc->max_samples) + samples_to_copy = wpc->max_samples - wpc->acc_samples; + else + samples_to_copy = sample_count; + + for (wpc->current_stream = 0; wpc->streams [wpc->current_stream]; wpc->current_stream++) { + WavpackStream *wps = wpc->streams [wpc->current_stream]; + int32_t *dptr, *sptr, cnt; + + dptr = wps->sample_buffer + wpc->acc_samples * (wps->wphdr.flags & MONO_FLAG ? 1 : 2); + sptr = source_pointer; + cnt = samples_to_copy; + + if (wps->wphdr.flags & MONO_FLAG) { + while (cnt--) { + *dptr++ = *sptr; + sptr += nch; + } + + source_pointer++; + } + else { + while (cnt--) { + *dptr++ = sptr [0]; + *dptr++ = sptr [1]; + sptr += nch; + } + + source_pointer += 2; + } + } + + sample_buffer += samples_to_copy * nch; + sample_count -= samples_to_copy; + + if ((wpc->acc_samples += samples_to_copy) == wpc->max_samples && + !pack_streams (wpc, wpc->block_samples)) + return FALSE; + } + + return TRUE; +} + +// Flush all accumulated samples into WavPack blocks. This is normally called +// after all samples have been sent to WavpackPackSamples(), but can also be +// called to terminate a WavPack block at a specific sample (in other words it +// is possible to continue after this operation). This is also called to +// dump non-audio blocks like those holding metadata for various purposes. +// A return of FALSE indicates an error. + +int WavpackFlushSamples (WavpackContext *wpc) +{ + while (wpc->acc_samples) { + uint32_t block_samples; + + if (wpc->acc_samples > wpc->block_samples) + block_samples = wpc->acc_samples / 2; + else + block_samples = wpc->acc_samples; + + if (!pack_streams (wpc, block_samples)) + return FALSE; + } + + if (wpc->metacount) + write_metadata_block (wpc); + + return TRUE; +} + +// Add wrapper (currently RIFF only) to WavPack blocks. This should be called +// before sending any audio samples for the RIFF header or after all samples +// have been sent for any RIFF trailer. WavpackFlushSamples() should be called +// between sending the last samples and calling this for trailer data to make +// sure that headers and trailers don't get mixed up in very short files. If +// the exact contents of the RIFF header are not known because, for example, +// the file duration is uncertain or trailing chunks are possible, simply write +// a "dummy" header of the correct length. When all data has been written it +// will be possible to read the first block written and update the header +// directly. An example of this can be found in the Audition filter. A +// return of FALSE indicates an error. + +int WavpackAddWrapper (WavpackContext *wpc, void *data, uint32_t bcount) +{ + uint32_t index = WavpackGetSampleIndex (wpc); + + return add_to_metadata (wpc, data, bcount, + (uchar)((!index || index == (uint32_t) -1) ? ID_RIFF_HEADER : ID_RIFF_TRAILER)); +} + +// Store computed MD5 sum in WavPack metadata. Note that the user must compute +// the 16 byte sum; it is not done here. A return of FALSE indicates an error. + +int WavpackStoreMD5Sum (WavpackContext *wpc, uchar data [16]) +{ + return add_to_metadata (wpc, data, 16, ID_MD5_CHECKSUM); +} + +static int pack_streams (WavpackContext *wpc, uint32_t block_samples) +{ + uint32_t max_blocksize = block_samples * 10 + 4096, bcount; + uchar *outbuff, *outend, *out2buff, *out2end; + int result; + + out2buff = (wpc->wvc_flag) ? malloc (max_blocksize) : NULL; + out2end = out2buff + max_blocksize; + outbuff = malloc (max_blocksize); + outend = outbuff + max_blocksize; + + for (wpc->current_stream = 0; wpc->streams [wpc->current_stream]; wpc->current_stream++) { + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uint32_t flags = wps->wphdr.flags; + + flags &= ~MAG_MASK; + flags += (1 << MAG_LSB) * ((flags & BYTES_STORED) * 8 + 7); + + wps->wphdr.block_index = wps->sample_index; + wps->wphdr.block_samples = block_samples; + wps->wphdr.flags = flags; + wps->block2buff = out2buff; + wps->block2end = out2end; + wps->blockbuff = outbuff; + wps->blockend = outend; + + result = pack_block (wpc, wps->sample_buffer); + wps->blockbuff = wps->block2buff = NULL; + + if (!result) { + strcpy (wpc->error_message, "output buffer overflowed!"); + break; + } + + bcount = ((WavpackHeader *) outbuff)->ckSize + 8; + native_to_little_endian ((WavpackHeader *) outbuff, WavpackHeaderFormat); + result = wpc->blockout (wpc->wv_out, outbuff, bcount); + + if (!result) { + strcpy (wpc->error_message, "can't write WavPack data, disk probably full!"); + break; + } + + wpc->filelen += bcount; + + if (out2buff) { + bcount = ((WavpackHeader *) out2buff)->ckSize + 8; + native_to_little_endian ((WavpackHeader *) out2buff, WavpackHeaderFormat); + result = wpc->blockout (wpc->wvc_out, out2buff, bcount); + + if (!result) { + strcpy (wpc->error_message, "can't write WavPack data, disk probably full!"); + break; + } + + wpc->file2len += bcount; + } + + if (wpc->acc_samples != block_samples) + memcpy (wps->sample_buffer, wps->sample_buffer + block_samples * (flags & MONO_FLAG ? 1 : 2), + (wpc->acc_samples - block_samples) * sizeof (int32_t) * (flags & MONO_FLAG ? 1 : 2)); + } + + wpc->current_stream = 0; + wpc->acc_samples -= block_samples; + free (outbuff); + + if (out2buff) + free (out2buff); + + return result; +} + +// Given the pointer to the first block written (to either a .wv or .wvc file), +// update the block with the actual number of samples written. This should +// be done if WavpackSetConfiguration() was called with an incorrect number +// of samples (or -1). It is the responsibility of the application to read and +// rewrite the block. An example of this can be found in the Audition filter. + +void WavpackUpdateNumSamples (WavpackContext *wpc, void *first_block) +{ + little_endian_to_native (wpc, WavpackHeaderFormat); + ((WavpackHeader *) first_block)->total_samples = WavpackGetSampleIndex (wpc); + native_to_little_endian (wpc, WavpackHeaderFormat); +} + +// Given the pointer to the first block written to a WavPack file, this +// function returns the location of the stored RIFF header that was originally +// written with WavpackAddWrapper(). This would normally be used to update +// the wav header to indicate that a different number of samples was actually +// written or if additional RIFF chunks are written at the end of the file. +// It is the responsibility of the application to read and rewrite the block. +// An example of this can be found in the Audition filter. + +void *WavpackGetWrapperLocation (void *first_block) +{ + if (((uchar *) first_block) [32] == ID_RIFF_HEADER) + return ((uchar *) first_block) + 34; + else + return NULL; +} + +#ifdef TAGS + +// Limited functionality to append APEv2 tags to WavPack files when they are +// created has been added for version 4.2. This function is used to append the +// specified field to the tag being created. If no tag has been started, then +// an empty one will be allocated first. When finished, use WavpackWriteTag() +// to write the completed tag to the file. Note that ID3 tags are not +// supported and that no editing of existing tags is allowed (there are several +// fine libraries available for this). + +int WavpackAppendTagItem (WavpackContext *wpc, const char *item, const char *value) +{ + M_Tag *m_tag = &wpc->m_tag; + int vsize = strlen (value); + int isize = strlen (item); + + if (!m_tag->ape_tag_hdr.ID [0]) { + strncpy (m_tag->ape_tag_hdr.ID, "APETAGEX", sizeof (m_tag->ape_tag_hdr.ID)); + m_tag->ape_tag_hdr.version = 2000; + m_tag->ape_tag_hdr.length = sizeof (m_tag->ape_tag_hdr); + m_tag->ape_tag_hdr.item_count = 0; + m_tag->ape_tag_hdr.flags = 0x80000000; + } + + if (m_tag->ape_tag_hdr.ID [0] == 'A') { + int new_item_len = vsize + isize + 9, flags = 0; + char *p; + + m_tag->ape_tag_hdr.item_count++; + m_tag->ape_tag_hdr.length += new_item_len; + p = m_tag->ape_tag_data = realloc (m_tag->ape_tag_data, m_tag->ape_tag_hdr.length); + p += m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr) - new_item_len; + native_to_little_endian (&vsize, "L"); + native_to_little_endian (&flags, "L"); + * (int32_t *) p = vsize; p += 4; + * (int32_t *) p = flags; p += 4; + little_endian_to_native (&vsize, "L"); + little_endian_to_native (&flags, "L"); + strcpy (p, item); + p += isize + 1; + memcpy (p, value, vsize); + + return TRUE; + } + else + return FALSE; +} + +// Once a APEv2 tag has been created with WavpackAppendTag(), this function is +// used to write the completed tag to the end of the WavPack file. Note that +// this function uses the same "blockout" function that is used to write +// regular WavPack blocks, although that's where the similarity ends. + +int WavpackWriteTag (WavpackContext *wpc) +{ + M_Tag *m_tag = &wpc->m_tag; + int result = TRUE; + + if (m_tag->ape_tag_hdr.ID [0] == 'A') { + m_tag->ape_tag_hdr.flags |= 0x20000000; + native_to_little_endian (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format); + result = wpc->blockout (wpc->wv_out, &m_tag->ape_tag_hdr, sizeof (m_tag->ape_tag_hdr)); + little_endian_to_native (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format); + + if (m_tag->ape_tag_hdr.length > sizeof (m_tag->ape_tag_hdr)) + result = wpc->blockout (wpc->wv_out, m_tag->ape_tag_data, m_tag->ape_tag_hdr.length - sizeof (m_tag->ape_tag_hdr)); + + m_tag->ape_tag_hdr.flags &= ~0x20000000; + native_to_little_endian (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format); + result = wpc->blockout (wpc->wv_out, &m_tag->ape_tag_hdr, sizeof (m_tag->ape_tag_hdr)); + little_endian_to_native (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format); + } + else if (m_tag->id3_tag.tag_id [0] == 'T') + result = wpc->blockout (wpc->wv_out, &m_tag->id3_tag, sizeof (m_tag->id3_tag)); + + if (!result) + strcpy (wpc->error_message, "can't write WavPack data, disk probably full!"); + + return result; +} + +#endif + +#endif + +// 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) { +#ifdef VER3 + if (wpc->stream3) + return get_sample_index3 (wpc); + else if (wpc->streams [0]) + return wpc->streams [0]->sample_index; +#else + if (wpc->streams [0]) + return wpc->streams [0]->sample_index; +#endif + } + + 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; +} + +// Calculate the progress through the file as a double from 0.0 (for begin) +// to 1.0 (for done). A return value of -1.0 indicates that the progress is +// unknown. + +double WavpackGetProgress (WavpackContext *wpc) +{ + if (wpc && wpc->total_samples != (uint32_t) -1 && wpc->total_samples != 0) + return (double) WavpackGetSampleIndex (wpc) / wpc->total_samples; + else + return -1.0; +} + +// Return the total size of the WavPack file(s) in bytes. + +uint32_t WavpackGetFileSize (WavpackContext *wpc) +{ + return wpc ? wpc->filelen + wpc->file2len : 0; +} + +// Calculate the ratio of the specified WavPack file size to the size of the +// original audio data as a double greater than 0.0 and (usually) smaller than +// 1.0. A value greater than 1.0 represents "negative" compression and a +// return value of 0.0 indicates that the ratio cannot be determined. + +double WavpackGetRatio (WavpackContext *wpc) +{ + if (wpc && wpc->total_samples != (uint32_t) -1 && wpc->filelen) { + double output_size = (double) wpc->total_samples * wpc->config.num_channels * + wpc->config.bytes_per_sample; + double input_size = (double) wpc->filelen + wpc->file2len; + + if (output_size >= 1.0 && input_size >= 1.0) + return input_size / output_size; + } + + return 0.0; +} + +// Calculate the average bitrate of the WavPack file in bits per second. A +// return of 0.0 indicates that the bitrate cannot be determined. An option is +// provided to use (or not use) any attendant .wvc file. + +double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc) +{ + if (wpc && wpc->total_samples != (uint32_t) -1 && wpc->filelen) { + double output_time = (double) wpc->total_samples / wpc->config.sample_rate; + double input_size = (double) wpc->filelen + (count_wvc ? wpc->file2len : 0); + + if (output_time >= 1.0 && input_size >= 1.0) + return input_size * 8.0 / output_time; + } + + return 0.0; +} + +#ifdef UNPACK + +// Calculate the bitrate of the current WavPack file block in bits per second. +// This can be used for an "instant" bit display and gets updated from about +// 1 to 4 times per second. A return of 0.0 indicates that the bitrate cannot +// be determined. + +double WavpackGetInstantBitrate (WavpackContext *wpc) +{ + if (wpc->stream3) + return WavpackGetAverageBitrate (wpc, TRUE); + + if (wpc && wpc->streams [0] && wpc->streams [0]->wphdr.block_samples) { + double output_time = (double) wpc->streams [0]->wphdr.block_samples / wpc->config.sample_rate; + double input_size = 0; + int si; + + for (si = 0; si < wpc->num_streams; ++si) { + if (wpc->streams [si]->blockbuff) + input_size += ((WavpackHeader *) wpc->streams [si]->blockbuff)->ckSize; + + if (wpc->streams [si]->block2buff) + input_size += ((WavpackHeader *) wpc->streams [si]->block2buff)->ckSize; + } + + if (output_time > 0.0 && input_size >= 1.0) + return input_size * 8.0 / output_time; + } + + return 0.0; +} + +#endif + +// Close the specified WavPack file and release all resources used by it. +// Returns NULL. + +WavpackContext *WavpackCloseFile (WavpackContext *wpc) +{ + free_streams (wpc); + + if (wpc->streams [0]) + free (wpc->streams [0]); + +#ifdef VER3 + if (wpc->stream3) + free_stream3 (wpc); +#endif + +#if defined(UNPACK) || defined(INFO_ONLY) + if (wpc->close_files) { +#ifdef USE_FSTREAMS + if (wpc->wv_in != NULL) + fclose (wpc->wv_in); + + if (wpc->wvc_in != NULL) + fclose (wpc->wvc_in); +#endif + } + + WavpackFreeWrapper (wpc); +#endif + +#ifdef TAGS + free_tag (&wpc->m_tag); +#endif + + free (wpc); + + return NULL; +} + +// 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 even if the +// OPEN_2CH_MAX flag was specified when the file was opened. + +int WavpackGetNumChannels (WavpackContext *wpc) +{ + return wpc ? wpc->config.num_channels : 2; +} + +int WavpackGetFloatNormExp (WavpackContext *wpc) +{ + return wpc->config.float_norm_exp; +} + +// 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 ints, 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; +} + +#if defined(UNPACK) || defined(INFO_ONLY) + +// If the OPEN_2CH_MAX flag is specified when opening the file, 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 +// multichannel file. + +int WavpackGetReducedChannels (WavpackContext *wpc) +{ + if (wpc) + return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels; + else + return 2; +} + +// These routines are used to access (and free) header and trailer data that +// was retrieved from the Wavpack file. The header will be available before +// the samples are decoded and the trailer will be available after all samples +// have been read. + +uint32_t WavpackGetWrapperBytes (WavpackContext *wpc) +{ + return wpc ? wpc->wrapper_bytes : 0; +} + +uchar *WavpackGetWrapperData (WavpackContext *wpc) +{ + return wpc ? wpc->wrapper_data : NULL; +} + +void WavpackFreeWrapper (WavpackContext *wpc) +{ + if (wpc && wpc->wrapper_data) { + free (wpc->wrapper_data); + wpc->wrapper_data = NULL; + wpc->wrapper_bytes = 0; + } +} + +// Get any MD5 checksum stored in the metadata (should be called after reading +// last sample or an extra seek will occur). A return value of FALSE indicates +// that no MD5 checksum was stored. + +static int seek_md5 (stream_reader *reader, void *id, uchar data [16]); + +int WavpackGetMD5Sum (WavpackContext *wpc, uchar data [16]) +{ + if (wpc->config.flags & CONFIG_MD5_CHECKSUM) { + if (wpc->config.md5_read) { + memcpy (data, wpc->config.md5_checksum, 16); + return TRUE; + } + else if (wpc->reader->can_seek (wpc->wv_in)) { + uint32_t pos_save = wpc->reader->get_pos (wpc->wv_in); + + wpc->config.md5_read = seek_md5 (wpc->reader, wpc->wv_in, wpc->config.md5_checksum); + wpc->reader->set_pos_abs (wpc->wv_in, pos_save); + + if (wpc->config.md5_read) { + memcpy (data, wpc->config.md5_checksum, 16); + return TRUE; + } + else + return FALSE; + } + } + + return FALSE; +} + +#endif + +// Free all memory allocated for raw WavPack blocks (for all allocated streams) +// and free all additonal streams. This does not free the default stream ([0]) +// which is always kept around. + +static void free_streams (WavpackContext *wpc) +{ + int si = wpc->num_streams; + + while (si--) { + if (wpc->streams [si]->blockbuff) { + free (wpc->streams [si]->blockbuff); + wpc->streams [si]->blockbuff = NULL; + } + + if (wpc->streams [si]->block2buff) { + free (wpc->streams [si]->block2buff); + wpc->streams [si]->block2buff = NULL; + } + + if (wpc->streams [si]->sample_buffer) { + free (wpc->streams [si]->sample_buffer); + wpc->streams [si]->sample_buffer = NULL; + } + + if (si) { + wpc->num_streams--; + free (wpc->streams [si]); + wpc->streams [si] = NULL; + } + } + + wpc->current_stream = 0; +} + +#ifdef TAGS + +// Return TRUE is a valid ID3v1 or APEv2 tag has been loaded. + +static int valid_tag (M_Tag *m_tag) +{ + if (m_tag->ape_tag_hdr.ID [0] == 'A') + return 'A'; + else if (m_tag->id3_tag.tag_id [0] == 'T') + return 'T'; + else + return 0; +} + +// Free the data for any APEv2 tag that was allocated. + +static void free_tag (M_Tag *m_tag) +{ + if (m_tag->ape_tag_data) { + free (m_tag->ape_tag_data); + m_tag->ape_tag_data = 0; + } +} + +#endif + +#if defined(UNPACK) || defined(INFO_ONLY) + +// 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 (stream_reader *reader, void *id, 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 (reader->read_bytes (id, buffer + bleft, sizeof (*wphdr) - bleft) != sizeof (*wphdr) - bleft) + return -1; + + sp = buffer; + + if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' && + !(*++sp & 1) && sp [2] < 16 && !sp [3] && sp [5] == 4 && sp [4] >= 2 && sp [4] <= 0xf) { + memcpy (wphdr, buffer, sizeof (*wphdr)); + little_endian_to_native (wphdr, WavpackHeaderFormat); + return bytes_skipped; + } + + while (sp < ep && *sp != 'w') + sp++; + + if ((bytes_skipped += sp - buffer) > 1024 * 1024) + return -1; + } +} + +// This function is used to seek to end of a file to determine its actual +// length in samples by reading the last header block containing data. +// Currently, all WavPack files contain the sample length in the first block +// containing samples, however this might not always be the case. Obviously, +// this function requires a seekable file or stream and leaves the file +// pointer undefined. A return value of -1 indicates the length could not +// be determined. + +static uint32_t seek_final_index (stream_reader *reader, void *id) +{ + uint32_t result = (uint32_t) -1, bcount; + WavpackHeader wphdr; + + if (reader->get_length (id) > 1200000L) + reader->set_pos_rel (id, -1048576L, SEEK_END); + + while (1) { + bcount = read_next_header (reader, id, &wphdr); + + if (bcount == (uint32_t) -1) + return result; + + if (wphdr.block_samples && (wphdr.flags & FINAL_BLOCK)) + result = wphdr.block_index + wphdr.block_samples; + + if (wphdr.ckSize > sizeof (WavpackHeader) - 8) + reader->set_pos_rel (id, wphdr.ckSize - sizeof (WavpackHeader) + 8, SEEK_CUR); + } +} + +static int seek_md5 (stream_reader *reader, void *id, uchar data [16]) +{ + uchar meta_id, c1, c2; + uint32_t bcount, meta_bc; + WavpackHeader wphdr; + + if (reader->get_length (id) > 1200000L) + reader->set_pos_rel (id, -1048576L, SEEK_END); + + while (1) { + bcount = read_next_header (reader, id, &wphdr); + + if (bcount == (uint32_t) -1) + return FALSE; + + bcount = wphdr.ckSize - sizeof (WavpackHeader) + 8; + + while (bcount >= 2) { + if (reader->read_bytes (id, &meta_id, 1) != 1 || + reader->read_bytes (id, &c1, 1) != 1) + return FALSE; + + meta_bc = c1 << 1; + bcount -= 2; + + if (meta_id & ID_LARGE) { + if (bcount < 2 || reader->read_bytes (id, &c1, 1) != 1 || + reader->read_bytes (id, &c2, 1) != 1) + return FALSE; + + meta_bc += ((uint32_t) c1 << 9) + ((uint32_t) c2 << 17); + bcount -= 2; + } + + if (meta_id == ID_MD5_CHECKSUM) + return (meta_bc == 16 && bcount >= 16 && + reader->read_bytes (id, data, 16) == 16); + + reader->set_pos_rel (id, meta_bc, SEEK_CUR); + bcount -= meta_bc; + } + } +} + +#ifdef SEEKING + +// Find a valid WavPack header, searching either from the current file position +// (or from the specified position if not -1) and store it (endian corrected) +// at the specified pointer. The return value is the exact file position of the +// header, although we may have actually read past it. Because this function +// is used for seeking to a specific audio sample, it only considers blocks +// that contain audio samples for the initial stream to be valid. + +#define BUFSIZE 4096 + +static uint32_t find_header (stream_reader *reader, void *id, uint32_t filepos, WavpackHeader *wphdr) +{ + char *buffer = malloc (BUFSIZE), *sp = buffer, *ep = buffer; + + if (filepos != (uint32_t) -1 && reader->set_pos_abs (id, filepos)) { + free (buffer); + return -1; + } + + while (1) { + int bleft; + + if (sp < ep) { + bleft = ep - sp; + memcpy (buffer, sp, bleft); + ep -= (sp - buffer); + sp = buffer; + } + else { + if (sp > ep) + if (reader->set_pos_rel (id, sp - ep, SEEK_CUR)) { + free (buffer); + return -1; + } + + sp = ep = buffer; + bleft = 0; + } + + ep += reader->read_bytes (id, ep, BUFSIZE - bleft); + + if (ep - sp < 32) { + free (buffer); + return -1; + } + + while (sp + 32 <= ep) + if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' && + !(*++sp & 1) && sp [2] < 16 && !sp [3] && sp [5] == 4 && sp [4] >= 2 && sp [4] <= 0xf) { + memcpy (wphdr, sp - 4, sizeof (*wphdr)); + little_endian_to_native (wphdr, WavpackHeaderFormat); + + if (wphdr->block_samples && (wphdr->flags & INITIAL_BLOCK)) { + free (buffer); + return reader->get_pos (id) - (ep - sp + 4); + } + + if (wphdr->ckSize > 1024) + sp += wphdr->ckSize - 1024; + } + } +} + +// Find the WavPack block that contains the specified sample. If "header_pos" +// is zero, then no information is assumed except the total number of samples +// in the file and its size in bytes. If "header_pos" is non-zero then we +// assume that it is the file position of the valid header image contained in +// the first stream and we can limit our search to either the portion above +// or below that point. If a .wvc file is being used, then this must be called +// for that file also. + +static uint32_t find_sample (WavpackContext *wpc, void *infile, uint32_t header_pos, uint32_t sample) +{ + WavpackStream *wps = wpc->streams [wpc->current_stream]; + uint32_t file_pos1 = 0, file_pos2 = wpc->reader->get_length (infile); + uint32_t sample_pos1 = 0, sample_pos2 = wpc->total_samples; + double ratio = 0.96; + int file_skip = 0; + + if (sample >= wpc->total_samples) + return -1; + + if (header_pos) { + if (wps->wphdr.block_index > sample) { + sample_pos2 = wps->wphdr.block_index; + file_pos2 = header_pos; + } + else if (wps->wphdr.block_index + wps->wphdr.block_samples <= sample) { + sample_pos1 = wps->wphdr.block_index; + file_pos1 = header_pos; + } + else + return header_pos; + } + + while (1) { + double bytes_per_sample; + uint32_t seek_pos; + + bytes_per_sample = file_pos2 - file_pos1; + bytes_per_sample /= sample_pos2 - sample_pos1; + seek_pos = file_pos1 + (file_skip ? 32 : 0); + seek_pos += (uint32_t)(bytes_per_sample * (sample - sample_pos1) * ratio); + seek_pos = find_header (wpc->reader, infile, seek_pos, &wps->wphdr); + + if (seek_pos == (uint32_t) -1 || seek_pos >= file_pos2) { + if (ratio > 0.0) { + if ((ratio -= 0.24) < 0.0) + ratio = 0.0; + } + else + return -1; + } + else if (wps->wphdr.block_index > sample) { + sample_pos2 = wps->wphdr.block_index; + file_pos2 = seek_pos; + } + else if (wps->wphdr.block_index + wps->wphdr.block_samples <= sample) { + + if (seek_pos == file_pos1) + file_skip = 1; + else { + sample_pos1 = wps->wphdr.block_index; + file_pos1 = seek_pos; + } + } + else + return seek_pos; + } +} + +#endif + +#ifdef TAGS + +// This function attempts to load an ID3v1 or APEv2 tag from the specified +// file into the specified M_Tag structure. The ID3 tag fits in completely, +// but an APEv2 tag is variable length and so space must be allocated here +// to accomodate the data, and this will need to be freed later. A return +// value of TRUE indicates a valid tag was found and loaded. Note that the +// file pointer is undefined when this function exits. + +static int load_tag (WavpackContext *wpc) +{ + M_Tag *m_tag = &wpc->m_tag; + + CLEAR (*m_tag); + + // First, attempt to find an APEv2 tag... + + wpc->reader->set_pos_rel (wpc->wv_in, -sizeof (APE_Tag_Hdr), SEEK_END); + + if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->ape_tag_hdr, sizeof (APE_Tag_Hdr)) == sizeof (APE_Tag_Hdr) && + !strncmp (m_tag->ape_tag_hdr.ID, "APETAGEX", 8)) { + + little_endian_to_native (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format); + + if (m_tag->ape_tag_hdr.version == 2000 && m_tag->ape_tag_hdr.item_count && + m_tag->ape_tag_hdr.length > sizeof (m_tag->ape_tag_hdr) && + m_tag->ape_tag_hdr.length < (1024 * 1024) && + (m_tag->ape_tag_data = malloc (m_tag->ape_tag_hdr.length)) != NULL) { + + memset (m_tag->ape_tag_data, 0, m_tag->ape_tag_hdr.length); + wpc->reader->set_pos_rel (wpc->wv_in, -m_tag->ape_tag_hdr.length, SEEK_END); + + if (wpc->reader->read_bytes (wpc->wv_in, m_tag->ape_tag_data, m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr)) != + m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr)) { + free (m_tag->ape_tag_data); + CLEAR (*m_tag); + return FALSE; + } + else + return TRUE; + } + } + + // ...if not, try a ID3v1 tag + + wpc->reader->set_pos_rel (wpc->wv_in, -sizeof (ID3_Tag), SEEK_END); + + if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->id3_tag, sizeof (ID3_Tag)) == sizeof (ID3_Tag) && + !strncmp (m_tag->id3_tag.tag_id, "TAG", 3)) + return TRUE; + else { + CLEAR (*m_tag); + return FALSE; + } +} + +// Copy the specified ID3v1 tag value (with specified field size) from the +// source pointer to the destination, eliminating leading spaces and trailing +// spaces and nulls. + +static void tagcpy (char *dest, char *src, int tag_size) +{ + char *s1 = src, *s2 = src + tag_size - 1; + + while (s1 <= s2) + if (*s1 == ' ') + ++s1; + else if (!*s2 || *s2 == ' ') + --s2; + else + break; + + while (*s1 && s1 <= s2) + *dest++ = *s1++; + + *dest = 0; +} + +#endif + +#endif diff --git a/Libraries/WavPack/Files/wputils.h b/Libraries/WavPack/Files/wputils.h new file mode 100644 index 000000000..892a793ee --- /dev/null +++ b/Libraries/WavPack/Files/wputils.h @@ -0,0 +1,163 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wputils.h + +#ifndef WPUTILS_H +#define WPUTILS_H + +// This header file contains all the definitions required to use the +// functions in "wputils.c" to read and write WavPack files and streams. + +#include + +#if defined(_WIN32) && !defined(__MINGW32__) +#include +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +typedef float float32_t; +#else +#include +#endif + +typedef unsigned char uchar; + +#if !defined(__GNUC__) || defined(WIN32) +typedef unsigned short ushort; +typedef unsigned int uint; +#endif + +///////////////////////// WavPack Configuration /////////////////////////////// + +// This external 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 { + float bitrate, shaping_weight; + int bits_per_sample, bytes_per_sample; + int qmode, flags, xmode, num_channels, float_norm_exp; + int32_t block_samples, extra_flags, sample_rate, channel_mask; + uchar md5_checksum [16], md5_read; + int num_tag_strings; + char **tag_strings; +} WavpackConfig; + +#define CONFIG_HYBRID_FLAG 8 // hybrid mode +#define CONFIG_JOINT_STEREO 0x10 // joint stereo +#define CONFIG_HYBRID_SHAPE 0x40 // noise shape (hybrid mode only) +#define CONFIG_FAST_FLAG 0x200 // fast mode +#define CONFIG_HIGH_FLAG 0x800 // high quality mode +#define CONFIG_BITRATE_KBPS 0x2000 // bitrate is kbps, not bits / sample +#define CONFIG_SHAPE_OVERRIDE 0x8000 // shaping mode specified +#define CONFIG_JOINT_OVERRIDE 0x10000 // joint-stereo mode specified +#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_EXTRA_MODE 0x2000000 // extra processing mode +#define CONFIG_SKIP_WVX 0x4000000 // no wvx stream w/ floats & big ints + +////////////// Callbacks used for reading & writing WavPack streams ////////// + +typedef struct { + int32_t (*read_bytes)(void *id, void *data, int32_t bcount); + uint32_t (*get_pos)(void *id); + int (*set_pos_abs)(void *id, uint32_t pos); + int (*set_pos_rel)(void *id, int32_t delta, int mode); + int (*push_back_byte)(void *id, int c); + uint32_t (*get_length)(void *id); + int (*can_seek)(void *id); +} stream_reader; + +typedef int (*blockout)(void *id, void *data, int32_t bcount); + +//////////////////////// function prototypes and macros ////////////////////// + +typedef void WavpackContext; + +#ifdef __cplusplus +extern "C" { +#endif + +WavpackContext *WavpackOpenFileInputEx (stream_reader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset); +WavpackContext *WavpackOpenFileInput (const char *infilename, char *error, int flags, int norm_offset); + +#define OPEN_WVC 0x1 // open/read "correction" file +#define OPEN_TAGS 0x2 // read ID3v1 / APEv2 tags (seekable file) +#define OPEN_WRAPPER 0x4 // make audio wrapper available (i.e. RIFF) +#define OPEN_2CH_MAX 0x8 // open multichannel as stereo (no downmix) +#define OPEN_NORMALIZE 0x10 // normalize floating point data to +/- 1.0 +#define OPEN_STREAMING 0x20 // "streaming" mode blindly unpacks blocks + // w/o regard to header file position info + +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 +#define MODE_EXTRA 0x80 +#define MODE_APETAG 0x100 +#define MODE_SFX 0x200 + +int WavpackGetVersion (WavpackContext *wpc); +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); +int WavpackSeekSample (WavpackContext *wpc, uint32_t sample); +WavpackContext *WavpackCloseFile (WavpackContext *wpc); +uint32_t WavpackGetSampleRate (WavpackContext *wpc); +int WavpackGetBitsPerSample (WavpackContext *wpc); +int WavpackGetBytesPerSample (WavpackContext *wpc); +int WavpackGetNumChannels (WavpackContext *wpc); +int WavpackGetReducedChannels (WavpackContext *wpc); +int WavpackGetFloatNormExp (WavpackContext *wpc); +int WavpackGetMD5Sum (WavpackContext *wpc, uchar data [16]); +uint32_t WavpackGetWrapperBytes (WavpackContext *wpc); +uchar *WavpackGetWrapperData (WavpackContext *wpc); +void WavpackFreeWrapper (WavpackContext *wpc); +double WavpackGetProgress (WavpackContext *wpc); +uint32_t WavpackGetFileSize (WavpackContext *wpc); +double WavpackGetRatio (WavpackContext *wpc); +double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc); +double WavpackGetInstantBitrate (WavpackContext *wpc); +int WavpackGetTagItem (WavpackContext *wpc, const char *item, char *value, int size); +int WavpackAppendTagItem (WavpackContext *wpc, const char *item, const char *value); +int WavpackWriteTag (WavpackContext *wpc); + + +WavpackContext *WavpackOpenFileOutput (blockout blockout, void *wv_id, void *wvc_id); +int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, uint32_t total_samples); +int WavpackAddWrapper (WavpackContext *wpc, void *data, uint32_t bcount); +int WavpackStoreMD5Sum (WavpackContext *wpc, uchar data [16]); +int WavpackPackInit (WavpackContext *wpc); +int WavpackPackSamples (WavpackContext *wpc, int32_t *sample_buffer, uint32_t sample_count); +int WavpackFlushSamples (WavpackContext *wpc); +void WavpackUpdateNumSamples (WavpackContext *wpc, void *first_block); +void *WavpackGetWrapperLocation (void *first_block); + +// this function is not actually in wputils.c, but is generally useful + +void float_normalize (int32_t *values, int32_t num_values, int delta_exp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Libraries/WavPack/Files/wvunpack.c b/Libraries/WavPack/Files/wvunpack.c new file mode 100644 index 000000000..d4e776de5 --- /dev/null +++ b/Libraries/WavPack/Files/wvunpack.c @@ -0,0 +1,926 @@ +//////////////////////////////////////////////////////////////////////////// +// **** WAVPACK **** // +// Hybrid Lossless Wavefile Compressor // +// Copyright (c) 1998 - 2005 Conifer Software. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +// wvunpack.c + +// This is the main module for the WavPack command-line decompressor. + +#if defined(WIN32) +#include +#include +#else +#include +#include +#if defined (__GNUC__) +#include +#include +#endif +#endif + +#ifdef __BORLANDC__ +#include +#elif defined(__GNUC__) && !defined(WIN32) +#include +#else +#include +#endif + +#include +#include +#include +#include + +#include "wavpack.h" +#include "md5.h" + +#ifdef DEBUG_ALLOC +#define malloc malloc_db +#define realloc realloc_db +#define free free_db +void *malloc_db (uint32_t size); +void *realloc_db (void *ptr, uint32_t size); +void free_db (void *ptr); +int32_t dump_alloc (void); +static char *strdup (const char *s) + { char *d = malloc (strlen (s) + 1); return strcpy (d, s); } +#endif + +///////////////////////////// local variable storage ////////////////////////// + +static const char *sign_on = "\n" +" WVUNPACK Hybrid Lossless Wavefile Decompressor %s Version %s %s\n" +" Copyright (c) 1998 - 2005 Conifer Software. All Rights Reserved.\n\n"; + +static const char *usage = +" Usage: WVUNPACK [-options] [@]infile[.wv]|- [[@]outfile[.wav]|outpath|-]\n" +" (infile may contain wildcards: ?,*)\n\n" +" Options: -d = delete source file if successful (use with caution!)\n" +" -i = ignore .wvc file (forces hybrid lossy decompression)\n" +" -m = calculate and display MD5 signature; verify if lossless\n" +" -q = quiet (keep console output to a minimum)\n" +" -r = force raw audio decode (skip RIFF headers & trailers)\n" +" -s = display summary information only to stdout (no decode)\n" +#if defined (WIN32) +" -t = copy input file's time stamp to output file(s)\n" +#endif +" -v = verify source data only (no output file created)\n" +" -y = yes to overwrite warning (use with caution!)\n\n" +" Web: Visit www.wavpack.com for latest version and info\n"; + +static char overwrite_all = 0, delete_source = 0, raw_decode = 0, summary = 0, + ignore_wvc = 0, quiet_mode = 0, calc_md5 = 0, copy_time = 0; + +static int num_files, file_index, outbuf_k; + +/////////////////////////// local function declarations /////////////////////// + +static int unpack_file (char *infilename, char *outfilename); +static void display_progress (double file_progress); + +#define NO_ERROR 0L +#define SOFT_ERROR 1 +#define HARD_ERROR 2 + +////////////////////////////////////////////////////////////////////////////// +// The "main" function for the command-line WavPack decompressor. // +////////////////////////////////////////////////////////////////////////////// + +int main (argc, argv) int argc; char **argv; +{ + int verify_only = 0, usage_error = 0, filelist = 0, add_extension = 0; + char *infilename = NULL, *outfilename = NULL; + char outpath, **matches = NULL; + int result, i; + +#ifdef __BORLANDC__ + struct ffblk ffblk; +#elif defined(WIN32) + struct _finddata_t _finddata_t; +#else + glob_t globs; + struct stat fstats; +#endif + + // loop through command-line arguments + + while (--argc) { +#if defined (WIN32) + if ((**++argv == '-' || **argv == '/') && (*argv)[1]) +#else + if ((**++argv == '-') && (*argv)[1]) +#endif + while (*++*argv) + switch (**argv) { + case 'Y': case 'y': + overwrite_all = 1; + break; + + case 'D': case 'd': + delete_source = 1; + break; +#if defined (WIN32) + case 'T': case 't': + copy_time = 1; + break; +#endif + case 'V': case 'v': + verify_only = 1; + break; + + case 'S': case 's': + summary = 1; + break; + + case 'K': case 'k': + outbuf_k = strtol (++*argv, argv, 10); + --*argv; + break; + + case 'M': case 'm': + calc_md5 = 1; + break; + + case 'R': case 'r': + raw_decode = 1; + break; + + case 'Q': case 'q': + quiet_mode = 1; + break; + + case 'I': case 'i': + ignore_wvc = 1; + break; + + default: + error_line ("illegal option: %c !", **argv); + usage_error = 1; + } + else { + if (!infilename) { + infilename = malloc (strlen (*argv) + PATH_MAX); + strcpy (infilename, *argv); + } + else if (!outfilename) { + outfilename = malloc (strlen (*argv) + PATH_MAX); + strcpy (outfilename, *argv); + } + else { + error_line ("extra unknown argument: %s !", *argv); + usage_error = 1; + } + } + } + + // check for various command-line argument problems + + if (verify_only && delete_source) { + error_line ("can't delete in verify mode!"); + delete_source = 0; + } + + if (verify_only && outfilename) { + error_line ("outfile specification and verify mode are incompatible!"); + usage_error = 1; + } + + if (!quiet_mode && !usage_error) + fprintf (stderr, sign_on, VERSION_OS, VERSION_STR, DATE_STR); + + if (!infilename) { + printf ("%s", usage); + return 0; + } + + if (usage_error) { + free (infilename); + return 0; + } + + setup_break (); + + // If the infile specification begins with a '@', then it actually points + // to a file that contains the names of the files to be converted. This + // was included for use by Wim Speekenbrink's frontends, but could be used + // for other purposes. + + if (infilename [0] == '@') { + FILE *list = fopen (infilename+1, "rt"); + int c; + + if (list == NULL) { + error_line ("file %s not found!", infilename+1); + free(infilename); + return 1; + } + + while ((c = getc (list)) != EOF) { + + while (c == '\n') + c = getc (list); + + if (c != EOF) { + char *fname = malloc (PATH_MAX); + int ci = 0; + + do + fname [ci++] = c; + while ((c = getc (list)) != '\n' && c != EOF && ci < PATH_MAX); + + fname [ci++] = '\0'; + matches = realloc (matches, ++num_files * sizeof (*matches)); + matches [num_files - 1] = realloc (fname, ci); + } + } + + fclose (list); + free (infilename); + infilename = NULL; + filelist = 1; + } + else if (*infilename != '-') { // skip this if infile is stdin (-) + if (!filespec_ext (infilename)) + strcat (infilename, ".wv"); + +#ifdef NO_WILDCARDS + matches = malloc (sizeof (*matches)); + matches [num_files++] = infilename; + filelist = 1; +#else + // search for and store any filenames that match the user supplied spec + +#ifdef __BORLANDC__ + if (findfirst (infilename, &ffblk, 0) == 0) { + do { + matches = realloc (matches, ++num_files * sizeof (*matches)); + matches [num_files - 1] = strdup (ffblk.ff_name); + } while (findnext (&ffblk) == 0); + } +#elif defined (WIN32) + if ((i = _findfirst (infilename, &_finddata_t)) != -1L) { + do { + if (!(_finddata_t.attrib & _A_SUBDIR)) { + matches = realloc (matches, ++num_files * sizeof (*matches)); + matches [num_files - 1] = strdup (_finddata_t.name); + } + } while (_findnext (i, &_finddata_t) == 0); + + _findclose (i); + } +#else + i = 0; + if (glob(infilename, 0, NULL, &globs) == 0 && globs.gl_pathc > 0) { + do { + if (stat(globs.gl_pathv[i], &fstats) == 0 && !(fstats.st_mode & S_IFDIR)) { + matches = realloc (matches, ++num_files * sizeof (*matches)); + matches [num_files - 1] = strdup (globs.gl_pathv[i]); + } + } while (i++ < globs.gl_pathc); + } + globfree(&globs); +#endif +#endif + } + else { // handle case of stdin (-) + matches = malloc (sizeof (*matches)); + matches [num_files++] = infilename; + } + + // If the outfile specification begins with a '@', then it actually points + // to a file that contains the output specification. This was included for + // use by Wim Speekenbrink's frontends because certain filenames could not + // be passed on the command-line, but could be used for other purposes. + + if (outfilename && outfilename [0] == '@') { + FILE *list = fopen (outfilename+1, "rt"); + int c; + + if (list == NULL) { + error_line ("file %s not found!", outfilename+1); + free(outfilename); + return 1; + } + + while ((c = getc (list)) == '\n'); + + if (c != EOF) { + int ci = 0; + + do + outfilename [ci++] = c; + while ((c = getc (list)) != '\n' && c != EOF && ci < PATH_MAX); + + outfilename [ci] = '\0'; + } + else { + error_line ("output spec file is empty!"); + free(outfilename); + fclose (list); + return 1; + } + + fclose (list); + } + + // if we found any files to process, this is where we start + + if (num_files) { + int soft_errors = 0; + + if (outfilename && *outfilename != '-') { + outpath = (filespec_path (outfilename) != NULL); + + if (num_files > 1 && !outpath) { + error_line ("%s is not a valid output path", outfilename); + free(outfilename); + return 1; + } + } + else + outpath = 0; + + add_extension = !outfilename || outpath || !filespec_ext (outfilename); + + // loop through and process files in list + + for (file_index = 0; file_index < num_files; ++file_index) { + if (check_break ()) + break; + + // get input filename from list + + if (filelist) + infilename = matches [file_index]; + else if (*infilename != '-') { + *filespec_name (infilename) = '\0'; + strcat (infilename, matches [file_index]); + } + + // generate output filename + + if (outpath) { + strcat (outfilename, filespec_name (matches [file_index])); + + if (filespec_ext (outfilename)) + *filespec_ext (outfilename) = '\0'; + } + else if (!outfilename) { + outfilename = malloc (strlen (infilename) + 10); + strcpy (outfilename, infilename); + + if (filespec_ext (outfilename)) + *filespec_ext (outfilename) = '\0'; + } + + if (outfilename && *outfilename != '-' && add_extension) + strcat (outfilename, raw_decode ? ".raw" : ".wav"); + + if (num_files > 1) + fprintf (stderr, "\n%s:\n", infilename); + + result = unpack_file (infilename, verify_only ? NULL : outfilename); + + if (result == HARD_ERROR) + break; + else if (result == SOFT_ERROR) + ++soft_errors; + + // clean up in preparation for potentially another file + + if (outpath) + *filespec_name (outfilename) = '\0'; + else if (*outfilename != '-') { + free (outfilename); + outfilename = NULL; + } + + free (matches [file_index]); + } + + if (num_files > 1) { + if (soft_errors) + fprintf (stderr, "\n **** warning: errors occurred in %d of %d files! ****\n", soft_errors, num_files); + else if (!quiet_mode) + fprintf (stderr, "\n **** %d files successfully processed ****\n", num_files); + } + + free (matches); + } + else + error_line (filespec_wild (infilename) ? "nothing to do!" : + "file %s not found!", infilename); + + if (outfilename) + free(outfilename); + +#ifdef DEBUG_ALLOC + error_line ("malloc_count = %d", dump_alloc ()); +#endif + + return 0; +} + +// Unpack the specified WavPack input file into the specified output file name. +// This function uses the library routines provided in wputils.c to do all +// unpacking. This function takes care of reformatting the data (which is +// returned in native-endian longs) to the standard little-endian format. This +// function also handles optionally calculating and displaying the MD5 sum of +// the resulting audio data and verifying the sum if a sum was stored in the +// source and lossless compression is used. + +static uchar *format_samples (int bps, uchar *dst, int32_t *src, uint32_t samcnt); +static void dump_summary (WavpackContext *wpc, char *name, FILE *dst); + +extern int delta_blocks [8]; + +static int unpack_file (char *infilename, char *outfilename) +{ + int result = NO_ERROR, md5_diff = FALSE, open_flags = 0, bytes_per_sample, num_channels, wvc_mode, bps; + uint32_t outfile_length, output_buffer_size, bcount, total_unpacked_samples = 0; + uchar *output_buffer = NULL, *output_pointer = NULL; + double dtime, progress = -1.0; + MD5_CTX md5_context; + WavpackContext *wpc; + int32_t *temp_buffer; + char error [80]; + FILE *outfile; + +#ifdef __BORLANDC__ + struct time time1, time2; +#elif defined(WIN32) + struct _timeb time1, time2; +#else + struct timeval time1, time2; + struct timezone timez; +#endif + + // use library to open WavPack file + + if (outfilename && !raw_decode) + open_flags |= OPEN_WRAPPER; + + if (raw_decode) + open_flags |= OPEN_STREAMING; + + if (!ignore_wvc) + open_flags |= OPEN_WVC; + + wpc = WavpackOpenFileInput (infilename, error, open_flags, 0); + + if (!wpc) { + error_line (error); + return SOFT_ERROR; + } + + if (calc_md5) + MD5Init (&md5_context); + + wvc_mode = WavpackGetMode (wpc) & MODE_WVC; + num_channels = WavpackGetNumChannels (wpc); + bps = WavpackGetBytesPerSample (wpc); + bytes_per_sample = num_channels * bps; + + if (summary) { + dump_summary (wpc, infilename, stdout); + WavpackCloseFile (wpc); + return NO_ERROR; + } + + if (outfilename) { + if (*outfilename != '-') { + + // check the output file for overwrite warning required + + if (!overwrite_all && (outfile = fopen (outfilename, "rb")) != NULL) { + DoCloseHandle (outfile); + fprintf (stderr, "overwrite %s (yes/no/all)? ", FN_FIT (outfilename)); + SetConsoleTitle ("overwrite?"); + + switch (yna ()) { + + case 'n': + result = SOFT_ERROR; + break; + + case 'a': + overwrite_all = 1; + } + + if (result != NO_ERROR) { + WavpackCloseFile (wpc); + return result; + } + } + + // open output file for writing + + if ((outfile = fopen (outfilename, "wb")) == NULL) { + error_line ("can't create file %s!", outfilename); + WavpackCloseFile (wpc); + return SOFT_ERROR; + } + else if (!quiet_mode) + fprintf (stderr, "restoring %s,", FN_FIT (outfilename)); + } + else { // come here to open stdout as destination + + outfile = stdout; +#if defined(WIN32) + setmode (fileno (stdout), O_BINARY); +#endif + + if (!quiet_mode) + fprintf (stderr, "unpacking %s%s to stdout,", *infilename == '-' ? + "stdin" : FN_FIT (infilename), wvc_mode ? " (+.wvc)" : ""); + } + + if (outbuf_k) + output_buffer_size = outbuf_k * 1024; + else + output_buffer_size = 1024 * 256; + + output_pointer = output_buffer = malloc (output_buffer_size); + } + else { // in verify only mode we don't worry about headers + outfile = NULL; + + if (!quiet_mode) + fprintf (stderr, "verifying %s%s,", *infilename == '-' ? "stdin" : + FN_FIT (infilename), wvc_mode ? " (+.wvc)" : ""); + } + +#ifdef __BORLANDC__ + gettime (&time1); +#elif defined(WIN32) + _ftime (&time1); +#else + gettimeofday(&time1,&timez); +#endif + + if (WavpackGetWrapperBytes (wpc)) { + if (outfile && (!DoWriteFile (outfile, WavpackGetWrapperData (wpc), WavpackGetWrapperBytes (wpc), &bcount) || + bcount != WavpackGetWrapperBytes (wpc))) { + error_line ("can't write .WAV data, disk probably full!"); + DoTruncateFile (outfile); + result = HARD_ERROR; + } + + WavpackFreeWrapper (wpc); + } + + temp_buffer = malloc (4096L * num_channels * 4); + + while (result == NO_ERROR) { + uint32_t samples_to_unpack, samples_unpacked; + + if (output_buffer) { + samples_to_unpack = (output_buffer_size - (output_pointer - output_buffer)) / bytes_per_sample; + + if (samples_to_unpack > 4096) + samples_to_unpack = 4096; + } + else + samples_to_unpack = 4096; + + samples_unpacked = WavpackUnpackSamples (wpc, temp_buffer, samples_to_unpack); + total_unpacked_samples += samples_unpacked; + + if (output_buffer) { + if (samples_unpacked) + output_pointer = format_samples (bps, output_pointer, temp_buffer, samples_unpacked * num_channels); + + if (!samples_unpacked || (output_buffer_size - (output_pointer - output_buffer)) < bytes_per_sample) { + if (!DoWriteFile (outfile, output_buffer, output_pointer - output_buffer, &bcount) || + bcount != output_pointer - output_buffer) { + error_line ("can't write .WAV data, disk probably full!"); + DoTruncateFile (outfile); + result = HARD_ERROR; + break; + } + + output_pointer = output_buffer; + } + } + + if (calc_md5 && samples_unpacked) { + format_samples (bps, (uchar *) temp_buffer, temp_buffer, samples_unpacked * num_channels); + MD5Update (&md5_context, temp_buffer, bps * samples_unpacked * num_channels); + } + + if (!samples_unpacked) + break; + + if (check_break ()) { + fprintf (stderr, "^C\n"); + DoTruncateFile (outfile); + result = SOFT_ERROR; + break; + } + + if (WavpackGetProgress (wpc) != -1.0 && + progress != floor (WavpackGetProgress (wpc) * 100.0 + 0.5)) { + int nobs = progress == -1.0; + + progress = WavpackGetProgress (wpc); + display_progress (progress); + progress = floor (progress * 100.0 + 0.5); + + if (!quiet_mode) + fprintf (stderr, "%s%3d%% done...", + nobs ? " " : "\b\b\b\b\b\b\b\b\b\b\b\b", (int) progress); + } + } + + free (temp_buffer); + + if (output_buffer) + free (output_buffer); + +if (0) { +int i; +for (i = 0; i < 8; ++i) + error_line ("delta = %d, count = %d", i, delta_blocks [i]); +} + + if (!check_break () && calc_md5) { + char md5_string1 [] = "00000000000000000000000000000000"; + char md5_string2 [] = "00000000000000000000000000000000"; + uchar md5_original [16], md5_unpacked [16]; + int i; + + MD5Final (md5_unpacked, &md5_context); + + if (WavpackGetMD5Sum (wpc, md5_original)) { + + for (i = 0; i < 16; ++i) + sprintf (md5_string1 + (i * 2), "%02x", md5_original [i]); + + error_line ("original md5: %s", md5_string1); + + if (memcmp (md5_unpacked, md5_original, 16)) + md5_diff = TRUE; + } + + for (i = 0; i < 16; ++i) + sprintf (md5_string2 + (i * 2), "%02x", md5_unpacked [i]); + + error_line ("unpacked md5: %s", md5_string2); + } + + if (WavpackGetWrapperBytes (wpc)) { + if (outfile && result == NO_ERROR && + (!DoWriteFile (outfile, WavpackGetWrapperData (wpc), WavpackGetWrapperBytes (wpc), &bcount) || + bcount != WavpackGetWrapperBytes (wpc))) { + error_line ("can't write .WAV data, disk probably full!"); + DoTruncateFile (outfile); + result = HARD_ERROR; + } + + WavpackFreeWrapper (wpc); + } + + // if we are not just in verify only mode, grab the size of the output + // file and close the file + + if (outfile != NULL) { + fflush (outfile); + outfile_length = DoGetFileSize (outfile); + + if (!DoCloseHandle (outfile)) { + error_line ("can't close file!"); + result = SOFT_ERROR; + } + + if (outfilename && *outfilename != '-' && !outfile_length) + DoDeleteFile (outfilename); + } + +#if defined (WIN32) + if (result == NO_ERROR && copy_time && outfilename && + !copy_timestamp (infilename, outfilename)) + error_line ("failure copying time stamp!"); +#endif + + if (result == NO_ERROR && WavpackGetNumSamples (wpc) != (uint32_t) -1 && + total_unpacked_samples != WavpackGetNumSamples (wpc)) { + error_line ("incorrect number of samples!"); + result = SOFT_ERROR; + } + + if (result == NO_ERROR && WavpackGetNumErrors (wpc)) { + error_line ("crc errors detected in %d block(s)!", WavpackGetNumErrors (wpc)); + result = SOFT_ERROR; + } + else if (result == NO_ERROR && md5_diff && (WavpackGetMode (wpc) & MODE_LOSSLESS)) { + error_line ("MD5 signatures should match, but do not!"); + result = SOFT_ERROR; + } + + // Compute and display the time consumed along with some other details of + // the unpacking operation (assuming there was no error). + +#ifdef __BORLANDC__ + gettime (&time2); + dtime = time2.ti_sec * 100.0 + time2.ti_hund + time2.ti_min * 6000.0 + time2.ti_hour * 360000.00; + dtime -= time1.ti_sec * 100.0 + time1.ti_hund + time1.ti_min * 6000.0 + time1.ti_hour * 360000.00; + + if ((dtime /= 100.0) < 0.0) + dtime += 86400.0; +#elif defined(WIN32) + _ftime (&time2); + dtime = time2.time + time2.millitm / 1000.0; + dtime -= time1.time + time1.millitm / 1000.0; +#else + gettimeofday(&time2,&timez); + dtime = time2.tv_sec + time2.tv_usec / 1000000.0; + dtime -= time1.tv_sec + time1.tv_usec / 1000000.0; +#endif + + if (result == NO_ERROR && !quiet_mode) { + char *file, *fext, *oper, *cmode, cratio [16] = ""; + + if (outfilename && *outfilename != '-') { + file = FN_FIT (outfilename); + fext = ""; + oper = "restored"; + } + else { + file = (*infilename == '-') ? "stdin" : FN_FIT (infilename); + fext = wvc_mode ? " (+.wvc)" : ""; + oper = outfilename ? "unpacked" : "verified"; + } + + if (WavpackGetMode (wpc) & MODE_LOSSLESS) { + cmode = "lossless"; + + if (WavpackGetRatio (wpc) != 0.0) + sprintf (cratio, ", %.2f%%", 100.0 - WavpackGetRatio (wpc) * 100.0); + } + else { + cmode = "lossy"; + + if (WavpackGetAverageBitrate (wpc, TRUE) != 0.0) + sprintf (cratio, ", %d kbps", (int) (WavpackGetAverageBitrate (wpc, TRUE) / 1000.0)); + } + + error_line ("%s %s%s in %.2f secs (%s%s)", oper, file, fext, dtime, cmode, cratio); + } + + WavpackCloseFile (wpc); + + if (result == NO_ERROR && delete_source) { + error_line ("%s source file %s", DoDeleteFile (infilename) ? + "deleted" : "can't delete", infilename); + + if (wvc_mode) { + char in2filename [PATH_MAX]; + + strcpy (in2filename, infilename); + strcat (in2filename, "c"); + + error_line ("%s source file %s", DoDeleteFile (in2filename) ? + "deleted" : "can't delete", in2filename); + } + } + + return result; +} + +// 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; +} + +static void dump_summary (WavpackContext *wpc, char *name, FILE *dst) +{ + int num_channels = WavpackGetNumChannels (wpc); + uchar md5_sum [16], modes [80]; + + fprintf (dst, "\n"); + + if (name && *name != '-') { + fprintf (dst, "file name: %s%s\n", name, (WavpackGetMode (wpc) & MODE_WVC) ? " (+wvc)" : ""); + fprintf (dst, "file size: %lu bytes\n", WavpackGetFileSize (wpc)); + } + + fprintf (dst, "source: %d-bit %s at %ld Hz\n", WavpackGetBitsPerSample (wpc), + (WavpackGetMode (wpc) & MODE_FLOAT) ? "floats" : "ints", + WavpackGetSampleRate (wpc)); + + fprintf (dst, "channels: %d (%s)\n", num_channels, + num_channels > 2 ? "multichannel" : (num_channels == 1 ? "mono" : "stereo")); + + if (WavpackGetNumSamples (wpc) != (uint32_t) -1) { + double seconds = (double) WavpackGetNumSamples (wpc) / WavpackGetSampleRate (wpc); + int minutes = (int) floor (seconds / 60.0); + int hours = (int) floor (seconds / 3600.0); + + seconds -= minutes * 60.0; + minutes -= hours * 60.0; + + fprintf (dst, "duration: %d:%02d:%0.2f\n", hours, minutes, seconds); + } + + modes [0] = 0; + + if (WavpackGetMode (wpc) & MODE_HYBRID) + strcat (modes, "hybrid "); + + strcat (modes, (WavpackGetMode (wpc) & MODE_LOSSLESS) ? "lossless" : "lossy"); + + if (WavpackGetMode (wpc) & MODE_FAST) + strcat (modes, ", fast"); + else if (WavpackGetMode (wpc) & MODE_HIGH) + strcat (modes, ", high"); + + if (WavpackGetMode (wpc) & MODE_EXTRA) + strcat (modes, ", extra"); + + if (WavpackGetMode (wpc) & MODE_SFX) + strcat (modes, ", sfx"); + + fprintf (dst, "modalities: %s\n", modes); + + if (WavpackGetRatio (wpc) != 0.0) { + fprintf (dst, "compression: %.2f%%\n", 100.0 - (100 * WavpackGetRatio (wpc))); + fprintf (dst, "ave bitrate: %d kbps\n", (int) ((WavpackGetAverageBitrate (wpc, TRUE) + 500.0) / 1000.0)); + + if (WavpackGetMode (wpc) & MODE_WVC) + fprintf (dst, "ave lossy bitrate: %d kbps\n", (int) ((WavpackGetAverageBitrate (wpc, FALSE) + 500.0) / 1000.0)); + } + + if (WavpackGetVersion (wpc)) + fprintf (dst, "encoder version: %d\n", WavpackGetVersion (wpc)); + + if (WavpackGetMD5Sum (wpc, md5_sum)) { + char md5_string [] = "00000000000000000000000000000000"; + int i; + + for (i = 0; i < 16; ++i) + sprintf (md5_string + (i * 2), "%02x", md5_sum [i]); + + fprintf (dst, "original md5: %s\n", md5_string); + } +} + +////////////////////////////////////////////////////////////////////////////// +// This function displays the progress status on the title bar of the DOS // +// window that WavPack is running in. The "file_progress" argument is for // +// the current file only and ranges from 0 - 1; this function takes into // +// account the total number of files to generate a batch progress number. // +////////////////////////////////////////////////////////////////////////////// + +void display_progress (double file_progress) +{ + char title [40]; + + file_progress = (file_index + file_progress) / num_files; + sprintf (title, "%d%% (WvUnpack)", (int) ((file_progress * 100.0) + 0.5)); + SetConsoleTitle (title); +}