ABX<->WAV converter; ABX code modularized, ABX encoder added, direct WAV file io

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3596 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2011-05-04 20:19:27 +00:00
parent ede7d2a545
commit c18134d041
13 changed files with 1925 additions and 315 deletions
+8 -3
View File
@@ -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
+874
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <errno.h>
#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;
}
+99
View File
@@ -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 <stdint.h>
#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 */
-272
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <infile> <outfile>\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;
}
-27
View File
@@ -1,27 +0,0 @@
#include <stdint.h>
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.
+90
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <infile> <outfile>\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);
}
+116
View File
@@ -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
+10 -13
View File
@@ -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
+40
View File
@@ -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 */
+368
View File
@@ -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 <stdio.h>
#include <memory.h>
#include <errno.h>
#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;
}
+81
View File
@@ -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 <stdint.h>
#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 */
+123
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <infile> <outfile>\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));
}
+116
View File
@@ -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