From: falcovorbis <fal...@us...> - 2024-09-23 01:02:39
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "A pseudo Operating System for the Dreamcast.". The branch, master has been updated via 497bfca329e78bc8d8259e62a3e88415b8cc8390 (commit) from 7adf0ae6ae75675f6d1c05ee23342696f73c9bab (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 497bfca329e78bc8d8259e62a3e88415b8cc8390 Author: Andy Barajas <and...@gm...> Date: Sun Sep 22 18:02:18 2024 -0700 Update wav2adpcm (#755) Replaced encoding/decoding GPL code with public domain code. Added interleave(-i) option thanks to SKMP. ----------------------------------------------------------------------- Summary of changes: utils/wav2adpcm/wav2adpcm.c | 714 +++++++++++++++++++++++++++++--------------- 1 file changed, 480 insertions(+), 234 deletions(-) diff --git a/utils/wav2adpcm/wav2adpcm.c b/utils/wav2adpcm/wav2adpcm.c index 13adf7e5..d91e99b1 100644 --- a/utils/wav2adpcm/wav2adpcm.c +++ b/utils/wav2adpcm/wav2adpcm.c @@ -1,188 +1,232 @@ /* aica adpcm <-> wave converter; - (c) 2002 BERO <be...@ge...> - under GPL or notify me + Copyright (C) 2002 BERO <be...@ge...> + Copyright (C) 2024 Andress Barajas + Copyright (C) 2024 Stefanos Kornilios Mitsis Poiitidis - aica adpcm seems same as YMZ280B adpcm - adpcm->pcm algorithm can found MAME/src/sound/ymz280b.c by Aaron Giles + AICA adpcm seems same as YMZ280B adpcm. The only difference + between YMZ280B and AICA adpcm is that the nibbles are swapped. - this code is for little endian machine + The encode and decode algorithms for AICA adpcm - 2019 by superctr. - Modified by Megan Potter to read/write ADPCM WAV files, and to - handle stereo (though the stereo is very likely KOS specific - since we make no effort to interleave it). Please see README.GPL - in the KOS docs dir for more info on the GPL license. + This code is for little endian machine. + + Originally modified by Megan Potter to read/write ADPCM WAV files, + and to handle stereo (non-interleaved). + + Modified by Andress Barajas to replace the GPL MAME encoding/decoding + code with public domain code written by superctr. This version also + handles interleaved stereo thanks to SKMP and can output headerless + audio data. + + Public domain code source: + https://github.com/superctr/adpcm/blob/master/ymz_codec.c */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> +#include <math.h> + +/* WAV Header */ +typedef struct wavhdr { + uint8_t hdr1[4]; + uint32_t totalsize; + + uint8_t hdr2[8]; + uint32_t hdrsize; + uint16_t format; + uint16_t channels; + uint32_t freq; + uint32_t byte_per_sec; + uint16_t block_align; + uint16_t bits_per_sample; +} wavhdr_t; -static int diff_lookup[16] = { - 1, 3, 5, 7, 9, 11, 13, 15, - -1, -3, -5, -7, -9, -11, -13, -15, -}; - -static int index_scale[16] = { - 0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266, - 0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266 /* same value for speedup */ -}; - -static inline int limit(int val, int min, int max) { - if(val < min) return min; - else if(val > max) return max; - else return val; +/* Header chunk */ +typedef struct wavhdr_chunk { + char hdr3[4]; + uint32_t datasize; +} wavhdr_chunk_t; + +/* Holds flags */ +static int interleaved = 0; +static int no_header = 0; + +/* Output Formats */ +#define WAVE_FMT_PCM 0x01 /* PCM */ +#define WAVE_FMT_YAMAHA_ADPCM_ITU_G723 0x14 /* ITU G.723 Yamaha ADPCM (KallistiOS) */ +#define WAVE_FMT_YAMAHA_ADPCM 0x20 /* Yamaha ADPCM (interleaved) */ + +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +static inline int16_t 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; + + /* Only found in the official AICA encoder + but it's possible all chips (including ADPCM-B) does this. */ + 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 pcm2adpcm(unsigned char *dst, const short *src, size_t length) { - int signal, step; - signal = 0; - step = 0x7f; - - /* length /= 4; */ - length = (length + 3) / 4; - - do { - int data, val, diff; - - /* hign nibble */ - diff = *src++ - signal; - diff = (diff * 8) / step; - - val = abs(diff) / 2; - - if(val > 7) val = 7; - - if(diff < 0) val += 8; - - signal += (step * diff_lookup[val]) / 8; - signal = limit(signal, -32768, 32767); - - step = (step * index_scale[val]) >> 8; - step = limit(step, 0x7f, 0x6000); - - data = val; - - /* low nibble */ - diff = *src++ - signal; - diff = (diff * 8) / step; - - val = (abs(diff)) / 2; - - if(val > 7) val = 7; - - if(diff < 0) val += 8; - - signal += (step * diff_lookup[val]) / 8; - signal = limit(signal, -32768, 32767); - - step = (step * index_scale[val]) >> 8; - step = limit(step, 0x7f, 0x6000); - - data |= val << 4; - - *dst++ = data; - +void adpcm2pcm(int16_t *outbuffer, uint8_t *buffer, size_t bytes) { + long i; + int16_t step_size = 127; + int16_t history = 0; + uint8_t nibble = 4; + size_t num_samples = bytes * 2; /* Each ADPCM byte contains two 4-bit samples */ + + for(i = 0; i < num_samples; i++) { + int8_t step = (*(int8_t *)buffer) << nibble; + step >>= 4; + if(!nibble) + buffer++; + nibble ^= 4; + history = history * 254 / 256; // High pass + *outbuffer++ = ymz_step(step, &history, &step_size); } - while(--length); } -void adpcm2pcm(short *dst, const unsigned char *src, size_t length) { - int signal, step; - signal = 0; - step = 0x7f; - - do { - int data, val; - - data = *src++; +void pcm2adpcm(uint8_t *outbuffer, int16_t *buffer, size_t bytes) { + long i; + int16_t step_size = 127; + int16_t history = 0; + uint8_t buf_sample = 0, nibble = 0; + uint32_t adpcm_sample; + size_t num_samples = bytes / 2; /* Divide by 2 to get the number of 16-bit samples */ + + for(i = 0;i < num_samples;i++) { + /* We remove a few bits_per_sample of accuracy to reduce some noise. */ + int step = ((*buffer++) & -8) - 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); + } +} - /* low nibble */ - val = data & 15; +void deinterleave(void *buffer, size_t bytes) { + uint16_t *buf; + uint16_t *left, *right; + int i; - signal += (step * diff_lookup[val]) / 8; - signal = limit(signal, -32768, 32767); + buf = (uint16_t *)buffer; + left = malloc(bytes); + if(!left) { + fprintf(stderr, "deinterleave: Memory allocation failed.\n"); + exit(EXIT_FAILURE); + } + right = left + bytes / 4; - step = (step * index_scale[val & 7]) >> 8; - step = limit(step, 0x7f, 0x6000); + for(i = 0; i < bytes / 4; i++) { + left[i] = buf[i * 2 + 0]; + right[i] = buf[i * 2 + 1]; + } - *dst++ = signal; + memcpy(buf, left, bytes / 2); + memcpy(buf + bytes / 4, right, bytes / 2); - /* high nibble */ - val = (data >> 4) & 15; + free(left); +} - signal += (step * diff_lookup[val]) / 8; - signal = limit(signal, -32768, 32767); +void deinterleave_adpcm(void *buffer, size_t bytes) { + uint8_t *buf; + uint8_t *left, *right; + int i; - step = (step * index_scale[val & 7]) >> 8; - step = limit(step, 0x7f, 0x6000); + buf = (uint8_t *)buffer; + left = malloc(bytes); + if(!left) { + fprintf(stderr, "deinterleave_adpcm: Memory allocation failed.\n"); + exit(EXIT_FAILURE); + } + right = left + bytes / 2; + + for(i = 0; i < bytes; i++) { + if(i % 2 == 0) { /* Set high nibble */ + left[i / 2] = (buf[i] & 0xF0); + right[i / 2] = (buf[i] & 0x0F) << 4; + } else { /* Set low nibble to complete the byte */ + left[i / 2] |= ((buf[i] >> 4) & 0x0F); + right[i / 2] |= (buf[i] & 0x0F); + } + } - *dst++ = signal; + memcpy(buf, left, bytes); - } - while(--length); + free(left); } -void deinterleave(void *buffer, size_t size) { - short * buf; - short * buf1, * buf2; +void interleave(void *buffer, size_t bytes) { + uint16_t *buf; + uint16_t *left, *right; int i; - buf = (short *)buffer; - buf1 = malloc(size / 2); - buf2 = malloc(size / 2); + buf = malloc(bytes); + if(!buf) { + fprintf(stderr, "interleave: Memory allocation failed.\n"); + exit(EXIT_FAILURE); + } + left = (uint16_t *)buffer; + right = left + bytes / 4; - for(i = 0; i < size / 4; i++) { - buf1[i] = buf[i * 2 + 0]; - buf2[i] = buf[i * 2 + 1]; + for(i = 0; i < bytes / 4; i++) { + buf[i * 2 + 0] = left[i]; + buf[i * 2 + 1] = right[i]; } - memcpy(buf, buf1, size / 2); - memcpy(buf + size / 4, buf2, size / 2); + memcpy(buffer, buf, bytes); - free(buf1); - free(buf2); + free(buf); } -void interleave(void *buffer, size_t size) { - short * buf; - short * buf1, * buf2; +void interleave_adpcm(void *buffer, size_t bytes) { + uint8_t *buf; + uint8_t *left, *right; int i; - buf = malloc(size); - buf1 = (short *)buffer; - buf2 = buf1 + size / 4; + buf = malloc(bytes); + if(!buf) { + fprintf(stderr, "interleave_adpcm: Memory allocation failed.\n"); + exit(EXIT_FAILURE); + } + left = (uint8_t *)buffer; + right = left + bytes / 2; - for(i = 0; i < size / 4; i++) { - buf[i * 2 + 0] = buf1[i]; - buf[i * 2 + 1] = buf2[i]; + for(i = 0; i < bytes; i++) { + buf[i] = (right[i/2] >> (i%2*4)) & 0xF; + buf[i] |= ((left[i/2] >> (i%2*4)) & 0XF) << 4; } - memcpy(buffer, buf, size); + memcpy(buffer, buf, bytes); free(buf); } -typedef struct wavhdr_t { - char hdr1[4]; - int32_t totalsize; - - char hdr2[8]; - int32_t hdrsize; - short format; - short channels; - int32_t freq; - int32_t byte_per_sec; - short blocksize; - short bits; - - char hdr3[4]; - int32_t datasize; -} wavhdr_t; - -int validate_wav_header(wavhdr_t *wavhdr, int format, int bits, FILE *in) { +int validate_wav_header(wavhdr_t *wavhdr, wavhdr_chunk_t *wavhdr3, int format_mask, int bits_per_sample, FILE *in) { int result = 0; if(memcmp(wavhdr->hdr1, "RIFF", 4)) { @@ -195,68 +239,112 @@ int validate_wav_header(wavhdr_t *wavhdr, int format, int bits, FILE *in) { result = -1; } - if(wavhdr->hdrsize != 0x10) { - fprintf(stderr, "Invalid header size.\n"); + if(wavhdr->hdrsize < 0x10) { + fprintf(stderr, "Invalid header size, %d bytes\n", wavhdr->hdrsize); result = -1; + } else if(wavhdr->hdrsize > 0x10) { + /*fprintf(stderr, "Unique header size, %d bytes\n", wavhdr->hdrsize); */ + fseek(in, wavhdr->hdrsize - 0x10, SEEK_CUR); } - if(wavhdr->format != format) { - fprintf(stderr, "Unsupported format.\n"); + if(!(wavhdr->format & format_mask)) { + fprintf(stderr, "Unsupported format: %#x\n", wavhdr->format); result = -1; } if(wavhdr->channels != 1 && wavhdr->channels != 2) { - fprintf(stderr, "Unsupported number of channels.\n"); + fprintf(stderr, "Unsupported number of channels: %d\n", wavhdr->channels); result = -1; } - if(wavhdr->bits != bits) { - fprintf(stderr, "Unsupported bit depth.\n"); + if(wavhdr->bits_per_sample != bits_per_sample) { + fprintf(stderr, "Unsupported bit depth: %d\n", wavhdr->bits_per_sample); result = -1; } - if(memcmp(wavhdr->hdr3, "data", 4)) - { - /* File contains meta data that we want to skip. - Keep reading until we find the "data" header. */ - fseek(in, wavhdr->datasize, SEEK_CUR); + for(;;) { + /* Read the next chunk header */ + if(fread(wavhdr3->hdr3, 1, 4, in) != 4) { + fprintf(stderr, "Failed to read next chunk header!\n"); + result = -1; + break; + } - do - { - /* Read the next chunk header */ - if(fread(wavhdr->hdr3, 1, 4, in) != 4) { - fprintf(stderr, "Failed to read next chunk header!\n"); - result = -1; - break; - } + /* Read the chunk size */ + if(fread(&wavhdr3->datasize, 1, 4, in) != 4) { + fprintf(stderr, "Failed to read chunk size!\n"); + result = -1; + break; + } - /* Read the chunk size */ - if(fread(&wavhdr->datasize, 1, 4, in) != 4) { - fprintf(stderr, "Failed to read chunk size!\n"); - result = -1; - break; - } + /* Skip the chunk if it's not the "data" chunk. */ + if(memcmp(wavhdr3->hdr3, "data", 4)) + fseek(in, wavhdr3->datasize, SEEK_CUR); + else + break; + } + + return result; +} + +/* Do a straight copy of the input to output file */ +int straight_copy(FILE *in, const char *outfile) { + FILE *out = NULL; + size_t filesize; + char *buffer = NULL; + int result = 0; + + fseek(in, 0, SEEK_END); + filesize = ftell(in); + rewind(in); - /* Skip the chunk if it's not the "data" chunk. */ - if(memcmp(wavhdr->hdr3, "data", 4)) - fseek(in, wavhdr->datasize, SEEK_CUR); - } while(memcmp(wavhdr->hdr3, "data", 4)); + buffer = malloc(filesize); + if(!buffer) { + fprintf(stderr, "Memory allocation failed.\n"); + result = -1; + goto cleanup; + } + + if(fread(buffer, filesize, 1, in) != 1) { + fprintf(stderr, "Cannot read file.\n"); + free(buffer); + result = -1; + goto cleanup; } + out = fopen(outfile, "wb"); + if(!out) { + fprintf(stderr, "Cannot open %s for writing.\n", outfile); + result = -1; + goto cleanup; + } + + if(fwrite(buffer, filesize, 1, out) != 1) { + fprintf(stderr, "Cannot write to output file.\n"); + result = -1; + goto cleanup; + } + +cleanup: + if(in) fclose(in); + if(out) fclose(out); + if(buffer) free(buffer); + return result; } int wav2adpcm(const char *infile, const char *outfile) { wavhdr_t wavhdr; - FILE *in, *out; + wavhdr_chunk_t wavhdr_chunk; + FILE *in, *out = NULL; ...<truncated>... hooks/post-receive -- A pseudo Operating System for the Dreamcast. |