diff --git a/.gitmodules b/.gitmodules index 498a32410..c78fee42a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,6 +15,3 @@ [submodule "extern/portaudio"] path = extern/portaudio url = https://github.com/PortAudio/portaudio.git -[submodule "extern/adpcm-xq"] - path = extern/adpcm-xq - url = https://github.com/dbry/adpcm-xq.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 49a4e22bc..8f1a53660 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -486,7 +486,7 @@ extern/adpcm/yma_codec.c extern/adpcm/ymb_codec.c extern/adpcm/ymz_codec.c -extern/adpcm-xq/adpcm-lib.c +extern/adpcm-xq-s/adpcm-lib.c extern/opn/ym3438.c extern/Nuked-PSG/ympsg.c diff --git a/extern/adpcm-xq b/extern/adpcm-xq deleted file mode 160000 index 6220fed76..000000000 --- a/extern/adpcm-xq +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6220fed7655e86a29702b45dbc641a028ed5a4bf diff --git a/extern/adpcm-xq-s/.gitignore b/extern/adpcm-xq-s/.gitignore new file mode 100644 index 000000000..d16386367 --- /dev/null +++ b/extern/adpcm-xq-s/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/extern/adpcm-xq-s/CMakeLists.txt b/extern/adpcm-xq-s/CMakeLists.txt new file mode 100644 index 000000000..309eebf50 --- /dev/null +++ b/extern/adpcm-xq-s/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.16) + +# set the project name +project(adpcm-xq) + +# lots of warnings and all warnings as errors +# add_compile_options(-Wall -Wextra ) + +# define as library +add_library (adpcm-xq adpcm-xq.c) + +# build executable +add_executable (adpcm-xq-exe adpcm-lib.c adpcm-xq.c) +set_property(TARGET adpcm-xq-exe PROPERTY OUTPUT_NAME adpcm-xq) + +# define location for header files +target_include_directories(adpcm-xq PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) + diff --git a/extern/adpcm-xq-s/README b/extern/adpcm-xq-s/README new file mode 100644 index 000000000..c15f53916 --- /dev/null +++ b/extern/adpcm-xq-s/README @@ -0,0 +1,63 @@ +# MODIFIED + +this is a modified version of adpcm-xq which fixes memory safety issues. + +//////////////////////////////////////////////////////////////////////////// +// **** ADPCM-XQ **** // +// Xtreme Quality ADPCM Encoder/Decoder // +// Copyright (c) 2022 David Bryant. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +While very popular at the end of the last century, ADPCM is no longer a +common audio encoding format, and is certainly not recommended as a general +purpose encoder. However, it requires minimal CPU resources for decoding, +and so still is ideally suited for certain embedded games and applications +that contain canned audio samples. + +This encoder combines two different techniques to achieve higher quality +than existing ADPCM encoders while remaining fully compatible with standard +decoders. The first is dynamic noise shaping, which shifts the quantization +noise up or down in frequency based on the spectrum of the source signal. +This technique is identical to the algorithm used in WavPack's lossy mode +and can make any audible quantization noise much less annoying (or, in some +cases, inaudible). + +The other technique is "lookahead" in which the encoder exhaustively +searches ahead to find the optimum coding sequence based on future samples. +This process can reduce the quantization noise from about 1 to 6 dB (depending +on the source) and also reduces or eliminates the harmonic content in the +noise that sometimes plagues ADPCM. Unfortunately, at its maximum settings +this can be very slow, but this should be relatively irrelevant if the +encoder is being used to generate canned samples. + +Adpcm-xq consists of two standard C files and builds with a single command +on most platforms. It has been designed with maximum portability in mind +and should work correctly even on 16-bit and big-endian architectures +(WAV files are of course always little-endian). + +Linux: +% gcc -O2 *.c -o adpcm-xq + +Darwin/Mac: +% clang -O2 *.c -o adpcm-xq + +MS Visual Studio: +cl -O2 adpcm-xq.c adpcm-lib.c + +Bugs: + +1. Unknown RIFF chunk types are correctly parsed on input files, but are not + passed to the output file. + +2. The lookahead feature does not work for the last samples in an ADPCM + block (i.e. it doesn't utilize samples in the _next_ block). + +3. In some situations the lookahead can get very slow or seem to be stuck + because it needs improved trellis pruning. However the default level 3 + should always be fine and then the user can simply try increasing levels + until the time becomes untenable. + +4. Pipes are not yet supported. + diff --git a/extern/adpcm-xq-s/adpcm-lib.c b/extern/adpcm-xq-s/adpcm-lib.c new file mode 100644 index 000000000..7ba78ed39 --- /dev/null +++ b/extern/adpcm-xq-s/adpcm-lib.c @@ -0,0 +1,415 @@ +//////////////////////////////////////////////////////////////////////////// +// **** ADPCM-XQ **** // +// Xtreme Quality ADPCM Encoder/Decoder // +// Copyright (c) 2022 David Bryant. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "adpcm-lib.h" + +/* This module encodes and decodes 4-bit ADPCM (DVI/IMA varient). ADPCM data is divided + * into independently decodable blocks that can be relatively small. The most common + * configuration is to store 505 samples into a 256 byte block, although other sizes are + * permitted as long as the number of samples is one greater than a multiple of 8. When + * multiple channels are present, they are interleaved in the data with an 8-sample + * interval. + */ + +/********************************* 4-bit ADPCM encoder ********************************/ + +#define CLIP(data, min, max) \ +if ((data) > (max)) data = max; \ +else if ((data) < (min)) data = min; + +/* step table */ +static const uint16_t step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767 +}; + +/* step index tables */ +static const int index_table[] = { + /* adpcm data size is 4 */ + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +struct adpcm_channel { + int32_t pcmdata; // current PCM value + int32_t error, weight, history [2]; // for noise shaping + int8_t index; // current index into step size table +}; + +struct adpcm_context { + struct adpcm_channel channels [2]; + int num_channels, lookahead, noise_shaping; +}; + +/* Create ADPCM encoder context with given number of channels. + * The returned pointer is used for subsequent calls. Note that + * even though an ADPCM encoder could be set up to encode frames + * independently, we use a context so that we can use previous + * data to improve quality (this encoder might not be optimal + * for encoding independent frames). + */ + +void *adpcm_create_context (int num_channels, int lookahead, int noise_shaping, int32_t initial_deltas [2]) +{ + struct adpcm_context *pcnxt = malloc (sizeof (struct adpcm_context)); + int ch, i; + + memset (pcnxt, 0, sizeof (struct adpcm_context)); + pcnxt->noise_shaping = noise_shaping; + pcnxt->num_channels = num_channels; + pcnxt->lookahead = lookahead; + + // given the supplied initial deltas, search for and store the closest index + + for (ch = 0; ch < num_channels; ++ch) + for (i = 0; i <= 88; i++) + if (i == 88 || initial_deltas [ch] < ((int32_t) step_table [i] + step_table [i+1]) / 2) { + pcnxt->channels [ch].index = i; + break; + } + + return pcnxt; +} + +/* Free the ADPCM encoder context. + */ + +void adpcm_free_context (void *p) +{ + struct adpcm_context *pcnxt = (struct adpcm_context *) p; + + free (pcnxt); +} + +static void set_decode_parameters (struct adpcm_context *pcnxt, int32_t *init_pcmdata, int8_t *init_index) +{ + int ch; + + for (ch = 0; ch < pcnxt->num_channels; ch++) { + pcnxt->channels[ch].pcmdata = init_pcmdata[ch]; + pcnxt->channels[ch].index = init_index[ch]; + } +} + +static void get_decode_parameters (struct adpcm_context *pcnxt, int32_t *init_pcmdata, int8_t *init_index) +{ + int ch; + + for (ch = 0; ch < pcnxt->num_channels; ch++) { + init_pcmdata[ch] = pcnxt->channels[ch].pcmdata; + init_index[ch] = pcnxt->channels[ch].index; + } +} + +static double minimum_error (const struct adpcm_channel *pchan, int nch, int32_t csample, const int16_t *sample, int depth, int *best_nibble) +{ + int32_t delta = csample - pchan->pcmdata; + struct adpcm_channel chan = *pchan; + uint16_t step = step_table[chan.index]; + uint16_t trial_delta = (step >> 3); + int nibble, nibble2; + double min_error; + + if (delta < 0) { + int mag = (-delta << 2) / step; + nibble = 0x8 | (mag > 7 ? 7 : mag); + } + else { + int mag = (delta << 2) / step; + nibble = mag > 7 ? 7 : mag; + } + + if (nibble & 1) trial_delta += (step >> 2); + if (nibble & 2) trial_delta += (step >> 1); + if (nibble & 4) trial_delta += step; + + if (nibble & 8) + chan.pcmdata -= trial_delta; + else + chan.pcmdata += trial_delta; + + CLIP(chan.pcmdata, -32768, 32767); + if (best_nibble) *best_nibble = nibble; + min_error = (double) (chan.pcmdata - csample) * (chan.pcmdata - csample); + + if (depth) { + chan.index += index_table[nibble & 0x07]; + CLIP(chan.index, 0, 88); + min_error += minimum_error (&chan, nch, sample [nch], sample + nch, depth - 1, NULL); + } + else + return min_error; + + for (nibble2 = 0; nibble2 <= 0xF; ++nibble2) { + double error; + + if (nibble2 == nibble) + continue; + + chan = *pchan; + trial_delta = (step >> 3); + + if (nibble2 & 1) trial_delta += (step >> 2); + if (nibble2 & 2) trial_delta += (step >> 1); + if (nibble2 & 4) trial_delta += step; + + if (nibble2 & 8) + chan.pcmdata -= trial_delta; + else + chan.pcmdata += trial_delta; + + CLIP(chan.pcmdata, -32768, 32767); + + error = (double) (chan.pcmdata - csample) * (chan.pcmdata - csample); + + if (error < min_error) { + chan.index += index_table[nibble2 & 0x07]; + CLIP(chan.index, 0, 88); + error += minimum_error (&chan, nch, sample [nch], sample + nch, depth - 1, NULL); + + if (error < min_error) { + if (best_nibble) *best_nibble = nibble2; + min_error = error; + } + } + } + + return min_error; +} + +static uint8_t encode_sample (struct adpcm_context *pcnxt, int ch, const int16_t *sample, int num_samples) +{ + struct adpcm_channel *pchan = pcnxt->channels + ch; + int32_t csample = *sample; + int depth = num_samples - 1, nibble; + uint16_t step = step_table[pchan->index]; + uint16_t trial_delta = (step >> 3); + + if (pcnxt->noise_shaping == NOISE_SHAPING_DYNAMIC) { + int32_t sam = (3 * pchan->history [0] - pchan->history [1]) >> 1; + int32_t temp = csample - (((pchan->weight * sam) + 512) >> 10); + int32_t shaping_weight; + + if (sam && temp) pchan->weight -= (((sam ^ temp) >> 29) & 4) - 2; + pchan->history [1] = pchan->history [0]; + pchan->history [0] = csample; + + shaping_weight = (pchan->weight < 256) ? 1024 : 1536 - (pchan->weight * 2); + temp = -((shaping_weight * pchan->error + 512) >> 10); + + if (shaping_weight < 0 && temp) { + if (temp == pchan->error) + temp = (temp < 0) ? temp + 1 : temp - 1; + + pchan->error = -csample; + csample += temp; + } + else + pchan->error = -(csample += temp); + } + else if (pcnxt->noise_shaping == NOISE_SHAPING_STATIC) + pchan->error = -(csample -= pchan->error); + + if (depth > pcnxt->lookahead) + depth = pcnxt->lookahead; + + minimum_error (pchan, pcnxt->num_channels, csample, sample, depth, &nibble); + + if (nibble & 1) trial_delta += (step >> 2); + if (nibble & 2) trial_delta += (step >> 1); + if (nibble & 4) trial_delta += step; + + if (nibble & 8) + pchan->pcmdata -= trial_delta; + else + pchan->pcmdata += trial_delta; + + pchan->index += index_table[nibble & 0x07]; + CLIP(pchan->index, 0, 88); + CLIP(pchan->pcmdata, -32768, 32767); + + if (pcnxt->noise_shaping) + pchan->error += pchan->pcmdata; + + return nibble; +} + +static void encode_chunks (struct adpcm_context *pcnxt, uint8_t **outbuf, size_t *outbufsize, const int16_t **inbuf, int inbufcount) +{ + const int16_t *pcmbuf; + int chunks, ch, i; + + chunks = (inbufcount - 1) / 8; + *outbufsize += (chunks * 4) * pcnxt->num_channels; + + while (chunks--) + { + for (ch = 0; ch < pcnxt->num_channels; ch++) + { + pcmbuf = *inbuf + ch; + + for (i = 0; i < 4; i++) { + **outbuf = encode_sample (pcnxt, ch, pcmbuf, chunks * 8 + (3 - i) * 2 + 2); + pcmbuf += pcnxt->num_channels; + **outbuf |= encode_sample (pcnxt, ch, pcmbuf, chunks * 8 + (3 - i) * 2 + 1) << 4; + pcmbuf += pcnxt->num_channels; + (*outbuf)++; + } + } + + *inbuf += 8 * pcnxt->num_channels; + } +} + +/* Encode a block of 16-bit PCM data into 4-bit ADPCM. + * + * Parameters: + * p the context returned by adpcm_begin() + * outbuf destination buffer + * outbufsize pointer to variable where the number of bytes written + * will be stored + * inbuf source PCM samples + * inbufcount number of composite PCM samples provided (note: this is + * the total number of 16-bit samples divided by the number + * of channels) + * + * Returns 1 (for success as there is no error checking) + */ + +int adpcm_encode_block (void *p, uint8_t *outbuf, size_t *outbufsize, const int16_t *inbuf, int inbufcount) +{ + struct adpcm_context *pcnxt = (struct adpcm_context *) p; + int32_t init_pcmdata[2]; + int8_t init_index[2]; + int ch; + + *outbufsize = 0; + + if (!inbufcount) + return 1; + + get_decode_parameters(pcnxt, init_pcmdata, init_index); + + for (ch = 0; ch < pcnxt->num_channels; ch++) { + init_pcmdata[ch] = *inbuf++; + outbuf[0] = init_pcmdata[ch]; + outbuf[1] = init_pcmdata[ch] >> 8; + outbuf[2] = init_index[ch]; + outbuf[3] = 0; + + outbuf += 4; + *outbufsize += 4; + } + + set_decode_parameters(pcnxt, init_pcmdata, init_index); + encode_chunks (pcnxt, &outbuf, outbufsize, &inbuf, inbufcount); + + return 1; +} + +/********************************* 4-bit ADPCM decoder ********************************/ + +/* Decode the block of ADPCM data into PCM. This requires no context because ADPCM blocks + * are indeppendently decodable. This assumes that a single entire block is always decoded; + * it must be called multiple times for multiple blocks and cannot resume in the middle of a + * block. + * + * Parameters: + * outbuf destination for interleaved PCM samples + * inbuf source ADPCM block + * inbufsize size of source ADPCM block + * channels number of channels in block (must be determined from other context) + * + * Returns number of converted composite samples (total samples divided by number of channels) + */ + +int adpcm_decode_block (int16_t *outbuf, const uint8_t *inbuf, size_t inbufsize, int channels) +{ + int ch, samples = 1, chunks; + int32_t pcmdata[2]; + int8_t index[2]; + + if (inbufsize < (uint32_t) channels * 4) + return 0; + + for (ch = 0; ch < channels; ch++) { + *outbuf++ = pcmdata[ch] = (int16_t) (inbuf [0] | (inbuf [1] << 8)); + index[ch] = inbuf [2]; + + if (index [ch] < 0 || index [ch] > 88 || inbuf [3]) // sanitize the input a little... + return 0; + + inbufsize -= 4; + inbuf += 4; + } + + chunks = inbufsize / (channels * 4); + samples += chunks * 8; + + while (chunks--) { + int ch, i; + + for (ch = 0; ch < channels; ++ch) { + + for (i = 0; i < 4; ++i) { + uint16_t step = step_table [index [ch]], delta = step >> 3; + + if (*inbuf & 1) delta += (step >> 2); + if (*inbuf & 2) delta += (step >> 1); + if (*inbuf & 4) delta += step; + + if (*inbuf & 8) + pcmdata[ch] -= delta; + else + pcmdata[ch] += delta; + + index[ch] += index_table [*inbuf & 0x7]; + CLIP(index[ch], 0, 88); + CLIP(pcmdata[ch], -32768, 32767); + outbuf [i * 2 * channels] = pcmdata[ch]; + + step = step_table [index [ch]]; delta = step >> 3; + + if (*inbuf & 0x10) delta += (step >> 2); + if (*inbuf & 0x20) delta += (step >> 1); + if (*inbuf & 0x40) delta += step; + + if (*inbuf & 0x80) + pcmdata[ch] -= delta; + else + pcmdata[ch] += delta; + + index[ch] += index_table [(*inbuf >> 4) & 0x7]; + CLIP(index[ch], 0, 88); + CLIP(pcmdata[ch], -32768, 32767); + outbuf [(i * 2 + 1) * channels] = pcmdata[ch]; + + inbuf++; + } + + outbuf++; + } + + outbuf += channels * 7; + } + + return samples; +} + diff --git a/extern/adpcm-xq-s/adpcm-lib.h b/extern/adpcm-xq-s/adpcm-lib.h new file mode 100644 index 000000000..120242eb2 --- /dev/null +++ b/extern/adpcm-xq-s/adpcm-lib.h @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////// +// **** ADPCM-XQ **** // +// Xtreme Quality ADPCM Encoder/Decoder // +// Copyright (c) 2015 David Bryant. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +#ifndef ADPCMLIB_H_ +#define ADPCMLIB_H_ + +#define NOISE_SHAPING_OFF 0 // flat noise (no shaping) +#define NOISE_SHAPING_STATIC 1 // first-order highpass shaping +#define NOISE_SHAPING_DYNAMIC 2 // dynamically tilted noise based on signal + +#if defined(_MSC_VER) && _MSC_VER < 1600 +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; +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +void *adpcm_create_context (int num_channels, int lookahead, int noise_shaping, int32_t initial_deltas [2]); +int adpcm_encode_block (void *p, uint8_t *outbuf, size_t *outbufsize, const int16_t *inbuf, int inbufcount); +int adpcm_decode_block (int16_t *outbuf, const uint8_t *inbuf, size_t inbufsize, int channels); +void adpcm_free_context (void *p); + +#ifdef __cplusplus +} +#endif + + +#endif /* ADPCMLIB_H_ */ diff --git a/extern/adpcm-xq-s/adpcm-xq.c b/extern/adpcm-xq-s/adpcm-xq.c new file mode 100644 index 000000000..38d0b2225 --- /dev/null +++ b/extern/adpcm-xq-s/adpcm-xq.c @@ -0,0 +1,819 @@ +#ifndef ARDUINO + +//////////////////////////////////////////////////////////////////////////// +// **** ADPCM-XQ **** // +// Xtreme Quality ADPCM Encoder/Decoder // +// Copyright (c) 2022 David Bryant. // +// All Rights Reserved. // +// Distributed under the BSD Software License (see license.txt) // +//////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "adpcm-lib.h" + +// This runtime macro is not strictly needed because the code is endian-safe, +// but including it improves performance on little-endian systems because we +// can avoid a couple loops through the audio. +#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x0100) + +static const char *sign_on = "\n" +" ADPCM-XQ Xtreme Quality IMA-ADPCM WAV Encoder / Decoder Version 0.4\n" +" Copyright (c) 2022 David Bryant. All Rights Reserved.\n\n"; + +static const char *usage = +" Usage: ADPCM-XQ [-options] infile.wav outfile.wav\n\n" +" Operation: conversion is performed based on the type of the infile\n" +" (either encode 16-bit PCM to 4-bit IMA-ADPCM or decode back)\n\n" +" Options: -[0-8] = encode lookahead samples (default = 3)\n" +" -bn = override auto block size, 2^n bytes (n = 8-15)\n" +" -d = decode only (fail on WAV file already PCM)\n" +" -e = encode only (fail on WAV file already ADPCM)\n" +" -f = encode flat noise (no dynamic noise shaping)\n" +" -h = display this help message\n" +" -q = quiet mode (display errors only)\n" +" -r = raw output (little-endian, no WAV header written)\n" +" -v = verbose (display lots of info)\n" +" -y = overwrite outfile if it exists\n\n" +" Web: Visit www.github.com/dbry/adpcm-xq for latest version and info\n\n"; + +#define ADPCM_FLAG_NOISE_SHAPING 0x1 +#define ADPCM_FLAG_RAW_OUTPUT 0x2 + +static int adpcm_converter (char *infilename, char *outfilename, int flags, int blocksize_pow2, int lookahead); +static int verbosity = 0, decode_only = 0, encode_only = 0; + +int main (argc, argv) int argc; char **argv; +{ + int lookahead = 3, flags = ADPCM_FLAG_NOISE_SHAPING, blocksize_pow2 = 0, overwrite = 0, asked_help = 0; + char *infilename = NULL, *outfilename = NULL; + FILE *outfile; + + // if the name of the executable ends in "encoder" or "decoder", just do that function + encode_only = argc && strstr (argv [0], "encoder") && strlen (strstr (argv [0], "encoder")) == strlen ("encoder"); + decode_only = argc && strstr (argv [0], "decoder") && strlen (strstr (argv [0], "decoder")) == strlen ("decoder"); + + // 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 '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + lookahead = **argv - '0'; + break; + + case 'B': case 'b': + blocksize_pow2 = strtol (++*argv, argv, 10); + + if (blocksize_pow2 < 8 || blocksize_pow2 > 15) { + fprintf (stderr, "\nblock size power must be 8 to 15!\n"); + return -1; + } + + --*argv; + break; + + case 'D': case 'd': + decode_only = 1; + break; + + case 'E': case 'e': + encode_only = 1; + break; + + case 'F': case 'f': + flags &= ~ADPCM_FLAG_NOISE_SHAPING; + break; + + case 'H': case 'h': + asked_help = 0; + break; + + case 'Q': case 'q': + verbosity = -1; + break; + + case 'R': case 'r': + flags |= ADPCM_FLAG_RAW_OUTPUT; + break; + + case 'V': case 'v': + verbosity = 1; + break; + + case 'Y': case 'y': + overwrite = 1; + break; + + default: + fprintf (stderr, "\nillegal option: %c !\n", **argv); + return 1; + } + else if (!infilename) { + infilename = malloc (strlen (*argv) + 10); + strcpy (infilename, *argv); + } + else if (!outfilename) { + outfilename = malloc (strlen (*argv) + 10); + strcpy (outfilename, *argv); + } + else { + fprintf (stderr, "\nextra unknown argument: %s !\n", *argv); + return 1; + } + } + + if (verbosity >= 0) + fprintf (stderr, "%s", sign_on); + + if (!outfilename || asked_help) { + printf ("%s", usage); + return 0; + } + + if (!strcmp (infilename, outfilename)) { + fprintf (stderr, "can't overwrite input file (specify different/new output file name)\n"); + return -1; + } + + if (!overwrite && (outfile = fopen (outfilename, "r"))) { + fclose (outfile); + fprintf (stderr, "output file \"%s\" exists (use -y to overwrite)\n", outfilename); + return -1; + } + + return adpcm_converter (infilename, outfilename, flags, blocksize_pow2, lookahead); +} + +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 { + uint16_t FormatTag, NumChannels; + uint32_t SampleRate, BytesPerSecond; + uint16_t BlockAlign, BitsPerSample; + uint16_t cbSize; + union { + uint16_t ValidBitsPerSample; + uint16_t SamplesPerBlock; + uint16_t Reserved; + } Samples; + int32_t ChannelMask; + uint16_t SubFormat; + char GUID [14]; +} WaveHeader; + +#define WaveHeaderFormat "SSLLSSSSLS" + +typedef struct { + char ckID [4]; + uint32_t ckSize; + uint32_t TotalSamples; +} FactHeader; + +#define FactHeaderFormat "4LL" + +#define WAVE_FORMAT_PCM 0x1 +#define WAVE_FORMAT_IMA_ADPCM 0x11 +#define WAVE_FORMAT_EXTENSIBLE 0xfffe + +static int write_pcm_wav_header (FILE *outfile, int num_channels, uint32_t num_samples, uint32_t sample_rate); +static int write_adpcm_wav_header (FILE *outfile, int num_channels, uint32_t num_samples, uint32_t sample_rate, int samples_per_block); +static int adpcm_decode_data (FILE *infile, FILE *outfile, int num_channels, uint32_t num_samples, int block_size); +static int adpcm_encode_data (FILE *infile, FILE *outfile, int num_channels, uint32_t num_samples, int samples_per_block, int lookahead, int noise_shaping); +static void little_endian_to_native (void *data, char *format); +static void native_to_little_endian (void *data, char *format); + +static int adpcm_converter (char *infilename, char *outfilename, int flags, int blocksize_pow2, int lookahead) +{ + int format = 0, res = 0, bits_per_sample, num_channels; + uint32_t fact_samples = 0, num_samples = 0, sample_rate; + FILE *infile, *outfile; + RiffChunkHeader riff_chunk_header; + ChunkHeader chunk_header; + WaveHeader WaveHeader; + + if (!(infile = fopen (infilename, "rb"))) { + fprintf (stderr, "can't open file \"%s\" for reading!\n", infilename); + return -1; + } + + // read initial RIFF form header + + if (!fread (&riff_chunk_header, sizeof (RiffChunkHeader), 1, infile) || + strncmp (riff_chunk_header.ckID, "RIFF", 4) || + strncmp (riff_chunk_header.formType, "WAVE", 4)) { + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + + // loop through all elements of the RIFF wav header (until the data chuck) + + while (1) { + + if (!fread (&chunk_header, sizeof (ChunkHeader), 1, infile)) { + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + + 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 = 1; + + if (chunk_header.ckSize < 16 || chunk_header.ckSize > sizeof (WaveHeader) || + !fread (&WaveHeader, chunk_header.ckSize, 1, infile)) { + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + + little_endian_to_native (&WaveHeader, WaveHeaderFormat); + + format = (WaveHeader.FormatTag == WAVE_FORMAT_EXTENSIBLE && chunk_header.ckSize == 40) ? + WaveHeader.SubFormat : WaveHeader.FormatTag; + + bits_per_sample = (chunk_header.ckSize == 40 && WaveHeader.Samples.ValidBitsPerSample) ? + WaveHeader.Samples.ValidBitsPerSample : WaveHeader.BitsPerSample; + + if (WaveHeader.NumChannels < 1 || WaveHeader.NumChannels > 2) + supported = 0; + else if (format == WAVE_FORMAT_PCM) { + if (decode_only) { + fprintf (stderr, "\"%s\" is PCM .WAV file, invalid in decode-only mode!\n", infilename); + return -1; + } + + if (bits_per_sample < 9 || bits_per_sample > 16) + supported = 0; + + if (WaveHeader.BlockAlign != WaveHeader.NumChannels * 2) + supported = 0; + } + else if (format == WAVE_FORMAT_IMA_ADPCM) { + if (encode_only) { + fprintf (stderr, "\"%s\" is ADPCM .WAV file, invalid in encode-only mode!\n", infilename); + return -1; + } + + if (bits_per_sample != 4) + supported = 0; + + if (WaveHeader.Samples.SamplesPerBlock != (WaveHeader.BlockAlign - WaveHeader.NumChannels * 4) * (WaveHeader.NumChannels ^ 3) + 1) { + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + } + else + supported = 0; + + if (!supported) { + fprintf (stderr, "\"%s\" is an unsupported .WAV format!\n", infilename); + return -1; + } + + if (verbosity > 0) { + fprintf (stderr, "format tag size = %d\n", chunk_header.ckSize); + fprintf (stderr, "FormatTag = 0x%x, NumChannels = %u, BitsPerSample = %u\n", + WaveHeader.FormatTag, WaveHeader.NumChannels, WaveHeader.BitsPerSample); + fprintf (stderr, "BlockAlign = %u, SampleRate = %lu, BytesPerSecond = %lu\n", + WaveHeader.BlockAlign, (unsigned long) WaveHeader.SampleRate, (unsigned long) WaveHeader.BytesPerSecond); + + if (chunk_header.ckSize > 16) { + if (format == WAVE_FORMAT_PCM) + fprintf (stderr, "cbSize = %d, ValidBitsPerSample = %d\n", WaveHeader.cbSize, + WaveHeader.Samples.ValidBitsPerSample); + else if (format == WAVE_FORMAT_IMA_ADPCM) + fprintf (stderr, "cbSize = %d, SamplesPerBlock = %d\n", WaveHeader.cbSize, + WaveHeader.Samples.SamplesPerBlock); + } + + if (chunk_header.ckSize > 20) + fprintf (stderr, "ChannelMask = %x, SubFormat = %d\n", + WaveHeader.ChannelMask, WaveHeader.SubFormat); + } + } + else if (!strncmp (chunk_header.ckID, "fact", 4)) { + + if (chunk_header.ckSize < 4 || !fread (&fact_samples, sizeof (fact_samples), 1, infile)) { + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + + little_endian_to_native (&fact_samples, "L"); + + if (chunk_header.ckSize > 4) { + int bytes_to_skip = chunk_header.ckSize - 4; + char dummy; + + while (bytes_to_skip--) + if (!fread (&dummy, 1, 1, infile)) { + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + } + } + else if (!strncmp (chunk_header.ckID, "data", 4)) { + + // on the data chunk, get size and exit parsing loop + + if (!WaveHeader.NumChannels) { // make sure we saw a "fmt" chunk... + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + + if (!chunk_header.ckSize) { + fprintf (stderr, "this .WAV file has no audio samples, probably is corrupt!\n"); + return -1; + } + + if (format == WAVE_FORMAT_PCM) { + if (chunk_header.ckSize % WaveHeader.BlockAlign) { + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + + num_samples = chunk_header.ckSize / WaveHeader.BlockAlign; + } + else { + uint32_t complete_blocks = chunk_header.ckSize / WaveHeader.BlockAlign; + int leftover_bytes = chunk_header.ckSize % WaveHeader.BlockAlign; + int samples_last_block; + + num_samples = complete_blocks * WaveHeader.Samples.SamplesPerBlock; + + if (leftover_bytes) { + if (leftover_bytes % (WaveHeader.NumChannels * 4)) { + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + if (verbosity > 0) fprintf (stderr, "data chunk has %d bytes left over for final ADPCM block\n", leftover_bytes); + samples_last_block = (leftover_bytes - (WaveHeader.NumChannels * 4)) * (WaveHeader.NumChannels ^ 3) + 1; + num_samples += samples_last_block; + } + else + samples_last_block = WaveHeader.Samples.SamplesPerBlock; + + if (fact_samples) { + if (fact_samples < num_samples && fact_samples > num_samples - samples_last_block) { + if (verbosity > 0) fprintf (stderr, "total samples reduced %lu by FACT chunk\n", (unsigned long) (num_samples - fact_samples)); + num_samples = fact_samples; + } + else if (WaveHeader.NumChannels == 2 && (fact_samples >>= 1) < num_samples && fact_samples > num_samples - samples_last_block) { + if (verbosity > 0) fprintf (stderr, "num samples reduced %lu by [incorrect] FACT chunk\n", (unsigned long) (num_samples - fact_samples)); + num_samples = fact_samples; + } + } + } + + if (!num_samples) { + fprintf (stderr, "this .WAV file has no audio samples, probably is corrupt!\n"); + return -1; + } + + if (verbosity > 0) + fprintf (stderr, "num samples = %lu\n", (unsigned long) num_samples); + + num_channels = WaveHeader.NumChannels; + sample_rate = WaveHeader.SampleRate; + break; + } + else { // just ignore unknown chunks + int bytes_to_eat = (chunk_header.ckSize + 1) & ~1L; + char dummy; + + if (verbosity > 0) + fprintf (stderr, "extra unknown chunk \"%c%c%c%c\" of %d bytes\n", + chunk_header.ckID [0], chunk_header.ckID [1], chunk_header.ckID [2], + chunk_header.ckID [3], chunk_header.ckSize); + + while (bytes_to_eat--) + if (!fread (&dummy, 1, 1, infile)) { + fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename); + return -1; + } + } + } + + if (!(outfile = fopen (outfilename, "wb"))) { + fprintf (stderr, "can't open file \"%s\" for writing!\n", outfilename); + return -1; + } + + if (format == WAVE_FORMAT_PCM) { + int block_size, samples_per_block; + + if (blocksize_pow2) + block_size = 1 << blocksize_pow2; + else + block_size = 256 * num_channels * (sample_rate < 11000 ? 1 : sample_rate / 11000); + + samples_per_block = (block_size - num_channels * 4) * (num_channels ^ 3) + 1; + + if (verbosity > 0) + fprintf (stderr, "each %d byte ADPCM block will contain %d samples * %d channels\n", + block_size, samples_per_block, num_channels); + + if (!(flags & ADPCM_FLAG_RAW_OUTPUT) && !write_adpcm_wav_header (outfile, num_channels, num_samples, sample_rate, samples_per_block)) { + fprintf (stderr, "can't write header to file \"%s\" !\n", outfilename); + return -1; + } + + if (verbosity >= 0) fprintf (stderr, "encoding PCM file \"%s\" to%sADPCM file \"%s\"...\n", + infilename, (flags & ADPCM_FLAG_RAW_OUTPUT) ? " raw " : " ", outfilename); + + res = adpcm_encode_data (infile, outfile, num_channels, num_samples, samples_per_block, lookahead, + (flags & ADPCM_FLAG_NOISE_SHAPING) ? (sample_rate > 64000 ? NOISE_SHAPING_STATIC : NOISE_SHAPING_DYNAMIC) : NOISE_SHAPING_OFF); + } + else if (format == WAVE_FORMAT_IMA_ADPCM) { + if (!(flags & ADPCM_FLAG_RAW_OUTPUT) && !write_pcm_wav_header (outfile, num_channels, num_samples, sample_rate)) { + fprintf (stderr, "can't write header to file \"%s\" !\n", outfilename); + return -1; + } + + if (verbosity >= 0) fprintf (stderr, "decoding ADPCM file \"%s\" to%sPCM file \"%s\"...\n", + infilename, (flags & ADPCM_FLAG_RAW_OUTPUT) ? " raw " : " ", outfilename); + + res = adpcm_decode_data (infile, outfile, num_channels, num_samples, WaveHeader.BlockAlign); + } + + fclose (outfile); + fclose (infile); + return res; +} + +static int write_pcm_wav_header (FILE *outfile, int num_channels, uint32_t num_samples, uint32_t sample_rate) +{ + RiffChunkHeader riffhdr; + ChunkHeader datahdr, fmthdr; + WaveHeader wavhdr; + + int wavhdrsize = 16; + int bytes_per_sample = 2; + uint32_t total_data_bytes = num_samples * bytes_per_sample * num_channels; + + memset (&wavhdr, 0, sizeof (wavhdr)); + + wavhdr.FormatTag = WAVE_FORMAT_PCM; + wavhdr.NumChannels = num_channels; + wavhdr.SampleRate = sample_rate; + wavhdr.BytesPerSecond = sample_rate * num_channels * bytes_per_sample; + wavhdr.BlockAlign = bytes_per_sample * num_channels; + wavhdr.BitsPerSample = 16; + + strncpy (riffhdr.ckID, "RIFF", sizeof (riffhdr.ckID)); + strncpy (riffhdr.formType, "WAVE", sizeof (riffhdr.formType)); + riffhdr.ckSize = sizeof (riffhdr) + wavhdrsize + sizeof (datahdr) + total_data_bytes; + strncpy (fmthdr.ckID, "fmt ", sizeof (fmthdr.ckID)); + fmthdr.ckSize = wavhdrsize; + + strncpy (datahdr.ckID, "data", sizeof (datahdr.ckID)); + datahdr.ckSize = total_data_bytes; + + // write the RIFF chunks up to just before the data starts + + native_to_little_endian (&riffhdr, ChunkHeaderFormat); + native_to_little_endian (&fmthdr, ChunkHeaderFormat); + native_to_little_endian (&wavhdr, WaveHeaderFormat); + native_to_little_endian (&datahdr, ChunkHeaderFormat); + + return fwrite (&riffhdr, sizeof (riffhdr), 1, outfile) && + fwrite (&fmthdr, sizeof (fmthdr), 1, outfile) && + fwrite (&wavhdr, wavhdrsize, 1, outfile) && + fwrite (&datahdr, sizeof (datahdr), 1, outfile); +} + +static int write_adpcm_wav_header (FILE *outfile, int num_channels, uint32_t num_samples, uint32_t sample_rate, int samples_per_block) +{ + RiffChunkHeader riffhdr; + ChunkHeader datahdr, fmthdr; + WaveHeader wavhdr; + FactHeader facthdr; + + int wavhdrsize = 20; + int block_size = (samples_per_block - 1) / (num_channels ^ 3) + (num_channels * 4); + uint32_t num_blocks = num_samples / samples_per_block; + int leftover_samples = num_samples % samples_per_block; + uint32_t total_data_bytes = num_blocks * block_size; + + if (leftover_samples) { + int last_block_samples = ((leftover_samples + 6) & ~7) + 1; + int last_block_size = (last_block_samples - 1) / (num_channels ^ 3) + (num_channels * 4); + total_data_bytes += last_block_size; + } + + memset (&wavhdr, 0, sizeof (wavhdr)); + + wavhdr.FormatTag = WAVE_FORMAT_IMA_ADPCM; + wavhdr.NumChannels = num_channels; + wavhdr.SampleRate = sample_rate; + wavhdr.BytesPerSecond = sample_rate * block_size / samples_per_block; + wavhdr.BlockAlign = block_size; + wavhdr.BitsPerSample = 4; + wavhdr.cbSize = 2; + wavhdr.Samples.SamplesPerBlock = samples_per_block; + + strncpy (riffhdr.ckID, "RIFF", sizeof (riffhdr.ckID)); + strncpy (riffhdr.formType, "WAVE", sizeof (riffhdr.formType)); + riffhdr.ckSize = sizeof (riffhdr) + wavhdrsize + sizeof (facthdr) + sizeof (datahdr) + total_data_bytes; + strncpy (fmthdr.ckID, "fmt ", sizeof (fmthdr.ckID)); + fmthdr.ckSize = wavhdrsize; + strncpy (facthdr.ckID, "fact", sizeof (facthdr.ckID)); + facthdr.TotalSamples = num_samples; + facthdr.ckSize = 4; + + strncpy (datahdr.ckID, "data", sizeof (datahdr.ckID)); + datahdr.ckSize = total_data_bytes; + + // write the RIFF chunks up to just before the data starts + + native_to_little_endian (&riffhdr, ChunkHeaderFormat); + native_to_little_endian (&fmthdr, ChunkHeaderFormat); + native_to_little_endian (&wavhdr, WaveHeaderFormat); + native_to_little_endian (&facthdr, FactHeaderFormat); + native_to_little_endian (&datahdr, ChunkHeaderFormat); + + return fwrite (&riffhdr, sizeof (riffhdr), 1, outfile) && + fwrite (&fmthdr, sizeof (fmthdr), 1, outfile) && + fwrite (&wavhdr, wavhdrsize, 1, outfile) && + fwrite (&facthdr, sizeof (facthdr), 1, outfile) && + fwrite (&datahdr, sizeof (datahdr), 1, outfile); +} + +static int adpcm_decode_data (FILE *infile, FILE *outfile, int num_channels, uint32_t num_samples, int block_size) +{ + int samples_per_block = (block_size - num_channels * 4) * (num_channels ^ 3) + 1, percent; + void *pcm_block = malloc (samples_per_block * num_channels * 2); + void *adpcm_block = malloc (block_size); + uint32_t progress_divider = 0; + + if (!pcm_block || !adpcm_block) { + fprintf (stderr, "could not allocate memory for buffers!\n"); + return -1; + } + + if (verbosity >= 0 && num_samples > 1000) { + progress_divider = (num_samples + 50) / 100; + fprintf (stderr, "\rprogress: %d%% ", percent = 0); + fflush (stderr); + } + + while (num_samples) { + int this_block_adpcm_samples = samples_per_block; + int this_block_pcm_samples = samples_per_block; + + if (this_block_adpcm_samples > num_samples) { + this_block_adpcm_samples = ((num_samples + 6) & ~7) + 1; + block_size = (this_block_adpcm_samples - 1) / (num_channels ^ 3) + (num_channels * 4); + this_block_pcm_samples = num_samples; + } + + if (!fread (adpcm_block, block_size, 1, infile)) { + fprintf (stderr, "could not read all audio data from input file!\n"); + return -1; + } + + if (adpcm_decode_block (pcm_block, adpcm_block, block_size, num_channels) != this_block_adpcm_samples) { + fprintf (stderr, "adpcm_decode_block() did not return expected value!\n"); + return -1; + } + + if (IS_BIG_ENDIAN) { + int scount = this_block_pcm_samples * num_channels; + unsigned char *cp = (unsigned char *) pcm_block; + + while (scount--) { + int16_t temp = * (int16_t *) cp; + *cp++ = (unsigned char) temp; + *cp++ = (unsigned char) (temp >> 8); + } + } + + if (!fwrite (pcm_block, this_block_pcm_samples * num_channels * 2, 1, outfile)) { + fprintf (stderr, "could not write all audio data to output file!\n"); + return -1; + } + + num_samples -= this_block_pcm_samples; + + if (progress_divider) { + int new_percent = 100 - num_samples / progress_divider; + + if (new_percent != percent) { + fprintf (stderr, "\rprogress: %d%% ", percent = new_percent); + fflush (stderr); + } + } + } + + if (verbosity >= 0) + fprintf (stderr, "\r...completed successfully\n"); + + free (adpcm_block); + free (pcm_block); + return 0; +} + +static int adpcm_encode_data (FILE *infile, FILE *outfile, int num_channels, uint32_t num_samples, int samples_per_block, int lookahead, int noise_shaping) +{ + int block_size = (samples_per_block - 1) / (num_channels ^ 3) + (num_channels * 4), percent; + int16_t *pcm_block = malloc (samples_per_block * num_channels * 2); + void *adpcm_block = malloc (block_size); + uint32_t progress_divider = 0; + void *adpcm_cnxt = NULL; + + if (!pcm_block || !adpcm_block) { + fprintf (stderr, "could not allocate memory for buffers!\n"); + return -1; + } + + if (verbosity >= 0 && num_samples > 1000) { + progress_divider = (num_samples + 50) / 100; + fprintf (stderr, "\rprogress: %d%% ", percent = 0); + fflush (stderr); + } + + while (num_samples) { + int this_block_adpcm_samples = samples_per_block; + int this_block_pcm_samples = samples_per_block; + size_t num_bytes; + + if (this_block_pcm_samples > num_samples) { + this_block_adpcm_samples = ((num_samples + 6) & ~7) + 1; + block_size = (this_block_adpcm_samples - 1) / (num_channels ^ 3) + (num_channels * 4); + this_block_pcm_samples = num_samples; + } + + if (!fread (pcm_block, this_block_pcm_samples * num_channels * 2, 1, infile)) { + fprintf (stderr, "\rcould not read all audio data from input file!\n"); + return -1; + } + + if (IS_BIG_ENDIAN) { + int scount = this_block_pcm_samples * num_channels; + unsigned char *cp = (unsigned char *) pcm_block; + + while (scount--) { + int16_t temp = cp [0] + (cp [1] << 8); + * (int16_t *) cp = temp; + cp += 2; + } + } + + // if this is the last block and it's not full, duplicate the last sample(s) so we don't + // create problems for the lookahead + + if (this_block_adpcm_samples > this_block_pcm_samples) { + int16_t *dst = pcm_block + this_block_pcm_samples * num_channels, *src = dst - num_channels; + int dups = (this_block_adpcm_samples - this_block_pcm_samples) * num_channels; + + while (dups--) + *dst++ = *src++; + } + + // if this is the first block, compute a decaying average (in reverse) so that we can let the + // encoder know what kind of initial deltas to expect (helps initializing index) + + if (!adpcm_cnxt) { + int32_t average_deltas [2]; + int i; + + average_deltas [0] = average_deltas [1] = 0; + + for (i = this_block_adpcm_samples * num_channels; i -= num_channels;) { + average_deltas [0] -= average_deltas [0] >> 3; + average_deltas [0] += abs ((int32_t) pcm_block [i] - pcm_block [i - num_channels]); + + if (num_channels == 2) { + average_deltas [1] -= average_deltas [1] >> 3; + average_deltas [1] += abs ((int32_t) pcm_block [i-1] - pcm_block [i+1]); + } + } + + average_deltas [0] >>= 3; + average_deltas [1] >>= 3; + + adpcm_cnxt = adpcm_create_context (num_channels, lookahead, noise_shaping, average_deltas); + } + + adpcm_encode_block (adpcm_cnxt, adpcm_block, &num_bytes, pcm_block, this_block_adpcm_samples); + + if (num_bytes != block_size) { + fprintf (stderr, "\radpcm_encode_block() did not return expected value (expected %d, got %d)!\n", block_size, (int) num_bytes); + return -1; + } + + if (!fwrite (adpcm_block, block_size, 1, outfile)) { + fprintf (stderr, "\rcould not write all audio data to output file!\n"); + return -1; + } + + num_samples -= this_block_pcm_samples; + + if (progress_divider) { + int new_percent = 100 - num_samples / progress_divider; + + if (new_percent != percent) { + fprintf (stderr, "\rprogress: %d%% ", percent = new_percent); + fflush (stderr); + } + } + } + + if (verbosity >= 0) + fprintf (stderr, "\r...completed successfully\n"); + + if (adpcm_cnxt) + adpcm_free_context (adpcm_cnxt); + + free (adpcm_block); + free (pcm_block); + return 0; +} + +static void little_endian_to_native (void *data, char *format) +{ + unsigned char *cp = (unsigned char *) data; + int32_t temp; + + while (*format) { + switch (*format) { + case 'L': + temp = cp [0] + ((int32_t) cp [1] << 8) + ((int32_t) cp [2] << 16) + ((int32_t) cp [3] << 24); + * (int32_t *) cp = temp; + cp += 4; + break; + + case 'S': + temp = cp [0] + (cp [1] << 8); + * (short *) cp = (short) temp; + cp += 2; + break; + + default: + if (isdigit ((unsigned char) *format)) + cp += *format - '0'; + + break; + } + + format++; + } +} + +static void native_to_little_endian (void *data, char *format) +{ + unsigned char *cp = (unsigned char *) data; + int32_t temp; + + while (*format) { + switch (*format) { + case 'L': + temp = * (int32_t *) cp; + *cp++ = (unsigned char) temp; + *cp++ = (unsigned char) (temp >> 8); + *cp++ = (unsigned char) (temp >> 16); + *cp++ = (unsigned char) (temp >> 24); + break; + + case 'S': + temp = * (short *) cp; + *cp++ = (unsigned char) temp; + *cp++ = (unsigned char) (temp >> 8); + break; + + default: + if (isdigit ((unsigned char) *format)) + cp += *format - '0'; + + break; + } + + format++; + } +} +#endif + diff --git a/extern/adpcm-xq-s/library.properties b/extern/adpcm-xq-s/library.properties new file mode 100644 index 000000000..28e975df0 --- /dev/null +++ b/extern/adpcm-xq-s/library.properties @@ -0,0 +1,9 @@ +name=adpcm-xq +version=0.3.0 +author=David Bryant +maintainer=David Bryant +sentence=ADPCM Codec +paragraph=Encoding and decoding of IMA ADPCM +category=Signal Input/Output +url=https://github.com/drby/adpcm-xq +architectures=* diff --git a/extern/adpcm-xq-s/license.txt b/extern/adpcm-xq-s/license.txt new file mode 100644 index 000000000..65d4a2e4b --- /dev/null +++ b/extern/adpcm-xq-s/license.txt @@ -0,0 +1,25 @@ + Copyright (c) David Bryant + 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/src/engine/sample.cpp b/src/engine/sample.cpp index 719344157..9fa7d3b2f 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -35,7 +35,7 @@ extern "C" { #include "../../extern/adpcm/ymb_codec.h" #include "../../extern/adpcm/ymz_codec.h" } -#include "../../extern/adpcm-xq/adpcm-lib.h" +#include "../../extern/adpcm-xq-s/adpcm-lib.h" #include "brrUtils.h" DivSampleHistory::~DivSampleHistory() {