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:
+8
-3
@@ -1,6 +1,11 @@
|
|||||||
abx2raw: abx2raw.c abx2raw.h
|
all: abx2wav wav2abx
|
||||||
gcc -W -Wall -g -O0 abx2raw.c -o abx2raw
|
|
||||||
|
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:
|
clean:
|
||||||
rm abx2raw
|
rm abx2wav abx2wav.exe wav2abx wav2abx.exe
|
||||||
|
|
||||||
|
|||||||
+874
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 */
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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.
|
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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
@@ -1,26 +1,23 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
ABX2RAW="./abx2raw"
|
ABX2WAV="./abx2wav"
|
||||||
SOX="sox"
|
|
||||||
|
|
||||||
echo "Converting all abx files in the current directory to wav files."
|
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 "This script looks for abx2wav in the current dir. If it's somewhere"
|
||||||
echo "else, edit it to point the variable ABX2RAW to the correct location."
|
echo "else, edit it to point the variable ABX2WAV to the correct location."
|
||||||
echo "The same goes for sox, which is expected somewhere in the path."
|
|
||||||
echo "It's just supposed to work once on a specific set of files, and hence"
|
echo "It's just supposed to work once on a specific set of files, and hence"
|
||||||
echo "is pretty fragile."
|
echo "is pretty fragile."
|
||||||
echo "It is assumed that all abx files have a sample rate of 11025."
|
echo "It's possible that the sample rate changes within one .abx file, and"
|
||||||
echo "If this is not the case, files won't be converted correctly."
|
echo "if so a warning will be printed"
|
||||||
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 "Press ENTER when ready."
|
echo "Press ENTER when ready."
|
||||||
read
|
read
|
||||||
|
|
||||||
for FILE in *.abx; do
|
for FILE in `find . -type f -name "*.[aA][bB][xX]"`; do
|
||||||
echo "File $FILE"
|
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}"
|
BASE="${FILE%%.abx}"
|
||||||
"$ABX2RAW" "$FILE" "${BASE}.raw"
|
BASE="${BASE%%.ABX}"
|
||||||
"$SOX" -c 1 -r 11025 -b -s "${BASE}.raw" "${BASE}.wav"
|
"$ABX2WAV" "$FILE" "${BASE}.wav"
|
||||||
echo
|
echo
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 */
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user