diff --git a/neotools/CMakeLists.txt b/neotools/CMakeLists.txt index 6131faa..8b1a817 100644 --- a/neotools/CMakeLists.txt +++ b/neotools/CMakeLists.txt @@ -9,11 +9,15 @@ set_property(TARGET adpcm PROPERTY C_STANDARD 99) target_compile_options(adpcm PRIVATE ${WARNINGS}) target_link_libraries(adpcm Common::wave $<$:m>) -add_executable(adpcmb adpcmb.h adpcmb.c) +add_executable(adpcmb adpcmb.h libadpcmb.c adpcmb.c) +set_property(TARGET adpcmb PROPERTY C_STANDARD 99) target_compile_options(adpcmb PRIVATE ${WARNINGS}) target_link_libraries(adpcmb Common::wave) -add_executable(neoadpcmextract adpcm.h libadpcma.c neoadpcmextract.c) +add_executable(neoadpcmextract + libadpcma.c adpcm.h + libadpcmb.c adpcmb.h + neoadpcmextract.c) set_property(TARGET neoadpcmextract PROPERTY C_STANDARD 99) target_compile_definitions(neoadpcmextract PRIVATE $<$:USE_ZLIB=1>) target_compile_options(neoadpcmextract PRIVATE ${WARNINGS}) diff --git a/neotools/adpcmb.c b/neotools/adpcmb.c index 1121128..71e7acd 100644 --- a/neotools/adpcmb.c +++ b/neotools/adpcmb.c @@ -1,10 +1,8 @@ -/*; YM2610 ADPCM-B Codec -; -; +/* adpcmb.c - CLI for encoding & decoding YM2610 ADPCM-B files ; Fred/FRONT ; -;Usage 1: ADPCM_Encode.exe -d [-r:reg,clock] Input.bin Output.wav -;Usage 2: ADPCM_Encode.exe -e Input.wav Output.bin +;Usage 1: ADPCM_Encode -d [-r:reg,clock] Input.bin Output.wav +;Usage 2: ADPCM_Encode -e Input.wav Output.bin ; ; Valley Bell ;----------------------------------------------------------------------------------------------------------------------------*/ @@ -17,90 +15,6 @@ #include -/* 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 -}; - -void adpcmBEncoderInit(AdpcmBEncoderState* encoder) -{ - encoder->xn = 0; - encoder->stepSize = 127; - encoder->flag = false; - encoder->adpcmPack = 0; -} - -void adpcmBEncode(AdpcmBEncoderState* encoder, const int16_t* restrict in, uint8_t* restrict out, int len) -{ - for (int lpc = 0; lpc < len; ++lpc) - { - long dn = (*in++) - encoder->xn; - - long i = (labs(dn) << 16) / (encoder->stepSize << 14); - i = MIN(i, 7); - uint8_t adpcm = i; - - i = (adpcm * 2 + 1) * encoder->stepSize / 8; - - if (dn < 0) - { - adpcm |= 0x8; - encoder->xn -= i; - } - else - { - encoder->xn += i; - } - - encoder->stepSize = (stepSizeTable[adpcm] * encoder->stepSize) / 64; - encoder->stepSize = CLAMP(encoder->stepSize, 127, 24576); - - if (!encoder->flag) - { - encoder->adpcmPack = adpcm << 4; - encoder->flag = true; - } - else - { - (*out++) = encoder->adpcmPack |= adpcm; - encoder->flag = false; - } - } -} - -void adpcmBDecoderInit(AdpcmBDecoderState* decoder) -{ - decoder->xn = 0; - decoder->stepSize = 127; -} - -void adpcmBDecode(AdpcmBDecoderState* decoder, const uint8_t* restrict in, int16_t* restrict out, int len) -{ - for (long lpc = 0; lpc < len * 2; ++lpc) - { - uint8_t adpcm = (!(lpc & 0x1) ? (*in) >> 4 : (*in++)) & 0xF; - - long i = ((adpcm & 7) * 2 + 1) * decoder->stepSize / 8; - if (adpcm & 8) - decoder->xn -= i; - else - decoder->xn += i; - decoder->xn = CLAMP(decoder->xn, -32768, 32767); - - decoder->stepSize = decoder->stepSize * stepSizeTable[adpcm] / 64; - decoder->stepSize = CLAMP(decoder->stepSize, 127, 24576); - - (*out++) = (int16_t)decoder->xn; - } -} - static FORCE_INLINE uint32_t DeltaTReg2SampleRate(uint16_t DeltaN, uint32_t Clock) { return (uint32_t)(DeltaN * (Clock / 72.0) / 65536.0 + 0.5); @@ -302,7 +216,7 @@ int main(int argc, char* argv[]) printf("NeoGeo ADPCM-B En-/Decoder\n--------------------------\n"); if (argc < 4) { - printf("Usage: ADPCM_Encode.exe -method [-option] InputFile OutputFile\n"); + printf("Usage: ADPCM_Encode -method [-option] InputFile OutputFile\n"); printf("-method - En-/Decoding Method:\n"); printf(" -d for decode (bin -> wav)\n"); printf(" -e for encode (wav -> bin)\n"); diff --git a/neotools/libadpcmb.c b/neotools/libadpcmb.c new file mode 100644 index 0000000..eabe021 --- /dev/null +++ b/neotools/libadpcmb.c @@ -0,0 +1,92 @@ +/* libadpcmb.c (C) 2023 a dinosaur (zlib) + + ** 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 +*/ + +#include "adpcmb.h" +#include "util.h" +#include + + +static const long stepSizeTable[16] = +{ + 57, 57, 57, 57, 77, 102, 128, 153, + 57, 57, 57, 57, 77, 102, 128, 153 +}; + +void adpcmBEncoderInit(AdpcmBEncoderState* encoder) +{ + encoder->xn = 0; + encoder->stepSize = 127; + encoder->flag = false; + encoder->adpcmPack = 0; +} + +void adpcmBEncode(AdpcmBEncoderState* encoder, const int16_t* restrict in, uint8_t* restrict out, int len) +{ + for (int lpc = 0; lpc < len; ++lpc) + { + long dn = (*in++) - encoder->xn; + + long i = (labs(dn) << 16) / (encoder->stepSize << 14); + i = MIN(i, 7); + uint8_t adpcm = i; + + i = (adpcm * 2 + 1) * encoder->stepSize / 8; + + if (dn < 0) + { + adpcm |= 0x8; + encoder->xn -= i; + } + else + { + encoder->xn += i; + } + + encoder->stepSize = (stepSizeTable[adpcm] * encoder->stepSize) / 64; + encoder->stepSize = CLAMP(encoder->stepSize, 127, 24576); + + if (!encoder->flag) + { + encoder->adpcmPack = adpcm << 4; + encoder->flag = true; + } + else + { + (*out++) = encoder->adpcmPack |= adpcm; + encoder->flag = false; + } + } +} + +void adpcmBDecoderInit(AdpcmBDecoderState* decoder) +{ + decoder->xn = 0; + decoder->stepSize = 127; +} + +void adpcmBDecode(AdpcmBDecoderState* decoder, const uint8_t* restrict in, int16_t* restrict out, int len) +{ + for (long lpc = 0; lpc < len * 2; ++lpc) + { + uint8_t adpcm = (!(lpc & 0x1) ? (*in) >> 4 : (*in++)) & 0xF; + + long i = ((adpcm & 7) * 2 + 1) * decoder->stepSize / 8; + if (adpcm & 8) + decoder->xn -= i; + else + decoder->xn += i; + decoder->xn = CLAMP(decoder->xn, -32768, 32767); + + decoder->stepSize = decoder->stepSize * stepSizeTable[adpcm] / 64; + decoder->stepSize = CLAMP(decoder->stepSize, 127, 24576); + + (*out++) = (int16_t)decoder->xn; + } +} diff --git a/neotools/neoadpcmextract.c b/neotools/neoadpcmextract.c index 662311c..6ec7280 100644 --- a/neotools/neoadpcmextract.c +++ b/neotools/neoadpcmextract.c @@ -2,6 +2,7 @@ #include "neoadpcmextract.h" #include "adpcm.h" +#include "adpcmb.h" #include "wave.h" #include "endian.h" #include "util.h" @@ -71,7 +72,6 @@ int writeAdpcmA(int id, const Buffer* enc, Buffer* pcm) { char name[32]; snprintf(name, sizeof(name), "smpa_%02x.wav", id); - fprintf(stderr, "write \"%s\"\n", name); FILE* fout = fopen(name, "wb"); if (!fout) return 1; @@ -93,7 +93,7 @@ int writeAdpcmA(int id, const Buffer* enc, Buffer* pcm) size_t decoded = 0; do { - size_t blockSize = MIN(enc->size - decoded, DECODE_BUFFER_SIZE); + const size_t blockSize = MIN(enc->size - decoded, DECODE_BUFFER_SIZE); adpcmADecode(&decoder, &((const char*)enc->data)[decoded], (short*)pcm->data, blockSize); fwrite(pcm->data, sizeof(short), blockSize * 2, fout); decoded += DECODE_BUFFER_SIZE; @@ -101,22 +101,44 @@ int writeAdpcmA(int id, const Buffer* enc, Buffer* pcm) while (decoded < enc->size); fclose(fout); + fprintf(stderr, "Wrote \"%s\"\n", name); return 0; } -int writeAdpcmB(int id, const Buffer* enc) +int writeAdpcmB(int id, const Buffer* enc, Buffer* pcm) { char name[32]; - snprintf(name, sizeof(name), "smpb_%02x.pcm", id); - printf("./adpcmb -d \"%s\" \"$WAVDIR/%s.wav\"\n", name, name); - - // Write ADPCM sample + snprintf(name, sizeof(name), "smpb_%02x.wav", id); FILE* fout = fopen(name, "wb"); if (!fout) return 1; - fwrite(enc->data, sizeof(uint8_t), enc->size, fout); + + // Write wave header + const uint32_t decodedSize = enc->size * 2 * sizeof(short); + waveWrite(&(const WaveSpec) + { + .format = WAVE_FMT_PCM, + .channels = 1, + .rate = 22050, + .bytedepth = 2 + }, + NULL, decodedSize, &waveStreamDefaultCb, fout); + + bufferResize(pcm, DECODE_BUFFER_SIZE * 2 * sizeof(short)); + AdpcmBDecoderState decoder; + adpcmBDecoderInit(&decoder); + size_t decoded = 0; + do + { + const size_t blockSize = MIN(enc->size - decoded, DECODE_BUFFER_SIZE); + adpcmBDecode(&decoder, &((const uint8_t*)enc->data)[decoded], (int16_t*)pcm->data, blockSize); + fwrite(pcm->data, sizeof(int16_t), blockSize * 2, fout); + decoded += DECODE_BUFFER_SIZE; + } + while (decoded < enc->size); fclose(fout); + fprintf(stderr, "Wrote \"%s\"\n", name); return 0; } @@ -153,7 +175,7 @@ int main(int argc, char** argv) if (scanType == 'A') writeAdpcmA(smpaCount++, &rawbuf, &decbuf); else if (scanType == 'B') - writeAdpcmB(smpbCount++, &rawbuf); + writeAdpcmB(smpbCount++, &rawbuf, &decbuf); } free(rawbuf.data);