desubmodulize adpcm-xq
This commit is contained in:
parent
849f0efc33
commit
0bf0d57738
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
1
extern/adpcm-xq
vendored
1
extern/adpcm-xq
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit 6220fed7655e86a29702b45dbc641a028ed5a4bf
|
1
extern/adpcm-xq-s/.gitignore
vendored
Normal file
1
extern/adpcm-xq-s/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
build/
|
18
extern/adpcm-xq-s/CMakeLists.txt
vendored
Normal file
18
extern/adpcm-xq-s/CMakeLists.txt
vendored
Normal file
|
@ -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} )
|
||||
|
63
extern/adpcm-xq-s/README
vendored
Normal file
63
extern/adpcm-xq-s/README
vendored
Normal file
|
@ -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.
|
||||
|
415
extern/adpcm-xq-s/adpcm-lib.c
vendored
Normal file
415
extern/adpcm-xq-s/adpcm-lib.c
vendored
Normal file
|
@ -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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
44
extern/adpcm-xq-s/adpcm-lib.h
vendored
Normal file
44
extern/adpcm-xq-s/adpcm-lib.h
vendored
Normal file
|
@ -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 <stdint.h>
|
||||
#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_ */
|
819
extern/adpcm-xq-s/adpcm-xq.c
vendored
Normal file
819
extern/adpcm-xq-s/adpcm-xq.c
vendored
Normal file
|
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#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
|
||||
|
9
extern/adpcm-xq-s/library.properties
vendored
Normal file
9
extern/adpcm-xq-s/library.properties
vendored
Normal file
|
@ -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=*
|
25
extern/adpcm-xq-s/license.txt
vendored
Normal file
25
extern/adpcm-xq-s/license.txt
vendored
Normal file
|
@ -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.
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue