mirror of
https://github.com/ScrelliCopter/VGM-Tools
synced 2025-02-21 04:09:25 +11:00
366 lines
8.5 KiB
C
366 lines
8.5 KiB
C
/*; YM2610 ADPCM-B Codec
|
|
;
|
|
;
|
|
; 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
|
|
;
|
|
; Valley Bell
|
|
;----------------------------------------------------------------------------------------------------------------------------*/
|
|
|
|
#include "adpcmb.h"
|
|
#include "wave.h"
|
|
#include "util.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
/* 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);
|
|
}
|
|
|
|
#define BUFFER_SIZE 2048
|
|
|
|
static int decode(const char* inPath, const char* outPath, uint32_t sampleRate)
|
|
{
|
|
FILE* inFile = fopen(inPath, "rb");
|
|
if (inFile == NULL)
|
|
{
|
|
printf("Error opening input file!\n");
|
|
return 2;
|
|
}
|
|
|
|
fseek(inFile, 0, SEEK_END);
|
|
long adpcmSize = ftell(inFile);
|
|
fseek(inFile, 0, SEEK_SET);
|
|
|
|
uint8_t* adpcmData = malloc(BUFFER_SIZE);
|
|
int16_t* wavData = malloc(BUFFER_SIZE * 2 * sizeof(int16_t));
|
|
|
|
FILE* outFile = fopen(outPath, "wb");
|
|
if (outFile == NULL)
|
|
{
|
|
printf("Error opening output file!\n");
|
|
free(wavData);
|
|
free(adpcmData);
|
|
fclose(inFile);
|
|
return 3;
|
|
}
|
|
|
|
// Write wave header
|
|
waveWrite(&(const WaveSpec)
|
|
{
|
|
.format = WAVE_FMT_PCM,
|
|
.channels = 1,
|
|
.rate = sampleRate ? sampleRate : 22050,
|
|
.bytedepth = 2
|
|
},
|
|
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);
|
|
fwrite(wavData, sizeof(int16_t), read * 2, outFile);
|
|
}
|
|
}
|
|
while (read == BUFFER_SIZE);
|
|
printf(" OK\n");
|
|
fclose(outFile);
|
|
|
|
free(wavData);
|
|
free(adpcmData);
|
|
fclose(inFile);
|
|
printf("File written.\n");
|
|
return 0;
|
|
}
|
|
|
|
typedef struct waveformat_tag {
|
|
uint16_t wFormatTag; /* format type */
|
|
uint16_t nChannels; /* number of channels (i.e. mono, stereo...) */
|
|
uint32_t nSamplesPerSec; /* sample rate */
|
|
uint32_t nAvgBytesPerSec; /* for buffer estimation */
|
|
uint16_t nBlockAlign; /* block size of data */
|
|
uint16_t wBitsPerSample;
|
|
} WAVEFORMAT;
|
|
|
|
typedef struct
|
|
{
|
|
char RIFFfcc[4];
|
|
uint32_t RIFFLen;
|
|
char WAVEfcc[4];
|
|
char fmt_fcc[4];
|
|
uint32_t fmt_Len;
|
|
WAVEFORMAT fmt_Data;
|
|
char datafcc[4];
|
|
uint32_t dataLen;
|
|
} WAVE_FILE;
|
|
|
|
static int encode(const char* inPath, const char* outPath)
|
|
{
|
|
FILE* hFile = fopen(inPath, "rb");
|
|
if (hFile == NULL)
|
|
{
|
|
printf("Error opening input file!\n");
|
|
return 2;
|
|
}
|
|
|
|
WAVE_FILE WaveFile;
|
|
fread(&WaveFile.RIFFfcc, 0x0C, 0x01, hFile);
|
|
if (memcmp(WaveFile.RIFFfcc, "RIFF", 4) != 0 || memcmp(WaveFile.WAVEfcc, "WAVE", 4) != 0)
|
|
{
|
|
fclose(hFile);
|
|
printf("This is no wave file!\n");
|
|
return 4;
|
|
}
|
|
|
|
unsigned int TempLng = fread(&WaveFile.fmt_fcc, 0x04, 0x01, hFile);
|
|
fread(&WaveFile.fmt_Len, 0x04, 0x01, hFile);
|
|
while (memcmp(WaveFile.fmt_fcc, "fmt ", 4) != 0)
|
|
{
|
|
if (!TempLng) // TempLng == 0 -> EOF reached
|
|
{
|
|
fclose(hFile);
|
|
printf("Error in wave file: Can't find format-tag!\n");
|
|
return 4;
|
|
}
|
|
fseek(hFile, WaveFile.fmt_Len, SEEK_CUR);
|
|
|
|
TempLng = fread(&WaveFile.fmt_fcc, 0x04, 0x01, hFile);
|
|
fread(&WaveFile.fmt_Len, 0x04, 0x01, hFile);
|
|
};
|
|
TempLng = ftell(hFile) + WaveFile.fmt_Len;
|
|
fread(&WaveFile.fmt_Data, sizeof(WAVEFORMAT), 0x01, hFile);
|
|
fseek(hFile, TempLng, SEEK_SET);
|
|
|
|
WAVEFORMAT* TempFmt = &WaveFile.fmt_Data;
|
|
if (TempFmt->wFormatTag != WAVE_FMT_PCM)
|
|
{
|
|
fclose(hFile);
|
|
printf("Error in wave file: Compressed wave file are not supported!\n");
|
|
return 4;
|
|
}
|
|
if (TempFmt->nChannels != 1)
|
|
{
|
|
fclose(hFile);
|
|
printf("Error in wave file: Unsupported number of channels (%hu)!\n", TempFmt->nChannels);
|
|
return 4;
|
|
}
|
|
if (TempFmt->wBitsPerSample != 16)
|
|
{
|
|
fclose(hFile);
|
|
printf("Error in wave file: Only 16-bit waves are supported! (File uses %hu bit)\n", TempFmt->wBitsPerSample);
|
|
return 4;
|
|
}
|
|
|
|
TempLng = fread(&WaveFile.datafcc, 0x04, 0x01, hFile);
|
|
fread(&WaveFile.dataLen, 0x04, 0x01, hFile);
|
|
while (memcmp(WaveFile.datafcc, "data", 4) != 0)
|
|
{
|
|
if (!TempLng) // TempLng == 0 -> EOF reached
|
|
{
|
|
fclose(hFile);
|
|
printf("Error in wave file: Can't find data-tag!\n");
|
|
return 4;
|
|
}
|
|
fseek(hFile, WaveFile.dataLen, SEEK_CUR);
|
|
|
|
TempLng = fread(&WaveFile.datafcc, 0x04, 0x01, hFile);
|
|
fread(&WaveFile.dataLen, 0x04, 0x01, hFile);
|
|
};
|
|
unsigned int WaveSize = WaveFile.dataLen / 2;
|
|
int16_t* WaveData = malloc(WaveSize * 2);
|
|
fread(WaveData, 0x02, WaveSize, hFile);
|
|
|
|
fclose(hFile);
|
|
|
|
unsigned int AdpcmSize = WaveSize / 2;
|
|
uint8_t* AdpcmData = malloc(AdpcmSize);
|
|
printf("Encoding ...");
|
|
AdpcmBEncoderState encoder;
|
|
adpcmBEncoderInit(&encoder);
|
|
adpcmBEncode(&encoder, WaveData, AdpcmData, WaveSize);
|
|
printf(" OK\n");
|
|
|
|
hFile = fopen(outPath, "wb");
|
|
if (hFile == NULL)
|
|
{
|
|
printf("Error opening output file!\n");
|
|
free(AdpcmData);
|
|
free(WaveData);
|
|
return 3;
|
|
}
|
|
|
|
fwrite(AdpcmData, 0x01, AdpcmSize, hFile);
|
|
fclose(hFile);
|
|
printf("File written.\n");
|
|
|
|
free(AdpcmData);
|
|
free(WaveData);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
unsigned int TempLng;
|
|
uint16_t DTRegs;
|
|
char* TempPnt;
|
|
|
|
printf("NeoGeo ADPCM-B En-/Decoder\n--------------------------\n");
|
|
if (argc < 4)
|
|
{
|
|
printf("Usage: ADPCM_Encode.exe -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");
|
|
printf("-option - Options for Sample Rate of decoded Wave:\n");
|
|
printf(" -s:rate - Sample Rate in Hz (default: 22050)\n");
|
|
printf(" -r:regs[,clock] - DeltaT Register value (use 0x for hex) and chip clock\n");
|
|
printf(" (default chip clock: 4 MHz)\n");
|
|
printf("\n");
|
|
printf("Wave In-/Output is 16-bit, Mono\n");
|
|
return 1;
|
|
}
|
|
|
|
if (strcmp(argv[1], "-d") && strcmp(argv[1], "-e"))
|
|
{
|
|
printf("Wrong option! Use -d or -e!\n");
|
|
return 1;
|
|
}
|
|
|
|
int ErrVal = 0;
|
|
uint32_t OutSmplRate = 0;
|
|
|
|
int ArgBase = 2;
|
|
if (argv[2][0] == '-' && argv[2][2] == ':')
|
|
{
|
|
switch (argv[2][1])
|
|
{
|
|
case 's':
|
|
OutSmplRate = strtol(argv[2] + 3, NULL, 0);
|
|
break;
|
|
case 'r':
|
|
DTRegs = (uint16_t)strtoul(argv[2] + 3, &TempPnt, 0);
|
|
TempLng = 0;
|
|
if (*TempPnt == ',')
|
|
{
|
|
TempLng = strtoul(TempPnt + 1, NULL, 0);
|
|
}
|
|
if (!TempLng)
|
|
TempLng = 4000000;
|
|
OutSmplRate = DeltaTReg2SampleRate(DTRegs, TempLng);
|
|
break;
|
|
}
|
|
if (argc < ++ArgBase + 2)
|
|
{
|
|
printf("Not enought arguments!\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
switch (argv[1][1])
|
|
{
|
|
case 'd':
|
|
ErrVal = decode(argv[ArgBase + 0], argv[ArgBase + 1], OutSmplRate);
|
|
break;
|
|
case 'e':
|
|
ErrVal = encode(argv[ArgBase + 0], argv[ArgBase + 1]);
|
|
break;
|
|
}
|
|
|
|
return ErrVal;
|
|
}
|