diff --git a/tools/abx/Makefile b/tools/abx/Makefile index cc0c30ba2..880c74181 100644 --- a/tools/abx/Makefile +++ b/tools/abx/Makefile @@ -1,6 +1,11 @@ -abx2raw: abx2raw.c abx2raw.h - gcc -W -Wall -g -O0 abx2raw.c -o abx2raw +all: abx2wav wav2abx + +abx2wav: abx2wav.c abx.h abx.c wav.h wav.c port.h + gcc -W -Wall -g -O0 abx2wav.c abx.c wav.c -o abx2wav + +wav2abx: wav2abx.c abx.h abx.c wav.h wav.c port.h + gcc -W -Wall -g -O0 wav2abx.c abx.c wav.c -o wav2abx clean: - rm abx2raw + rm abx2wav abx2wav.exe wav2abx wav2abx.exe diff --git a/tools/abx/abx.c b/tools/abx/abx.c new file mode 100644 index 000000000..6f96ec6e7 --- /dev/null +++ b/tools/abx/abx.c @@ -0,0 +1,874 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * ABX encoder/decoder + * By Serge van den Boom (svdb@stack.nl) and Alex Volkov (codepro@usa.net) + * Based on ABX decoding code from Toys for Bob. + * + * TODO: + * - so far, it ignores sample rates, so it will work ok as long as all + * the frames have the same frequency. This is probably enough for + * our purposes. + * + * - add abx_setMaxError(), abx_setMinSquelch() and abx_setBlockSize() for + * the encoder parameters, if anyone cares that is. The 3DO abx files all + * used the same params, as far as I know. + */ + +#include +#include +#include +#include +#include + +#include "abx.h" + +// This number can be increased to almost anything, as long +// as you have enough memory to store the data. It's kept +// on the low end to improve the sanity checks. +#define MAX_REASONABLE_FRAMES 100000 + +#define abx_FrameInfo_size 8 +#define abx_FrameHeader_size 8 + +static uint32_t abx_decodeFrame(abx_File *abx, const abx_FrameHeader *hdr, + int inlen, uint8_t *out); +static uint32_t abx_encodeFrame(abx_File *abx, abx_FrameHeader *hdr, + uint8_t *in, int inlen); + +// The deltas table came from TFB +static const int deltas[16 * 16] = +{ + -8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8, // Multiplier of 1 + -16,-14,-12,-10,-8,-6,-4,-2,2,4,6,8,10,12,14,16, // Multiplier of 2 + -24,-21,-18,-15,-12,-9,-6,-3,3,6,9,12,15,18,21,24, // Multiplier of 3 + -32,-28,-24,-20,-16,-12,-8,-4,4,8,12,16,20,24,28,32, // Multiplier of 4 + -40,-35,-30,-25,-20,-15,-10,-5,5,10,15,20,25,30,35,40, // Multiplier of 5 + -48,-42,-36,-30,-24,-18,-12,-6,6,12,18,24,30,36,42,48, // Multiplier of 6 + -56,-49,-42,-35,-28,-21,-14,-7,7,14,21,28,35,42,49,56, // Multiplier of 7 + -64,-56,-48,-40,-32,-24,-16,-8,8,16,24,32,40,48,56,64, // Multiplier of 8 + -72,-63,-54,-45,-36,-27,-18,-9,9,18,27,36,45,54,63,72, // Multiplier of 9 + -80,-70,-60,-50,-40,-30,-20,-10,10,20,30,40,50,60,70,80, // Multiplier of 10 + -88,-77,-66,-55,-44,-33,-22,-11,11,22,33,44,55,66,77,88, // Multiplier of 11 + -96,-84,-72,-60,-48,-36,-24,-12,12,24,36,48,60,72,84,96, // Multiplier of 12 + -104,-91,-78,-65,-52,-39,-26,-13,13,26,39,52,65,78,91,104, // Multiplier of 13 + -112,-98,-84,-70,-56,-42,-28,-14,14,28,42,56,70,84,98,112, // Multiplier of 14 + -120,-105,-90,-75,-60,-45,-30,-15,15,30,45,60,75,90,105,120,// Multiplier of 15 + -128,-112,-96,-80,-64,-48,-32,-16,16,32,48,64,80,96,112,127,// Multiplier of 16 +}; + +static bool read_8 (FILE *fp, uint8_t *v) +{ + return fread(v, sizeof(*v), 1, fp) == 1; +} + +static bool read_le_16 (FILE *fp, uint16_t *v) +{ + uint8_t buf[2]; + if (fread(buf, sizeof(buf), 1, fp) != 1) + return false; + *v = (buf[1] << 8) | buf[0]; + return true; +} + +static bool read_le_32 (FILE *fp, uint32_t *v) +{ + uint8_t buf[4]; + if (fread(buf, sizeof(buf), 1, fp) != 1) + return false; + *v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + return true; +} + +static bool write_8 (FILE *fp, uint8_t v) +{ + return fwrite(&v, sizeof(v), 1, fp) == 1; +} + +static bool write_le_16 (FILE *fp, uint16_t v) +{ + uint8_t buf[2]; + buf[0] = v; + buf[1] = v >> 8; + return fwrite(buf, sizeof(buf), 1, fp) == 1; +} + +static bool write_le_32 (FILE *fp, uint32_t v) +{ + uint8_t buf[4]; + buf[0] = v; + buf[1] = v >> 8; + buf[2] = v >> 16; + buf[3] = v >> 24; + return fwrite(buf, sizeof(buf), 1, fp) == 1; +} + +static bool abx_readFileHeader(abx_File *abx, abx_FileHeader *hdr) +{ + if (!read_le_16(abx->fp, &hdr->numFrames) || + !read_le_32(abx->fp, &hdr->totalSize) || + !read_le_16(abx->fp, &hdr->maxBufSize) || + !read_le_16(abx->fp, &hdr->freq)) + { + abx->last_error = errno; + return false; + } + return true; +} + +static bool abx_writeFileHeader(abx_File *abx, const abx_FileHeader *hdr) +{ + if (!write_le_16(abx->fp, hdr->numFrames) || + !write_le_32(abx->fp, hdr->totalSize) || + !write_le_16(abx->fp, hdr->maxBufSize) || + !write_le_16(abx->fp, hdr->freq)) + { + abx->last_error = errno; + return false; + } + return true; +} + +static bool abx_readFrameInfo(abx_File *abx, abx_FrameInfo *info) +{ + if (!read_le_32(abx->fp, &info->ofs) || + !read_le_16(abx->fp, &info->fsize) || + !read_le_16(abx->fp, &info->usize)) + { + abx->last_error = errno; + return false; + } + return true; +} + +static bool abx_writeFrameInfo(abx_File *abx, const abx_FrameInfo *info) +{ + if (!write_le_32(abx->fp, info->ofs) || + !write_le_16(abx->fp, info->fsize) || + !write_le_16(abx->fp, info->usize)) + { + abx->last_error = errno; + return false; + } + return true; +} + +static bool abx_readFrameHeader(abx_File *abx, abx_FrameHeader *hdr) +{ + if (!read_le_16(abx->fp, &hdr->usize) || + !read_le_16(abx->fp, &hdr->freq) || + !read_8(abx->fp, &hdr->blockSize) || + !read_8(abx->fp, &hdr->minSquelch) || + !read_le_16(abx->fp, &hdr->maxError)) + { + abx->last_error = errno; + return false; + } + return true; +} + +static bool abx_writeFrameHeader(abx_File *abx, const abx_FrameHeader *hdr) +{ + if (!write_le_16(abx->fp, hdr->usize) || + !write_le_16(abx->fp, hdr->freq) || + !write_8(abx->fp, hdr->blockSize) || + !write_8(abx->fp, hdr->minSquelch) || + !write_le_16(abx->fp, hdr->maxError)) + { + abx->last_error = errno; + return false; + } + return true; +} + +bool abx_open(abx_File *abx, const char *filename) +{ + abx_FileHeader fileHdr; + unsigned i; + unsigned maxCalcBuf; + + memset(abx, 0, sizeof(*abx)); + + abx->fp = fopen(filename, "rb"); + if (!abx->fp) + { + abx->last_error = errno; + return false; + } + + // read abx header + if (!abx_readFileHeader(abx, &fileHdr)) + { + abx->last_error = errno; + abx_close(abx); + return false; + } + abx->numFrames = fileHdr.numFrames; + abx->maxBufSize = fileHdr.maxBufSize; + abx->freq = fileHdr.freq; + if (abx->freq == 0) + abx->freq = ABX_DEFAULT_FREQ; + + // Some sanity checks. ABX format does not have a magic number + // or anything like that, but we can do some math. + if (abx->numFrames > MAX_REASONABLE_FRAMES) + { + abx->last_error = -1; + fprintf(stderr, "abx_open(): number of frames (%u) is not reasonable\n", + abx->numFrames); + abx_close(abx); + return false; + } + if (abx->freq != 11025 && abx->freq != 22050 && abx->freq != 44100 + && abx->freq != 48000) + { + fprintf(stderr, "abx_open() Warning: sampling frequency (%u) is suspect\n", + (unsigned)abx->freq); + } + + abx->frames = calloc(sizeof(abx->frames[0]), abx->numFrames); + if (!abx->frames) + { + abx->last_error = errno; + fprintf(stderr, "abx_open(): could not allocate frames array\n"); + abx_close(abx); + return false; + } + + maxCalcBuf = 0; + for (i = 0; i < abx->numFrames; ++i) + { + abx_FrameInfo *info = abx->frames + i; + + if (!abx_readFrameInfo(abx, info)) + { + abx_close(abx); + return false; + } + abx->totalSize += info->usize; + if (info->usize > maxCalcBuf) + maxCalcBuf = info->usize; + if (info->fsize > abx->maxEncSize) + abx->maxEncSize = info->fsize; + } + if (abx->totalSize != fileHdr.totalSize) + { + fprintf(stderr, "abx_open() Warning: " + "total size in header (%u) does not match sum of frames (%u)\n", + (unsigned)fileHdr.totalSize, (unsigned)abx->totalSize); + } + if (abx->maxBufSize < maxCalcBuf) + { + fprintf(stderr, "abx_open() Warning: " + "max buffer size in header (%u) is less than calculated max (%u)\n", + abx->maxBufSize, maxCalcBuf); + abx->maxBufSize = maxCalcBuf; + } + abx->data_ofs = ftell(abx->fp); + abx->maxFrames = abx->numFrames; + + // Our buffer stores encoded data during decoding. The maximum buffer + // size needed was computed just above. + abx->buf = malloc(abx->maxEncSize); + if (!abx->buf) + { + abx->last_error = errno; + abx_close(abx); + return false; + } + + return true; +} + +static bool abx_writeHeaders(abx_File *abx) +{ + abx_FileHeader fileHdr; + unsigned i; + + fileHdr.numFrames = abx->numFrames; + fileHdr.maxBufSize = abx->maxBufSize; + fileHdr.freq = abx->freq; + fileHdr.totalSize = abx->totalSize; + if (!abx_writeFileHeader(abx, &fileHdr)) + return false; + + for (i = 0; i < abx->numFrames; ++i) + { + abx_FrameInfo *info = abx->frames + i; + + if (!abx_writeFrameInfo(abx, info)) + return false; + } + + return true; +} + +bool abx_create(abx_File *abx, const char *filename) +{ + memset(abx, 0, sizeof(*abx)); + + abx->fp = fopen(filename, "wb"); + if (!abx->fp) + { + abx->last_error = errno; + return false; + } + abx->freq = ABX_DEFAULT_FREQ; + abx->maxError = ABX_DEFAULT_ERROR; + + if (!abx_writeHeaders(abx)) + { + abx_close(abx); + return false; + } + abx->frames_ofs = ftell(abx->fp); + + abx->maxFrames = 10; + abx->frames = calloc(sizeof(abx->frames[0]), abx->maxFrames); + if (!abx->frames) + { + abx->last_error = errno; + fprintf(stderr, "abx_create(): could not allocate frames array\n"); + abx_close(abx); + return false; + } + + fseek(abx->fp, abx->maxFrames * abx_FrameInfo_size, SEEK_CUR); + abx->data_ofs = ftell(abx->fp); + + abx->writing = true; + return true; +} + +static bool abx_flushHeaders(abx_File *abx) +{ + fseek(abx->fp, 0, SEEK_SET); + if (!abx_writeHeaders(abx)) + { + return false; + } + return true; +} + +void abx_close(abx_File *abx) +{ + if (abx->fp) + { + if (abx->writing) + abx_flushHeaders(abx); + + fclose(abx->fp); + } + if (abx->frames) + free(abx->frames); + if (abx->buf) + free(abx->buf); + + memset(abx, 0, sizeof(*abx)); +} + +bool abx_setSamplingRate(abx_File *abx, uint32_t freq) +{ + if (!abx->writing) + return false; + abx->freq = freq; + return true; +} + +uint32_t abx_getMaxBuffer(abx_File *abx) +{ + return abx->maxBufSize; +} + +bool abx_setMaxFrames(abx_File *abx, unsigned maxFrames) +{ + abx_FrameInfo *newf; + + if (!abx->writing) + return false; + + if (maxFrames < abx->numFrames) + return false; + + if (abx->numFrames > 0 && maxFrames <= abx->maxFrames) + { // We've already written some audio data to the file. + // Decreasing the allocated frame info space at this point involves + // way too much work, so we'll silently ignore this. + return true; + } + else if (abx->numFrames > 0 && maxFrames > abx->maxFrames) + { // We've already written some audio data to the file. + // Increasing the allocated frame info space at this point involves + // way too much work, so it is an error to attempt it. + abx->last_error = ENOSPC; + return false; + } + + if (abx->frames && maxFrames > abx->maxFrames) + { // grow the array + newf = realloc(abx->frames, maxFrames * sizeof(abx->frames[0])); + if (!newf) + { + abx->last_error = errno; + return false; + } + abx->frames = newf; + } + abx->maxFrames = maxFrames; + + if (abx->numFrames == 0) + { // We have not written any audio data yet. + // Adjust the data offset + fseek(abx->fp, abx->frames_ofs + abx->maxFrames * abx_FrameInfo_size, SEEK_SET); + abx->data_ofs = ftell(abx->fp); + } + + return true; +} + +uint32_t abx_readFrame(abx_File *abx, void *buf, uint32_t bufsize) +{ + abx_FrameInfo *info; + abx_FrameHeader hdr; + uint32_t decSize; + uint32_t inlen; + + if (abx->writing) + { + abx->last_error = EPERM; + return 0; + } + + if (abx->nextFrame == abx->numFrames) + { // EOF + abx->last_error = 0; + return 0; + } + + info = abx->frames + abx->nextFrame; + // Go get the next frame + if (fseek(abx->fp, info->ofs, SEEK_SET) != 0) + { + abx->last_error = errno; + return 0; + } + if (!abx_readFrameHeader(abx, &hdr)) + return 0; + if (hdr.usize != info->usize) + { + fprintf(stderr, "abx_readFrame() Warning: " + "decoded size in header (%u) does not match reported in info (%u) for frame %u\n", + (unsigned)hdr.usize, (unsigned)info->usize, abx->nextFrame); + } + if (hdr.freq != 0 && hdr.freq != abx->freq) + { + fprintf(stderr, "abx_readFrame() Warning: " + "frame frequency (%u) is different from file freq (%u) for frame %u\n", + (unsigned)hdr.freq, (unsigned)abx->freq, abx->nextFrame); + fprintf(stderr, "This is not supported. Output will be corrupted.\n"); + } + if (bufsize < hdr.usize) + { // Buffer is too small to accept the entire frame + // The caller should call abx_getMaxBuffer() to find out the size + abx->last_error = 0; + return 0; + } + + inlen = info->fsize - abx_FrameHeader_size; + if (fread(abx->buf, inlen, 1, abx->fp) != 1) + { + abx->last_error = errno; + return 0; + } + + decSize = abx_decodeFrame(abx, &hdr, inlen, buf); + if (decSize != hdr.usize) + { + fprintf(stderr, "abx_readFrame() Warning: " + "actual decoded data size (%u) does not match reported (%u) for frame %u\n", + (unsigned)decSize, (unsigned)hdr.usize, abx->nextFrame); + } + + ++abx->nextFrame; + + return decSize; +} + +uint32_t abx_writeFrame(abx_File *abx, void *buf, uint32_t bufsize) +{ + abx_FrameInfo *info; + abx_FrameHeader hdr; + uint32_t encSize; + + if (!abx->writing) + { + abx->last_error = EPERM; + return 0; + } + + if (abx->nextFrame >= abx->maxFrames) + { // No more room + abx->last_error = EFBIG; + return 0; + } + + info = abx->frames + abx->nextFrame; + + // Our buffer stores encoded data during encoding, but the encoded data + // can never be larger than the decoded one by algorithm definition. + if (bufsize > abx->maxBufSize) + { // grow the buffer + if (abx->buf) + free(abx->buf); + abx->buf = malloc(bufsize); + if (!abx->buf) + { + abx->last_error = errno; + return 0; + } + abx->maxBufSize = bufsize; + } + + if (fseek(abx->fp, abx->data_ofs, SEEK_SET) != 0) + { + abx->last_error = errno; + return 0; + } + hdr.blockSize = ABX_DEFAULT_BLOCKSIZE; + hdr.minSquelch = ABX_DEFAULT_SQUELCH; + hdr.maxError = abx->maxError; + encSize = abx_encodeFrame(abx, &hdr, buf, bufsize); + if (!abx_writeFrameHeader(abx, &hdr) || + fwrite(abx->buf, encSize, 1, abx->fp) != 1) + { + abx->last_error = errno; + return 0; + } + encSize += abx_FrameHeader_size; + + info->usize = bufsize; + info->ofs = abx->data_ofs; + info->fsize = encSize; + abx->data_ofs = ftell(abx->fp); + + if (encSize > abx->maxEncSize) + abx->maxEncSize = encSize; + abx->totalSize += bufsize; + + ++abx->nextFrame; + ++abx->numFrames; + + return encSize; +} + +static inline void clip_u8(int *val) +{ + if (*val < 0) + *val= 0; + else if (*val > 255) + *val = 255; +} + +static uint32_t abx_decodeFrame(abx_File *abx, const abx_FrameHeader *hdr, + int inlen, uint8_t *out) +{ + uint8_t *in = abx->buf; + int outlen = hdr->usize; + int prev; + + // Get initial data point + prev = *in; + ++in; + --inlen; // one byte consumed + *out = prev; + ++out; + --outlen; // one sample stored + + while (outlen > 0 && inlen > 0) + { + unsigned bytes; + unsigned sample; + + // Get next encoded byte + sample = *in; + ++in; + --inlen; + + if (sample & RESYNC) // Is it a resync byte? + { + prev = (sample & 0x7F) << 1; // Store resync byte. + *out = prev; + ++out; + --outlen; // one sample stored + } + else if (sample & SQLCH) // Is it a squelch byte? + { + bytes = sample & SQUELCHCNT; // And off the number of squelch bytes + memset(out, prev, bytes); + out += bytes; + outlen -= bytes; // bytes samples stored + } + else if (sample & DELTAMOD) // Is it delta modulate byte? + { + // base address to multiplier table + const int *base = deltas + (sample & MULTIPLIER) * 16; + unsigned sampleBits; // bits per sample + unsigned mask; + int samplesPerByte; + + // This is not optimized for efficiency, but rather deoptimized + // for readability + sampleBits = (sample & DELTAMOD) >> DELTASHIFT; + if (sampleBits == 3) // no 3-bit delta coding + sampleBits = 4; + + // Base address of deltas: middle of the table minus half the + // range of the delta + base += 8 - (1 << (sampleBits - 1)); + samplesPerByte = 8 / sampleBits; + mask = (1 << sampleBits) - 1; + + for (bytes = hdr->blockSize / samplesPerByte; bytes > 0; --bytes) + { + unsigned val; + int i; + + val = *in; + ++in; + --inlen; + + for (i = samplesPerByte; i > 0; --i) + { + val <<= sampleBits; + prev += base[(val >> 8) & mask]; + clip_u8(&prev); + *out = prev; + ++out; + } + } + + outlen -= hdr->blockSize; // one block of samples stored + } + else + { // None of the known bit combinations. Weird. + fprintf(stderr, "abx_decodeFrame() Warning: " + "unknown sample 0x%02x in frame %u\n", + (unsigned)sample, abx->nextFrame); + // We'll just suppress the sample + } + } + + if (outlen != 0 || inlen != 0) + { + fprintf(stderr, "abx_decodeFrame() Warning: " + "byte counts do not match at end of frame (%i, %i)\n", + inlen, outlen); + } + + return hdr->usize - outlen; +} + +static int lookupDelta(const int *base, int cnt, int prev, int sample) +{ + int i; + int imin = 0; + int mindiff = 65536; + + for (i = 0; i < cnt; ++i) + { + int diff; + // We want the delta that gives us a resulting sample that is + // the closest to the original *after* any clipping occurs. + // This is important in cases where both the previous sample + // and the current sample are at min or max points, since there + // is no 0 deltas in the tables. + int cur = prev + base[i]; + clip_u8(&cur); + diff = abs(cur - sample); + if (diff < mindiff) + { + mindiff = diff; + imin = i; + } + } + return imin; +} + +static uint32_t abx_encodeBlock(const abx_FrameHeader *hdr, uint8_t *in, + uint8_t *out, int *last, unsigned sampleBits, unsigned mult, + int *blockError) +{ + const int *base = deltas + mult * 16; + const int samplesPerByte = 8 / sampleBits; + const int deltaCnt = 1 << sampleBits; + unsigned bytes; + int prev = *last; + int error = 0; + + // Base address of deltas: middle of the table minus half the + // range of the delta + base += 8 - deltaCnt / 2; + + for (bytes = hdr->blockSize / samplesPerByte; bytes > 0; --bytes) + { + unsigned val = 0; + int i; + + for (i = samplesPerByte; i > 0; --i) + { + int sample = *in; + unsigned index; + + ++in; + // Computing the closest delta index directly involves a ridiculous + // amount of logic because the delta tables have no 0 deltas. It is + // simpler to just iterate over all of them. + index = lookupDelta(base, deltaCnt, prev, sample); + prev += base[index]; + clip_u8(&prev); + error += (prev - sample) * (prev - sample); + if (error > hdr->maxError) + return 0; // exceeded the maximum error, bail out + + val <<= sampleBits; + val |= index; + } + + if (out) + { + *out = val; + ++out; + } + } + + *last = prev; + if (blockError) + *blockError = error; + return hdr->blockSize / samplesPerByte; +} + +static uint32_t abx_encodeFrame(abx_File *abx, abx_FrameHeader *hdr, + uint8_t *in, int inlen) +{ + uint8_t *out = abx->buf; + int prev; + + hdr->usize = inlen; + hdr->freq = abx->freq; + + // Store initial data point + prev = *in; + ++in; + --inlen; // one sample consumed + *out = prev; + ++out; + + // Speed and efficiency is not an issue for us. The strategy here is + // simply to achieve maximum compression by brute force. We try all of + // the 48 delta coding variants and pick the one with the smallest + // total error within the allowed limit. + while (inlen > 0) + { + int cnt; + + // Try squelching first + for (cnt = 0; cnt < inlen && cnt < SQUELCHCNT; ++cnt) + { + if (in[cnt] != prev) + break; + } + if (cnt >= hdr->minSquelch) + { // Squelch sample repeats + *out = SQLCH | cnt; + ++out; + in += cnt; + inlen -= cnt; + continue; + } + + // Now try resync + squelch + for (cnt = 0; cnt < inlen - 1 && cnt < SQUELCHCNT; ++cnt) + { + if (in[cnt + 1] != in[0]) + break; + } + if (cnt >= hdr->minSquelch + 1) + { // Resync and squelch sample repeats + prev = *in; + out[0] = RESYNC | (prev >> 1); + out[1] = SQLCH | cnt; + out += 2; + in += 1 + cnt; + inlen -= 1 + cnt; + continue; + } + + // Try a delta-coding block + if (inlen >= hdr->blockSize) + { + int bits, bestBits = 0; + int mult, bestMult = 0; + int error, bestError = hdr->maxError * 4; + + error = bestError; // for shortcutting + for (bits = 1; bits < 4 && error != 0; ++bits) + { + if (bits == 3) // no 3-bit coding + bits = 4; + + for (mult = 0; mult < 16; ++mult) + { + uint32_t blk; + int last = prev; + blk = abx_encodeBlock(hdr, in, NULL, &last, + bits, mult, &error); + if (blk > 0 && error < bestError) + { // remember the best one so far + bestError = error; + bestBits = bits; + bestMult = mult; + + if (error == 0) + break; // shortcut + } + } + } + if (bestBits > 0) + { // success! + // out+1 because we need space for the DELTAMOD byte + uint32_t blk = abx_encodeBlock(hdr, in, out + 1, &prev, + bestBits, bestMult, NULL); + if (bestBits == 4) + bestBits = 3; + *out = (bestBits << DELTASHIFT) | bestMult; + out += 1 + blk; + in += hdr->blockSize; + inlen -= hdr->blockSize; + continue; + } + } + + // And when everything else fails, emit a RESYNC + prev = *in; + ++in; + --inlen; + *out = RESYNC | (prev >> 1); + ++out; + } + + return out - abx->buf; +} diff --git a/tools/abx/abx.h b/tools/abx/abx.h new file mode 100644 index 000000000..be8d41ebe --- /dev/null +++ b/tools/abx/abx.h @@ -0,0 +1,99 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* ABX format encoder/decoder */ + +#ifndef ABX_H_INCL +#define ABX_H_INCL + +#include +#include "port.h" + +typedef struct +{ + uint16_t numFrames; // Number of frames in this file + uint32_t totalSize; // Total size of uncompressed data + uint16_t maxBufSize; // Maximum buffer needed for decoded samples + uint16_t freq; // Base sampling frequency +} abx_FileHeader; + +typedef struct +{ + uint32_t ofs; // File offset of frame data + uint16_t fsize; // Compressed frame size (in file) + uint16_t usize; // Uncompressed data size +} abx_FrameInfo; + +typedef struct +{ + uint16_t usize; // Uncompressed data size + uint16_t freq; // Sampling frequency + uint8_t blockSize; // Block size picked by encoder; power of 2 and minimum 8 + uint8_t minSquelch; // INFO: Minimum repeat count for squelching + uint16_t maxError; // INFO: Maximum total error squared per block +} abx_FrameHeader; + +#define SQLCH 0x40 // Squelch byte flag +#define RESYNC 0x80 // Resync byte flag. + +#define DELTAMOD 0x30 // Delta modulation bits. +#define DELTASHIFT 4 // Delta modulation bits. + +#define ONEBIT 0x10 // One bit delta modulate +#define TWOBIT 0x20 // Two bit delta modulate +#define FOURBIT 0x30 // four bit delta modulate + +#define MULTIPLIER 0x0F // Bottom nibble contains multiplier value. +#define SQUELCHCNT 0x3F // Bits for squelching. + +#define ABX_DEFAULT_FREQ 11025 +#define ABX_DEFAULT_ERROR 32 +#define ABX_DEFAULT_SQUELCH 2 +#define ABX_DEFAULT_BLOCKSIZE 32 + +typedef struct +{ + // read-only + int last_error; + unsigned numFrames; + uint16_t freq; + + // internal + bool writing; + FILE *fp; + abx_FrameInfo *frames; + unsigned nextFrame; + unsigned maxFrames; + uint32_t frames_ofs; + uint32_t data_ofs; + uint32_t totalSize; + int maxError; + unsigned maxBufSize; + unsigned maxEncSize; + uint8_t *buf; +} abx_File; + +bool abx_open(abx_File *abx, const char *filename); +bool abx_create(abx_File *abx, const char *filename); +void abx_close(abx_File *abx); +bool abx_setSamplingRate(abx_File *abx, uint32_t freq); +bool abx_setMaxFrames(abx_File *abx, unsigned maxFrames); +uint32_t abx_getMaxBuffer(abx_File *abx); + +uint32_t abx_readFrame(abx_File *abx, void *buf, uint32_t bufsize); +uint32_t abx_writeFrame(abx_File *abx, void *buf, uint32_t bufsize); + +#endif /* ABX_H_INCL */ diff --git a/tools/abx/abx2raw.c b/tools/abx/abx2raw.c deleted file mode 100644 index d021a83dd..000000000 --- a/tools/abx/abx2raw.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * abx to raw converter. By Serge van den Boom (svdb@stack.nl), - * The actual conversion code is from Toys for Bob. - * So far, it ignores sample rates, so it will work ok as long as all - * the frames have the same frequency. This is probably - * enough for our purposes. - * - */ - -#include -#include -#include - -#include "abx2raw.h" - -void convert_abx(FILE *in, FILE *out); -uint8_t *UnCompressAudio(struct abx_header *abx, uint8_t *source); - -int -main(int argc, char *argv[]) { - FILE *in, *out; - - if (argc != 3) { - fprintf(stderr, "abx2wav \n"); - return EXIT_FAILURE; - } - - in = fopen(argv[1], "rb"); - if (!in) { - perror("Could not open input file"); - return EXIT_FAILURE; - } - - out = fopen(argv[2], "wb"); - if (!out) { - perror("Could not open output file"); - return EXIT_FAILURE; - } - - convert_abx(in, out); - - fclose(in); - fclose(out); - return EXIT_SUCCESS; -} - -static signed char trans[16*16] = -{ - -8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8, // Multiplier of 1 - -16,-14,-12,-10,-8,-6,-4,-2,2,4,6,8,10,12,14,16, // Multiplier of 2 - -24,-21,-18,-15,-12,-9,-6,-3,3,6,9,12,15,18,21,24, // Multiplier of 3 - -32,-28,-24,-20,-16,-12,-8,-4,4,8,12,16,20,24,28,32, // Multiplier of 4 - -40,-35,-30,-25,-20,-15,-10,-5,5,10,15,20,25,30,35,40, // Multiplier of 5 - -48,-42,-36,-30,-24,-18,-12,-6,6,12,18,24,30,36,42,48, // Multiplier of 6 - -56,-49,-42,-35,-28,-21,-14,-7,7,14,21,28,35,42,49,56, // Multiplier of 7 - -64,-56,-48,-40,-32,-24,-16,-8,8,16,24,32,40,48,56,64, // Multiplier of 8 - -72,-63,-54,-45,-36,-27,-18,-9,9,18,27,36,45,54,63,72, // Multiplier of 9 - -80,-70,-60,-50,-40,-30,-20,-10,10,20,30,40,50,60,70,80, // Multiplier of 10 - -88,-77,-66,-55,-44,-33,-22,-11,11,22,33,44,55,66,77,88, // Multiplier of 11 - -96,-84,-72,-60,-48,-36,-24,-12,12,24,36,48,60,72,84,96, // Multiplier of 12 - -104,-91,-78,-65,-52,-39,-26,-13,13,26,39,52,65,78,91,104, // Multiplier of 13 - -112,-98,-84,-70,-56,-42,-28,-14,14,28,42,56,70,84,98,112, // Multiplier of 14 - -120,-105,-90,-75,-60,-45,-30,-15,15,30,45,60,75,90,105,120,// Multiplier of 15 - -128,-112,-96,-80,-64,-48,-32,-16,16,32,48,64,80,96,112,127,// Multiplier of 16 -}; - -void -read_data(char *buf, size_t size, FILE *file) { - ssize_t numread; - - numread = fread(buf, size, 1, file); - if (numread == 0 && ferror(file)) { - perror("read header"); - exit(EXIT_FAILURE); - } - if ((size_t) numread != 1) { - fprintf(stderr, "Input file too small.\n"); - exit(EXIT_FAILURE); - } -} - -void -convert_abx(FILE *in, FILE *out) { - struct abx_header abx; - struct frame_info *frame_info; - uint8_t **frames; - uint8_t **uncoded; - int i; - - read_data((uint8_t *) &abx, sizeof (struct abx_header), in); - fprintf(stderr, "Base sample rate: %dHz\n", abx.freq); - frame_info = malloc(sizeof (struct frame_info) * abx.num_frames); - read_data((uint8_t *) frame_info, - sizeof (struct frame_info) * abx.num_frames, in); - frames = malloc(sizeof (uint8_t *) * abx.num_frames); - uncoded = malloc(sizeof (uint8_t *) * abx.num_frames); - for (i = 0; i < abx.num_frames; i++) { - frames[i] = malloc(frame_info[i].fsize); - fseek(in, frame_info[i].addr, SEEK_SET); - read_data(frames[i], frame_info[i].fsize, in); - -#if 0 - // debug output to locate the first bad frame - fprintf(stderr, "Now going to process frame %d.\n", i); -#endif - -#if 0 - // skip some corrupt frames - if (i >= 270 && i <= 271) { - // fill the bad part with zeros - uncoded[i] = malloc(frame_info[i].usize); - memset(uncoded[i], '\0', frame_info[i].usize); - continue; - } -#endif - - uncoded[i] = UnCompressAudio(&abx, frames[i]); - } - - for (i = 0; i < abx.num_frames; i++) { - fwrite(uncoded[i], frame_info[i].usize, 1, out); - } - - for (i = 0; i < abx.num_frames; i++) { - free(frames[i]); - free(uncoded[i]); - } - free(uncoded); - free(frames); - free(frame_info); -} - -#define MAKE_WORD(byte1, byte2) ((byte2 << 8) | (byte1)) - -// This is used to make certain this C code is compatible when compiled on -// a 68000 based machine. (Which it has been done and tested on.) -#define Get8086word(t) MAKE_WORD ((t)[0], (t)[1]) - -// GetFreq will report the playback frequency of a particular ACOMP data -// file. -uint16_t -GetFreq(uint8_t *sound) { - return(Get8086word(sound + 2)); -} - -uint8_t * -UnCompressAudio(struct abx_header *abx, - uint8_t *source) { - uint16_t slen, frame, freq; - int16_t prev; - uint8_t *result, *dest; - - slen = Get8086word(source); - dest = result = malloc(slen * sizeof (uint8_t)); - freq = GetFreq(source); - if (freq == 0) { - freq = abx->freq; - } else if (freq != abx->freq) { - fprintf(stderr, "Frame frequency (%d) != global frequency (%d).\n", - freq, abx->freq); - fprintf(stderr, "This is not supported. Output will be corrupted.\n"); - abx->freq = freq; - } - source += 4; // Skip length, and then frequency word. - frame = *source++; // Frame size. - source += 3; // Skip sqelch value, and maximum error allowed. - prev = *source++; // Get initial previous data point. - *dest++ = prev ^ 0x80; - slen--; // Decrement total sound length. - while (slen > 0) - { - uint16_t bytes; - uint8_t sample; - - sample = *source++; // Get sample. - if (sample & RESYNC) // Is it a resync byte? - { - --slen; // Decrement output sample length. - - prev = (sample & 0x7F) << 1; // Store resync byte. - *dest++ = prev ^ 0x80; - } - else if (sample & SQLCH) // Is it a squelch byte? - { - bytes = sample & SQUELCHCNT; // And off the number of squelch bytes - slen -= bytes; // Decrement total samples remaining count. - - memset(dest, prev ^ 0x80, bytes); - dest += bytes; - } - else // Must be a delta modulate byte!! - { - int8_t *base; - - slen -= frame; // Pulling one frame out. - // Compute base address to multiplier table. - base = trans + (sample & MULTIPLIER) * 16; - switch (sample & DELTAMOD) // Delta mod resolution. - { - case ONEBIT: - { - int16_t up; - - up = base[8]; // Go up 1 bit. - for (bytes = frame / 8; bytes; bytes--) - { - uint8_t mask; - - sample = *source++; - for(mask = 0x80; mask; mask >>= 1) - { - if ( sample & mask ) - prev += up; - else - prev -= up; - if ( prev < 0 ) prev = 0; - else if ( prev > 255 ) prev = 255; - *dest++ = prev ^ 0x80; - } - } - break; - } - case TWOBIT: - base+=6; // Base address of two bit delta's. - for (bytes = frame / 4; bytes; bytes--) - { - sample = *source++; - - prev += base[sample>>6]; - if ( prev < 0 ) prev = 0; - else if ( prev > 255 ) prev = 255; - *dest++ = prev ^ 0x80; - - prev += base[(sample>>4)&0x3]; - if ( prev < 0 ) prev = 0; - else if ( prev > 255 ) prev = 255; - *dest++ = prev ^ 0x80; - - prev += base[(sample>>2)&0x3]; - if ( prev < 0 ) prev = 0; - else if ( prev > 255 ) prev = 255; - *dest++ = prev ^ 0x80; - - prev += base[sample&0x3]; - if ( prev < 0 ) prev = 0; - else if ( prev > 255 ) prev = 255; - *dest++ = prev ^ 0x80; - } - break; - case FOURBIT: - for (bytes = frame / 2; bytes; bytes--) - { - sample = *source++; - - prev += base[sample>>4]; - if ( prev < 0 ) prev = 0; - else if ( prev > 255 ) prev = 255; - *dest++ = prev ^ 0x80; - - prev += base[sample&0x0F]; - if ( prev < 0 ) prev = 0; - else if ( prev > 255 ) prev = 255; - *dest++ = prev ^ 0x80; - } - break; - } - } - // While still audio data to decompress.... - } - return result; -} - diff --git a/tools/abx/abx2raw.h b/tools/abx/abx2raw.h deleted file mode 100644 index a45caeafd..000000000 --- a/tools/abx/abx2raw.h +++ /dev/null @@ -1,27 +0,0 @@ -#include - -struct abx_header { - uint16_t num_frames __attribute__ ((packed)); - uint32_t tot_size __attribute__ ((packed)); - uint16_t bufsize __attribute__ ((packed)); - uint16_t freq __attribute__ ((packed)); -}; - -struct frame_info { - uint32_t addr __attribute__ ((packed)); - uint16_t fsize __attribute__ ((packed)); // compressed file size - uint16_t usize __attribute__ ((packed)); // uncompressed file size -}; - -#define SQLCH 0x40 // Squelch byte flag -#define RESYNC 0x80 // Resync byte flag. - -#define DELTAMOD 0x30 // Delta modulation bits. - -#define ONEBIT 0x10 // One bit delta modulate -#define TWOBIT 0x20 // Two bit delta modulate -#define FOURBIT 0x30 // four bit delta modulate - -#define MULTIPLIER 0x0F // Bottom nibble contains multiplier value. -#define SQUELCHCNT 0x3F // Bits for squelching. - diff --git a/tools/abx/abx2wav.c b/tools/abx/abx2wav.c new file mode 100644 index 000000000..6fcc4f392 --- /dev/null +++ b/tools/abx/abx2wav.c @@ -0,0 +1,90 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * ABX to WAVE converter: By Serge van den Boom (svdb@stack.nl) + * Modularized and wave output by Alex Volkov (codepro@usa.net) + * + * So far, it ignores sample rates, so it will work ok as long as all + * the frames have the same frequency. This is probably + * enough for our purposes. + * + */ + +#include +#include +#include + +#include "abx.h" +#include "wav.h" + +void convert_abx_to_wave(abx_File *abx, wave_File *wave); + +int +main(int argc, char *argv[]) { + wave_File wave; + abx_File abx; + + if (argc != 3) { + fprintf(stderr, "abx2wav \n"); + return EXIT_FAILURE; + } + + if (!abx_open(&abx, argv[1])) { + perror("Could not open input file"); + return EXIT_FAILURE; + } + + if (!wave_create(&wave, argv[2])) { + perror("Could not open output file"); + return EXIT_FAILURE; + } + + convert_abx_to_wave(&abx, &wave); + + wave_close(&wave); + abx_close(&abx); + return EXIT_SUCCESS; +} + +void +convert_abx_to_wave(abx_File *abx, wave_File *wave) { + + uint32_t bufsize; + uint8_t *buf; + uint32_t bytes; + + wave_setFormat(wave, 1, 8, abx->freq); + + bufsize = abx_getMaxBuffer(abx); + buf = malloc(bufsize); + if (!buf) { + perror("alloc buffer"); + exit(EXIT_FAILURE); + } + + for (bytes = abx_readFrame(abx, buf, bufsize); bytes > 0; ) { + if (wave_writeData(wave, buf, bytes) != bytes) { + fprintf(stderr, "Cannot write wave: %s\n", strerror(wave->last_error)); + break; + } + bytes = abx_readFrame(abx, buf, bufsize); + } + if (bytes == 0 && abx->last_error != 0) + fprintf(stderr, "Cannot read abx: %s\n", strerror(abx->last_error)); + + free(buf); +} diff --git a/tools/abx/abx2wav.dsp b/tools/abx/abx2wav.dsp new file mode 100644 index 000000000..059220707 --- /dev/null +++ b/tools/abx/abx2wav.dsp @@ -0,0 +1,116 @@ +# Microsoft Developer Studio Project File - Name="abx2wav" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=abx2wav - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "abx2wav.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "abx2wav.mak" CFG="abx2wav - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "abx2wav - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "abx2wav - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "abx2wav - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "abx2wav - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "abx2wav - Win32 Release" +# Name "abx2wav - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\abx.c +# End Source File +# Begin Source File + +SOURCE=.\abx.h +# End Source File +# Begin Source File + +SOURCE=.\abx2wav.c +# End Source File +# Begin Source File + +SOURCE=.\port.h +# End Source File +# Begin Source File + +SOURCE=.\wav.c +# End Source File +# Begin Source File + +SOURCE=.\wav.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/tools/abx/abx2wav.sh b/tools/abx/abx2wav.sh index 381971f52..e9483411b 100755 --- a/tools/abx/abx2wav.sh +++ b/tools/abx/abx2wav.sh @@ -1,26 +1,23 @@ #!/bin/sh -ABX2RAW="./abx2raw" -SOX="sox" +ABX2WAV="./abx2wav" echo "Converting all abx files in the current directory to wav files." -echo "This script looks for abx2raw in the current dir. If it's somewhere" -echo "else, edit it to point the variable ABX2RAW to the correct location." -echo "The same goes for sox, which is expected somewhere in the path." +echo "This script looks for abx2wav in the current dir. If it's somewhere" +echo "else, edit it to point the variable ABX2WAV to the correct location." echo "It's just supposed to work once on a specific set of files, and hence" echo "is pretty fragile." -echo "It is assumed that all abx files have a sample rate of 11025." -echo "If this is not the case, files won't be converted correctly." -echo "The sample rate is reported, so you can see if it goes wrong." -echo "It's also possible that the sample rate changes within one .abx file." - +echo "It's possible that the sample rate changes within one .abx file, and" +echo "if so a warning will be printed" echo "Press ENTER when ready." read -for FILE in *.abx; do +for FILE in `find . -type f -name "*.[aA][bB][xX]"`; do echo "File $FILE" + # This is lame and does not handle "Abx", but I + # do not want to mess with it too much BASE="${FILE%%.abx}" - "$ABX2RAW" "$FILE" "${BASE}.raw" - "$SOX" -c 1 -r 11025 -b -s "${BASE}.raw" "${BASE}.wav" + BASE="${BASE%%.ABX}" + "$ABX2WAV" "$FILE" "${BASE}.wav" echo done diff --git a/tools/abx/port.h b/tools/abx/port.h new file mode 100644 index 000000000..22ea2c878 --- /dev/null +++ b/tools/abx/port.h @@ -0,0 +1,40 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PORT_H_INCL +#define PORT_H_INCL + +#ifdef _MSC_VER +// MSVC +# define inline __inline + +#else +// GCC, etc. +# define inline __inline__ + +#endif + +#ifndef __bool_true_false_are_defined +# undef bool +# undef false +# undef true +typedef unsigned char bool; +#define true 1 +#define false 0 +#define __bool_true_false_are_defined +#endif /* __bool_true_false_are_defined */ + +#endif /* PORT_H_INCL */ diff --git a/tools/abx/wav.c b/tools/abx/wav.c new file mode 100644 index 000000000..3dad5e2ab --- /dev/null +++ b/tools/abx/wav.c @@ -0,0 +1,368 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Wave format encoder/decoder */ + +#include +#include +#include + +#include "wav.h" + + +#define wave_FormatHeader_size 16 +#define wave_ChunkHeader_size 8 + + +static bool read_le_16 (FILE *fp, uint16_t *v) +{ + uint8_t buf[2]; + if (fread(buf, sizeof(buf), 1, fp) != 1) + return false; + *v = (buf[1] << 8) | buf[0]; + return true; +} + +static bool read_le_32 (FILE *fp, uint32_t *v) +{ + uint8_t buf[4]; + if (fread(buf, sizeof(buf), 1, fp) != 1) + return false; + *v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + return true; +} + +static bool write_le_16 (FILE *fp, uint16_t v) +{ + uint8_t buf[2]; + buf[0] = v; + buf[1] = v >> 8; + return fwrite(buf, sizeof(buf), 1, fp) == 1; +} + +static bool write_le_32 (FILE *fp, uint32_t v) +{ + uint8_t buf[4]; + buf[0] = v; + buf[1] = v >> 8; + buf[2] = v >> 16; + buf[3] = v >> 24; + return fwrite(buf, sizeof(buf), 1, fp) == 1; +} + +static bool wave_readFileHeader(wave_File *wave, wave_FileHeader *hdr) +{ + if (!read_le_32(wave->fp, &hdr->id) || + !read_le_32(wave->fp, &hdr->size) || + !read_le_32(wave->fp, &hdr->type)) + { + wave->last_error = errno; + return false; + } + return true; +} + +static bool wave_writeFileHeader(wave_File *wave, const wave_FileHeader *hdr) +{ + if (!write_le_32(wave->fp, hdr->id) || + !write_le_32(wave->fp, hdr->size) || + !write_le_32(wave->fp, hdr->type)) + { + wave->last_error = errno; + return false; + } + return true; +} + +static bool wave_readChunkHeader(wave_File *wave, wave_ChunkHeader *chunk) +{ + if (!read_le_32(wave->fp, &chunk->id) || + !read_le_32(wave->fp, &chunk->size)) + { + wave->last_error = errno; + return false; + } + return true; +} + +static bool wave_writeChunkHeader(wave_File *wave, const wave_ChunkHeader *chunk) +{ + if (!write_le_32(wave->fp, chunk->id) || + !write_le_32(wave->fp, chunk->size)) + { + wave->last_error = errno; + return false; + } + return true; +} + +static bool wave_readFormatHeader(wave_File *wave, wave_FormatHeader *fmt) +{ + if (!read_le_16(wave->fp, &fmt->format) || + !read_le_16(wave->fp, &fmt->channels) || + !read_le_32(wave->fp, &fmt->samplesPerSec) || + !read_le_32(wave->fp, &fmt->bytesPerSec) || + !read_le_16(wave->fp, &fmt->blockAlign) || + !read_le_16(wave->fp, &fmt->bitsPerSample)) + { + wave->last_error = errno; + return false; + } + return true; +} + +static bool wave_writeFormatHeader(wave_File *wave, const wave_FormatHeader *fmt) +{ + if (!write_le_16(wave->fp, fmt->format) || + !write_le_16(wave->fp, fmt->channels) || + !write_le_32(wave->fp, fmt->samplesPerSec) || + !write_le_32(wave->fp, fmt->bytesPerSec) || + !write_le_16(wave->fp, fmt->blockAlign) || + !write_le_16(wave->fp, fmt->bitsPerSample)) + { + wave->last_error = errno; + return false; + } + return true; +} + +bool wave_open(wave_File *wave, const char *filename) +{ + wave_FileHeader fileHdr; + wave_ChunkHeader chunkHdr; + long dataLeft; + + memset(wave, 0, sizeof(*wave)); + + wave->fp = fopen(filename, "rb"); + if (!wave->fp) + { + wave->last_error = errno; + return false; + } + + // read wave header + if (!wave_readFileHeader(wave, &fileHdr)) + { + wave->last_error = errno; + wave_close(wave); + return false; + } + if (fileHdr.id != wave_RiffID || fileHdr.type != wave_WaveID) + { + fprintf(stderr, "wave_open(): " + "not a wave file, ID 0x%08x, Type 0x%08x", + (unsigned)fileHdr.id, (unsigned)fileHdr.type); + wave_close(wave); + return false; + } + + for (dataLeft = ((fileHdr.size + 1) & ~1) - 4; dataLeft > 0; + dataLeft -= (((chunkHdr.size + 1) & ~1) + 8)) + { + if (!wave_readChunkHeader(wave, &chunkHdr)) + { + wave_close(wave); + return false; + } + + if (chunkHdr.id == wave_FmtID) + { + if (!wave_readFormatHeader(wave, &wave->fmtHdr)) + { + wave_close(wave); + return false; + } + fseek(wave->fp, chunkHdr.size - 16, SEEK_CUR); + } + else + { + if (chunkHdr.id == wave_DataID) + { + wave->data_size = chunkHdr.size; + wave->data_ofs = ftell(wave->fp); + } + fseek(wave->fp, chunkHdr.size, SEEK_CUR); + } + + // 2-align the file ptr + // XXX: I do not think this is necessary in WAVE files; + // possibly a remnant of ported AIFF reader + fseek(wave->fp, chunkHdr.size & 1, SEEK_CUR); + } + + if (!wave->data_size || !wave->data_ofs) + { + fprintf(stderr, "wave_open(): bad wave file," + " no DATA chunk found"); + wave_close(wave); + return false; + } + + if (wave->fmtHdr.format != WAVE_FORMAT_PCM) + { // not a PCM format + fprintf(stderr, "wave_open(): unsupported format %x", + wave->fmtHdr.format); + wave_close(wave); + return false; + } + if (wave->fmtHdr.channels != 1 && wave->fmtHdr.channels != 2) + { + fprintf(stderr, "wave_open(): unsupported number of channels %u", + (unsigned)wave->fmtHdr.channels); + wave_close(wave); + return false; + } + + if (dataLeft != 0) + { + fprintf(stderr, "wave_open(): bad or unsupported wave file, " + "size in header does not match read chunks"); + } +#if 0 + wave->format = (wave->fmtHdr.channels == 1 ? + (wave->fmtHdr.bitsPerSample == 8 ? + wava_formats->mono8 : wava_formats->mono16) + : + (wave->fmtHdr.bitsPerSample == 8 ? + wava_formats->stereo8 : wava_formats->stereo16) + ); + wave->frequency = wave->fmtHdr.samplesPerSec; +#endif + + fseek(wave->fp, wave->data_ofs, SEEK_SET); + wave->max_pcm = wave->data_size / wave->fmtHdr.blockAlign; + wave->cur_pcm = 0; +#if 0 + wave->length = (float) wave->max_pcm / wave->fmtHdr.samplesPerSec; +#endif + wave->last_error = 0; + + return true; +} + +static bool wave_writeHeaders(wave_File *wave) +{ + wave_FileHeader fileHdr; + wave_ChunkHeader chunkHdr; + + fileHdr.id = wave_RiffID; + fileHdr.size = 4 + wave_ChunkHeader_size + wave_FormatHeader_size + + wave_ChunkHeader_size + wave->data_size; + fileHdr.type = wave_WaveID; + if (!wave_writeFileHeader(wave, &fileHdr)) + return false; + + chunkHdr.id = wave_FmtID; + chunkHdr.size = wave_FormatHeader_size; + if (!wave_writeChunkHeader(wave, &chunkHdr) || + !wave_writeFormatHeader(wave, &wave->fmtHdr)) + return false; + + chunkHdr.id = wave_DataID; + chunkHdr.size = wave->data_size; + if (!wave_writeChunkHeader(wave, &chunkHdr)) + return false; + + return true; +} + +bool wave_create(wave_File *wave, const char *filename) +{ + memset(wave, 0, sizeof(*wave)); + + wave->fp = fopen(filename, "wb"); + if (!wave->fp) + { + wave->last_error = errno; + return false; + } + + wave->fmtHdr.format = WAVE_FORMAT_PCM; + if (!wave_writeHeaders(wave)) + { + wave->last_error = errno; + return false; + } + + wave->data_ofs = ftell(wave->fp); + wave->writing = true; + return true; +} + +static bool wave_flushHeaders(wave_File *wave) +{ + wave->data_size = wave->max_pcm * wave->fmtHdr.blockAlign; + fseek(wave->fp, 0, SEEK_SET); + if (!wave_writeHeaders(wave)) + { + wave->last_error = errno; + return false; + } + return true; +} + +void wave_close(wave_File *wave) +{ + if (wave->fp) + { + if (wave->writing) + wave_flushHeaders(wave); + + fclose(wave->fp); + } + memset(wave, 0, sizeof(*wave)); +} + +bool wave_setFormat(wave_File *wave, uint16_t chans, uint16_t bitsPerSample, + uint32_t freq) +{ + wave->fmtHdr.format = WAVE_FORMAT_PCM; + wave->fmtHdr.channels = chans; + wave->fmtHdr.bitsPerSample = bitsPerSample; + wave->fmtHdr.samplesPerSec = freq; + wave->fmtHdr.blockAlign = (bitsPerSample / 8) * chans; + wave->fmtHdr.bytesPerSec = wave->fmtHdr.blockAlign * freq; + return true; +} + +uint32_t wave_readData(wave_File *wave, void *buf, uint32_t bufsize) +{ + uint32_t pcm; + + pcm = bufsize / wave->fmtHdr.blockAlign; + if (pcm > wave->max_pcm - wave->cur_pcm) + pcm = wave->max_pcm - wave->cur_pcm; + + pcm = fread(buf, wave->fmtHdr.blockAlign, pcm, wave->fp); + wave->cur_pcm += pcm; + + return pcm * wave->fmtHdr.blockAlign; +} + +uint32_t wave_writeData(wave_File *wave, void *buf, uint32_t bufsize) +{ + uint32_t pcm; + + pcm = bufsize / wave->fmtHdr.blockAlign; + + pcm = fwrite(buf, wave->fmtHdr.blockAlign, pcm, wave->fp); + wave->cur_pcm += pcm; + wave->max_pcm = wave->cur_pcm; + wave->data_size += pcm * wave->fmtHdr.blockAlign; + + return pcm * wave->fmtHdr.blockAlign; +} diff --git a/tools/abx/wav.h b/tools/abx/wav.h new file mode 100644 index 000000000..f38893388 --- /dev/null +++ b/tools/abx/wav.h @@ -0,0 +1,81 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Wave format encoder/decoder */ + +#ifndef WAV_H_INCL +#define WAV_H_INCL + +#include +#include "port.h" + +#define wave_MAKE_ID(x1, x2, x3, x4) \ + (((x4) << 24) | ((x3) << 16) | ((x2) << 8) | (x1)) + +#define wave_RiffID wave_MAKE_ID('R', 'I', 'F', 'F') +#define wave_WaveID wave_MAKE_ID('W', 'A', 'V', 'E') +#define wave_FmtID wave_MAKE_ID('f', 'm', 't', ' ') +#define wave_DataID wave_MAKE_ID('d', 'a', 't', 'a') + +typedef struct +{ + uint32_t id; + uint32_t size; + uint32_t type; +} wave_FileHeader; + +typedef struct +{ + uint16_t format; + uint16_t channels; + uint32_t samplesPerSec; + uint32_t bytesPerSec; + uint16_t blockAlign; + uint16_t bitsPerSample; +} wave_FormatHeader; + +#define WAVE_FORMAT_PCM 1 + +typedef struct +{ + uint32_t id; + uint32_t size; +} wave_ChunkHeader; + +typedef struct +{ + // read-only + wave_FormatHeader fmtHdr; + int last_error; + + // internal + bool writing; + FILE *fp; + uint32_t data_ofs; + uint32_t data_size; + uint32_t max_pcm; + uint32_t cur_pcm; +} wave_File; + +bool wave_open(wave_File *wave, const char *filename); +bool wave_create(wave_File *wave, const char *filename); +void wave_close(wave_File *wave); +bool wave_setFormat(wave_File *wave, uint16_t chans, uint16_t bitsPerSample, + uint32_t freq); +uint32_t wave_readData(wave_File *wave, void *buf, uint32_t bufsize); +uint32_t wave_writeData(wave_File *wave, void *buf, uint32_t bufsize); + +#endif /* WAV_H_INCL */ diff --git a/tools/abx/wav2abx.c b/tools/abx/wav2abx.c new file mode 100644 index 000000000..0ef0dd8d5 --- /dev/null +++ b/tools/abx/wav2abx.c @@ -0,0 +1,123 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * WAVE to ABX: By Serge van den Boom (svdb@stack.nl) and + * Alex Volkov (codepro@usa.net) + * + */ + +#include +#include +#include + +#include "abx.h" +#include "wav.h" + +#define BUFFER_SIZE 0x1000 + +void convert_wave_to_abx(wave_File *wave, abx_File *abx); + +int +main(int argc, char *argv[]) { + wave_File wave; + abx_File abx; + + if (argc != 3) { + fprintf(stderr, "wav2abx \n"); + return EXIT_FAILURE; + } + + if (!wave_open(&wave, argv[1])) { + perror("Could not open input file"); + return EXIT_FAILURE; + } + if (wave.fmtHdr.channels != 1) { + fprintf(stderr, "Unsupported number of channels %u\n", + (unsigned)wave.fmtHdr.channels); + return EXIT_FAILURE; + } + if (wave.fmtHdr.bitsPerSample != 8 && wave.fmtHdr.bitsPerSample != 16) { + fprintf(stderr, "Unsupported bits per sample %u\n", + (unsigned)wave.fmtHdr.bitsPerSample); + return EXIT_FAILURE; + } + + if (!abx_create(&abx, argv[2])) { + perror("Could not open output file"); + return EXIT_FAILURE; + } + + convert_wave_to_abx(&wave, &abx); + + abx_close(&abx); + wave_close(&wave); + return EXIT_SUCCESS; +} + +void +convert_16(void *buf, uint32_t bufsize) { + uint8_t *src = buf; + uint8_t *dst = buf; + + for ( ; bufsize > 1; src += 2, ++dst, bufsize -= 2) { + int v = (int16_t)((src[1] << 8) | src[0]); + // with error correction + v += 0x80; + if (v > 0x7fff) + v = 0x7fff; + *dst = ((v >> 8) & 0xff) ^ 0x80; + } +} + +void +convert_wave_to_abx(wave_File *wave, abx_File *abx) { + + uint32_t bufsize; + uint8_t buf[BUFFER_SIZE * 2]; + uint32_t bytes; + unsigned numFrames; + unsigned bytesPerSample; + + abx_setSamplingRate(abx, wave->fmtHdr.samplesPerSec); + // Calculate number of frames as close as we can so that + // we do not waste file bytes + bytesPerSample = wave->fmtHdr.bitsPerSample / 8; + numFrames = (wave->data_size / bytesPerSample + BUFFER_SIZE - 1) + / BUFFER_SIZE; + abx_setMaxFrames(abx, numFrames); + + // We support 16 bit samples + bufsize = BUFFER_SIZE; + if (bytesPerSample == 2) + bufsize *= 2; + + for (bytes = wave_readData(wave, buf, bufsize); bytes > 0; ) { + if (bytesPerSample == 2) + { + convert_16(buf, bytes); + bytes /= 2; + } + + if (abx_writeFrame(abx, buf, bytes) == 0 && abx->last_error != 0) { + fprintf(stderr, "Cannot write abx: %s\n", strerror(abx->last_error)); + break; + } + bytes = wave_readData(wave, buf, bufsize); + } + if (bytes == 0 && wave->last_error != 0) + fprintf(stderr, "Cannot read wave: %s\n", strerror(wave->last_error)); +} diff --git a/tools/abx/wav2abx.dsp b/tools/abx/wav2abx.dsp new file mode 100644 index 000000000..823996d71 --- /dev/null +++ b/tools/abx/wav2abx.dsp @@ -0,0 +1,116 @@ +# Microsoft Developer Studio Project File - Name="wav2abx" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=wav2abx - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "wav2abx.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "wav2abx.mak" CFG="wav2abx - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "wav2abx - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "wav2abx - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "wav2abx - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "wav2abx - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "wav2abx - Win32 Release" +# Name "wav2abx - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\abx.c +# End Source File +# Begin Source File + +SOURCE=.\abx.h +# End Source File +# Begin Source File + +SOURCE=.\wav2abx.c +# End Source File +# Begin Source File + +SOURCE=.\port.h +# End Source File +# Begin Source File + +SOURCE=.\wav.c +# End Source File +# Begin Source File + +SOURCE=.\wav.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project