diff --git a/tools/adpcm.c b/tools/adpcm.c new file mode 100644 index 0000000..2c3be24 --- /dev/null +++ b/tools/adpcm.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include + +static inline int clamp(int x, int low, int high) +{ + if (x > high) + return high; + if (x < low) + return low; + return x; +} + +static inline int ymz_step(uint8_t step, int16_t * history, int16_t * step_size) +{ + static const int step_table[8] = { + 230, 230, 230, 230, 307, 409, 512, 614 + }; + + int sign = step & 8; + int delta = step & 7; + int diff = ((1 + (delta << 1)) * *step_size) >> 3; + int newval = *history; + int nstep = (step_table[delta] * *step_size) >> 8; + diff = clamp(diff, 0, 32767); + if (sign > 0) + newval -= diff; + else + newval += diff; + *step_size = clamp(nstep, 127, 24576); + *history = newval = clamp(newval, -32768, 32767); + return newval; +} + +void adpcm_encode(int16_t * buffer, uint8_t * outbuffer, int len) +{ + int16_t step_size = 127; + int16_t history = 0; + uint8_t buf_sample = 0, nibble = 0; + unsigned int adpcm_sample; + + for (int i = 0; i < len; i++) { + //int step = ((*buffer++) & -8) - history; + int step = *buffer++ - history; + adpcm_sample = (abs(step) << 16) / (step_size << 14); + adpcm_sample = clamp(adpcm_sample, 0, 7); + if(step < 0) + adpcm_sample |= 8; + if(!nibble) + *outbuffer++ = buf_sample | (adpcm_sample << 4); + else + buf_sample = (adpcm_sample & 15); + nibble ^= 1; + ymz_step(adpcm_sample, &history, &step_size); + } +} + +int read_file(const char * filename, uint8_t ** buf, uint32_t * size_out) +{ + FILE * file = fopen(filename, "rb"); + if (file == NULL) { + fprintf(stderr, "fopen(\"%s\", \"rb\"): %s\n", filename, strerror(errno)); + return -1; + } + + int ret; + ret = fseek(file, 0L, SEEK_END); + if (ret < 0) { + fprintf(stderr, "fseek(SEEK_END)"); + return -1; + } + + long offset = ftell(file); + if (offset < 0) { + fprintf(stderr, "ftell"); + return -1; + } + size_t size = offset; + + ret = fseek(file, 0L, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "fseek(SEEK_SET)"); + return -1; + } + + fprintf(stderr, "read_file: %s size %ld\n", filename, size); + *buf = (uint8_t *)malloc(size); + size_t fread_size = fread(*buf, 1, size, file); + if (fread_size != size) { + fprintf(stderr, "fread `%s` short read: %" PRIu64 " ; expected: %" PRIu64 "\n", filename, fread_size, size); + return -1; + } + + ret = fclose(file); + if (ret < 0) { + fprintf(stderr, "fclose"); + return -1; + } + + *size_out = size; + + return 0; +} + +int write_file(const char * filename, const uint8_t * buf, uint32_t size) +{ + FILE * file = fopen(filename, "wb"); + if (file == NULL) { + fprintf(stderr, "fopen(\"%s\", \"wb\"): %s\n", filename, strerror(errno)); + return -1; + } + + size_t write_size = fwrite(buf, 1, size, file); + if (write_size != size) { + fprintf(stderr, "fwrite: %d %ld %s\n", size, write_size, strerror(errno)); + return -1; + } + + int ret = fclose(file); + if (ret < 0) { + fprintf(stderr, "fclose"); + return -1; + } + + return 0; +} + +int main(int argc, char * argv[]) +{ + assert(argc == 3); + + uint8_t * buf; + int size; + int ret = read_file(argv[1], &buf, &size); + assert(ret == 0); + + uint8_t * out = malloc(size / 4); + + adpcm_encode((uint16_t *)buf, out, size / 2); + + ret = write_file(argv[2], out, size / 4); + assert(ret == 0); +}