1
0
mirror of https://github.com/ScrelliCopter/VGM-Tools synced 2025-02-21 04:09:25 +11:00

neotools: make adpcmb encoder & decoder streamable (encoder untested)

This commit is contained in:
2023-12-29 03:25:14 +11:00
parent 9f6c0664ff
commit 70bbf3d0d1
3 changed files with 112 additions and 89 deletions

View File

@@ -9,7 +9,7 @@ set_property(TARGET adpcm PROPERTY C_STANDARD 99)
target_compile_options(adpcm PRIVATE ${WARNINGS}) target_compile_options(adpcm PRIVATE ${WARNINGS})
target_link_libraries(adpcm Common::wave $<$<C_COMPILER_ID:Clang,GNU>:m>) target_link_libraries(adpcm Common::wave $<$<C_COMPILER_ID:Clang,GNU>:m>)
add_executable(adpcmb adpcmb.c) add_executable(adpcmb adpcmb.h adpcmb.c)
target_compile_options(adpcmb PRIVATE ${WARNINGS}) target_compile_options(adpcmb PRIVATE ${WARNINGS})
target_link_libraries(adpcmb Common::wave) target_link_libraries(adpcmb Common::wave)

View File

@@ -1,9 +1,5 @@
/*; YM2610 ADPCM-B Codec /*; YM2610 ADPCM-B Codec
; ;
;**** PCM to ADPCM-B & ADPCM-B to PCM converters for NEO-GEO System ****
;ADPCM-B - 1 channel 1.8-55.5 KHz, 16 MB Sample ROM size, 256 B min size of sample, 16 MB max, compatable with YM2608
;
;http://www.raregame.ru/file/15/YM2610.pdf YM2610 DATASHEET
; ;
; Fred/FRONT ; Fred/FRONT
; ;
@@ -13,6 +9,7 @@
; Valley Bell ; Valley Bell
;----------------------------------------------------------------------------------------------------------------------------*/ ;----------------------------------------------------------------------------------------------------------------------------*/
#include "adpcmb.h"
#include "wave.h" #include "wave.h"
#include "util.h" #include "util.h"
#include <stdio.h> #include <stdio.h>
@@ -20,108 +17,94 @@
#include <string.h> #include <string.h>
static const long stepsizeTable[16] = /* PCM to ADPCM-B & ADPCM-B to PCM converters for NEO-GEO System ****
ADPCM-B - 1 channel 1.8-55.5 KHz, 16 MB Sample ROM size, 256 B min size of sample, 16 MB max, compatable with YM2608
http://www.raregame.ru/file/15/YM2610.pdf YM2610 DATASHEET
*/
static const long stepSizeTable[16] =
{ {
57, 57, 57, 57, 77, 102, 128, 153, 57, 57, 57, 57, 77, 102, 128, 153,
57, 57, 57, 57, 77, 102, 128, 153 57, 57, 57, 57, 77, 102, 128, 153
}; };
static int YM2610_ADPCM_Encode(int16_t *src, uint8_t *dest, int len) void adpcmBEncoderInit(AdpcmBEncoderState* encoder)
{ {
int lpc, flag; encoder->xn = 0;
long i, dn, xn, stepSize; encoder->stepSize = 127;
uint8_t adpcm; encoder->flag = false;
uint8_t adpcmPack; encoder->adpcmPack = 0;
}
xn = 0; void adpcmBEncode(AdpcmBEncoderState* encoder, const int16_t* restrict in, uint8_t* restrict out, int len)
stepSize = 127; {
flag = 0; for (int lpc = 0; lpc < len; ++lpc)
for (lpc = 0; lpc < len; lpc ++)
{ {
dn = *src - xn; long dn = (*in++) - encoder->xn;
src ++;
i = (labs(dn) << 16) / (stepSize << 14); long i = (labs(dn) << 16) / (encoder->stepSize << 14);
if (i > 7) i = MIN(i, 7);
i = 7; uint8_t adpcm = i;
adpcm = (uint8_t)i;
i = (adpcm * 2 + 1) * stepSize / 8; i = (adpcm * 2 + 1) * encoder->stepSize / 8;
if (dn < 0) if (dn < 0)
{ {
adpcm |= 0x8; adpcm |= 0x8;
xn -= i; encoder->xn -= i;
} }
else else
{ {
xn += i; encoder->xn += i;
} }
stepSize = (stepsizeTable[adpcm] * stepSize) / 64; encoder->stepSize = (stepSizeTable[adpcm] * encoder->stepSize) / 64;
encoder->stepSize = CLAMP(encoder->stepSize, 127, 24576);
if (stepSize < 127) if (!encoder->flag)
stepSize = 127;
else if (stepSize > 24576)
stepSize = 24576;
if (flag == 0)
{ {
adpcmPack = (adpcm << 4); encoder->adpcmPack = adpcm << 4;
flag = 1; encoder->flag = true;
} }
else else
{ {
adpcmPack |= adpcm; (*out++) = encoder->adpcmPack |= adpcm;
*dest = adpcmPack; encoder->flag = false;
dest ++;
flag = 0;
} }
} }
return 0;
} }
static int YM2610_ADPCM_Decode(uint8_t *src, int16_t *dest, int len) void adpcmBDecoderInit(AdpcmBDecoderState* decoder)
{ {
int lpc, shift, step; decoder->xn = 0;
long i, xn, stepSize; decoder->stepSize = 127;
uint8_t adpcm; decoder->shift = 4;
decoder->step = 0;
}
xn = 0; void adpcmBDecode(AdpcmBDecoderState* decoder, const uint8_t* restrict in, int16_t* restrict out, int len)
stepSize = 127; {
shift = 4; for (int lpc = 0; lpc < len; ++lpc)
step = 0;
for (lpc = 0; lpc < len; lpc ++)
{ {
adpcm = (*src >> shift) & 0xf; uint8_t adpcm = (*in >> decoder->shift) & 0xF;
i = ((adpcm & 7) * 2 + 1) * stepSize / 8; long i = ((adpcm & 7) * 2 + 1) * decoder->stepSize / 8;
if (adpcm & 8) if (adpcm & 8)
xn -= i; decoder->xn -= i;
else else
xn += i; decoder->xn += i;
decoder->xn = CLAMP(decoder->xn, -32768, 32767);
xn = CLAMP(xn, -32768, 32767); decoder->stepSize = decoder->stepSize * stepSizeTable[adpcm] / 64;
decoder->stepSize = CLAMP(decoder->stepSize, 127, 24576);
stepSize = stepSize * stepsizeTable[adpcm] / 64; (*out++) = (int16_t)decoder->xn;
if (stepSize < 127) in += decoder->step;
stepSize = 127; decoder->step = decoder->step ^ 1;
else if (stepSize > 24576) decoder->shift = decoder->shift ^ 4;
stepSize = 24576;
*dest = (int16_t)xn;
dest ++;
src += step;
step = step ^ 1;
shift = shift ^ 4;
} }
return 0;
} }
static FORCE_INLINE uint32_t DeltaTReg2SampleRate(uint16_t DeltaN, uint32_t Clock) static FORCE_INLINE uint32_t DeltaTReg2SampleRate(uint16_t DeltaN, uint32_t Clock)
@@ -129,6 +112,8 @@ static FORCE_INLINE uint32_t DeltaTReg2SampleRate(uint16_t DeltaN, uint32_t Cloc
return (uint32_t)(DeltaN * (Clock / 72.0) / 65536.0 + 0.5); return (uint32_t)(DeltaN * (Clock / 72.0) / 65536.0 + 0.5);
} }
#define BUFFER_SIZE 2048
static int decode(const char* inPath, const char* outPath, uint32_t sampleRate) static int decode(const char* inPath, const char* outPath, uint32_t sampleRate)
{ {
FILE* inFile = fopen(inPath, "rb"); FILE* inFile = fopen(inPath, "rb");
@@ -142,25 +127,20 @@ static int decode(const char* inPath, const char* outPath, uint32_t sampleRate)
long adpcmSize = ftell(inFile); long adpcmSize = ftell(inFile);
fseek(inFile, 0, SEEK_SET); fseek(inFile, 0, SEEK_SET);
uint8_t* adpcmData = malloc((size_t)adpcmSize); uint8_t* adpcmData = malloc(BUFFER_SIZE);
fread(adpcmData, 0x01, adpcmSize, inFile); int16_t* wavData = malloc(BUFFER_SIZE * 2 * sizeof(int16_t));
fclose(inFile);
long wavSize = adpcmSize * 2; // 4-bit ADPCM -> 2 values per byte
int16_t* wavData = malloc((size_t)wavSize * sizeof(int16_t));
printf("Decoding ...");
YM2610_ADPCM_Decode(adpcmData, wavData, wavSize);
printf(" OK\n");
// Write decoded sample as WAVE
FILE* outFile = fopen(outPath, "wb"); FILE* outFile = fopen(outPath, "wb");
if (outFile == NULL) if (outFile == NULL)
{ {
printf("Error opening output file!\n"); printf("Error opening output file!\n");
free(adpcmData);
free(wavData); free(wavData);
free(adpcmData);
fclose(inFile);
return 3; return 3;
} }
// Write wave header
waveWrite(&(const WaveSpec) waveWrite(&(const WaveSpec)
{ {
.format = WAVE_FMT_PCM, .format = WAVE_FMT_PCM,
@@ -168,13 +148,28 @@ static int decode(const char* inPath, const char* outPath, uint32_t sampleRate)
.rate = sampleRate ? sampleRate : 22050, .rate = sampleRate ? sampleRate : 22050,
.bytedepth = 2 .bytedepth = 2
}, },
wavData, (size_t)wavSize * sizeof(int16_t), &waveStreamDefaultCb, outFile); NULL, (size_t)adpcmSize * 2 * sizeof(int16_t), &waveStreamDefaultCb, outFile);
printf("Decoding ...");
AdpcmBDecoderState decoder;
adpcmBDecoderInit(&decoder);
size_t read;
do
{
if ((read = fread(adpcmData, 1, BUFFER_SIZE, inFile)) > 0)
{
adpcmBDecode(&decoder, adpcmData, wavData, read * 2);
fwrite(wavData, sizeof(int16_t), read * 2, outFile);
}
}
while (read == BUFFER_SIZE);
printf(" OK\n");
fclose(outFile); fclose(outFile);
printf("File written.\n");
free(adpcmData);
free(wavData); free(wavData);
free(adpcmData);
fclose(inFile);
printf("File written.\n");
return 0; return 0;
} }
@@ -280,7 +275,9 @@ static int encode(const char* inPath, const char* outPath)
unsigned int AdpcmSize = WaveSize / 2; unsigned int AdpcmSize = WaveSize / 2;
uint8_t* AdpcmData = malloc(AdpcmSize); uint8_t* AdpcmData = malloc(AdpcmSize);
printf("Encoding ..."); printf("Encoding ...");
YM2610_ADPCM_Encode(WaveData, AdpcmData, WaveSize); AdpcmBEncoderState encoder;
adpcmBEncoderInit(&encoder);
adpcmBEncode(&encoder, WaveData, AdpcmData, WaveSize);
printf(" OK\n"); printf(" OK\n");
hFile = fopen(outPath, "wb"); hFile = fopen(outPath, "wb");
@@ -336,7 +333,7 @@ int main(int argc, char* argv[])
int ArgBase = 2; int ArgBase = 2;
if (argv[2][0] == '-' && argv[2][2] == ':') if (argv[2][0] == '-' && argv[2][2] == ':')
{ {
switch(argv[2][1]) switch (argv[2][1])
{ {
case 's': case 's':
OutSmplRate = strtol(argv[2] + 3, NULL, 0); OutSmplRate = strtol(argv[2] + 3, NULL, 0);
@@ -353,15 +350,14 @@ int main(int argc, char* argv[])
OutSmplRate = DeltaTReg2SampleRate(DTRegs, TempLng); OutSmplRate = DeltaTReg2SampleRate(DTRegs, TempLng);
break; break;
} }
ArgBase++; if (argc < ++ArgBase + 2)
if (argc < ArgBase + 2)
{ {
printf("Not enought arguments!\n"); printf("Not enought arguments!\n");
return 1; return 1;
} }
} }
switch(argv[1][1]) switch (argv[1][1])
{ {
case 'd': case 'd':
ErrVal = decode(argv[ArgBase + 0], argv[ArgBase + 1], OutSmplRate); ErrVal = decode(argv[ArgBase + 0], argv[ArgBase + 1], OutSmplRate);

27
neotools/adpcmb.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef ADPCMB_H
#define ADPCMB_H
#include <stdint.h>
#include <stdbool.h>
typedef struct AdpcmBEncoderState
{
bool flag;
long xn, stepSize;
uint8_t adpcmPack;
} AdpcmBEncoderState;
void adpcmBEncoderInit(AdpcmBEncoderState* encoder);
void adpcmBEncode(AdpcmBEncoderState* encoder, const int16_t* restrict in, uint8_t* restrict out, int len);
typedef struct AdpcmBDecoderState
{
int shift, step;
long xn, stepSize;
} AdpcmBDecoderState;
void adpcmBDecoderInit(AdpcmBDecoderState* decoder);
void adpcmBDecode(AdpcmBDecoderState* decoder, const uint8_t* restrict in, int16_t* restrict out, int len);
#endif//ADPCMB_H