Added WAVE file output to AIFF converter; fixed AIFF decoding bug which caused 8 extra samples in the output

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3609 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2011-05-14 17:27:36 +00:00
parent c0cabf8bcb
commit 7b0505facc
10 changed files with 1273 additions and 361 deletions
+425
View File
@@ -0,0 +1,425 @@
/*
* 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.
*/
/* AIFF format decoder
* By Serge van den Boom (svdb@stack.nl) 20020816,
* modularization by Alex Volkov (codepro@usa.net)
*
* Doesn't convert all .aif files in general, only 8- and 16-bit PCM and
* AIFF-C 16-bit SDX2-compressed.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <assert.h>
#include "aiff.h"
static int aiff_decodePCM(aiff_File*, void *buf, uint32_t bufsize);
static int aiff_decodeSDX2(aiff_File*, void *buf, uint32_t bufsize);
static bool read_be_16 (FILE *fp, uint16_t *v)
{
uint8_t buf[2];
if (fread(buf, sizeof(buf), 1, fp) != 1)
return false;
*v = (buf[0] << 8) | buf[1];
return true;
}
static bool read_be_32 (FILE *fp, uint32_t *v)
{
uint8_t buf[4];
if (fread(buf, sizeof(buf), 1, fp) != 1)
return false;
*v = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
return true;
}
// Read 80-bit IEEE 754 floating point number.
// We are only interested in values that we can work with,
// so using an sint32 here is fine.
static bool read_be_f80(FILE *fp, int32_t *v)
{
int sign, exp;
int shift;
uint16_t se;
uint32_t mant, mant_low;
if (!read_be_16(fp, &se) ||
!read_be_32(fp, &mant) || !read_be_32(fp, &mant_low))
return false;
sign = (se >> 15) & 1; // sign is the highest bit
exp = (se & ((1 << 15) - 1)); // exponent is next highest 15 bits
#if 0 // XXX: 80bit IEEE 754 used in AIFF uses explicit mantissa MS bit
// mantissa has an implied leading bit which is typically 1
mant >>= 1;
if (exp != 0)
mant |= 0x80000000;
#endif
mant >>= 1; // we also need space for sign
exp -= (1 << 14) - 1; // exponent is biased by (2^(e-1) - 1)
shift = exp - 31 + 1; // mantissa is already 31 bits before decimal pt.
if (shift > 0)
mant = 0x7fffffff; // already too big
else if (shift < 0)
mant >>= -shift;
*v = sign ? -(int32_t)mant : (int32_t)mant;
return true;
}
static bool aiff_readFileHeader(aiff_File *aiff, aiff_FileHeader *hdr)
{
if (!read_be_32(aiff->fp, &hdr->chunk.id) ||
!read_be_32(aiff->fp, &hdr->chunk.size) ||
!read_be_32(aiff->fp, &hdr->type))
{
aiff->last_error = errno;
return false;
}
return true;
}
static bool aiff_readChunkHeader(aiff_File *aiff, aiff_ChunkHeader *hdr)
{
if (!read_be_32(aiff->fp, &hdr->id) ||
!read_be_32(aiff->fp, &hdr->size))
{
aiff->last_error = errno;
return false;
}
return true;
}
static int aiff_readCommonChunk(aiff_File *aiff, uint32_t size, aiff_ExtCommonChunk *fmt)
{
int bytes;
memset(fmt, sizeof(*fmt), 0);
if (size < AIFF_COMM_SIZE)
{
aiff->last_error = EIO;
return 0;
}
if (!read_be_16(aiff->fp, &fmt->channels) ||
!read_be_32(aiff->fp, &fmt->sampleFrames) ||
!read_be_16(aiff->fp, &fmt->sampleSize) ||
!read_be_f80(aiff->fp, &fmt->sampleRate))
{
aiff->last_error = errno;
return 0;
}
bytes = AIFF_COMM_SIZE;
if (size >= AIFF_EXT_COMM_SIZE)
{
if (!read_be_32(aiff->fp, &fmt->extTypeID))
{
aiff->last_error = errno;
return 0;
}
bytes += sizeof(fmt->extTypeID);
}
return bytes;
}
static bool aiff_readSoundDataChunk(aiff_File *aiff, aiff_SoundDataChunk *data)
{
if (!read_be_32(aiff->fp, &data->offset) ||
!read_be_32(aiff->fp, &data->blockSize))
{
aiff->last_error = errno;
return false;
}
return true;
}
bool aiff_open(aiff_File *aiff, const char *filename)
{
aiff_FileHeader fileHdr;
aiff_ChunkHeader chunkHdr;
uint32_t sdata_size = 0;
long remSize;
aiff->fp = fopen(filename, "rb");
if (!aiff->fp)
{
aiff->last_error = errno;
return false;
}
aiff->data_size = 0;
aiff->max_pcm = 0;
aiff->data_ofs = 0;
memset(&aiff->fmtHdr, 0, sizeof(aiff->fmtHdr));
memset(aiff->prev_val, sizeof(aiff->prev_val), 0);
// read the header
if (!aiff_readFileHeader(aiff, &fileHdr))
{
aiff->last_error = errno;
aiff_close(aiff);
return false;
}
if (fileHdr.chunk.id != aiff_FormID)
{
fprintf(stderr, "aiff_open(): not an aiff file, ID 0x%08x",
(unsigned)fileHdr.chunk.id);
aiff_close(aiff);
return false;
}
if (fileHdr.type != aiff_FormTypeAIFF && fileHdr.type != aiff_FormTypeAIFC)
{
fprintf(stderr, "aiff_open(): unsupported aiff file, Type 0x%08x",
(unsigned)fileHdr.type);
aiff_close(aiff);
return false;
}
for (remSize = fileHdr.chunk.size - sizeof(aiff_ID); remSize > 0;
remSize -= ((chunkHdr.size + 1) & ~1) + AIFF_CHUNK_HDR_SIZE)
{
if (!aiff_readChunkHeader(aiff, &chunkHdr))
{
aiff_close(aiff);
return false;
}
if (chunkHdr.id == aiff_CommonID)
{
int read = aiff_readCommonChunk(aiff, chunkHdr.size, &aiff->fmtHdr);
if (!read)
{
aiff_close(aiff);
return false;
}
fseek(aiff->fp, chunkHdr.size - read, SEEK_CUR);
}
else if (chunkHdr.id == aiff_SoundDataID)
{
aiff_SoundDataChunk data;
if (!aiff_readSoundDataChunk(aiff, &data))
{
aiff_close(aiff);
return false;
}
sdata_size = chunkHdr.size - AIFF_SSND_SIZE - data.offset;
aiff->data_ofs = ftell(aiff->fp) + data.offset;
fseek(aiff->fp, chunkHdr.size - AIFF_SSND_SIZE, SEEK_CUR);
}
else
{ // skip uninteresting chunk
fseek(aiff->fp, chunkHdr.size, SEEK_CUR);
}
// 2-align the file ptr
fseek(aiff->fp, chunkHdr.size & 1, SEEK_CUR);
}
if (aiff->fmtHdr.sampleFrames == 0)
{
fprintf(stderr, "aiff_open(): aiff file has no sound data");
aiff_close(aiff);
return false;
}
// make bits-per-sample a multiple of 8
aiff->bits_per_sample = (aiff->fmtHdr.sampleSize + 7) & ~7;
if (aiff->bits_per_sample == 0 || aiff->bits_per_sample > 16)
{ // XXX: for now we do not support 24 and 32 bps
fprintf(stderr, "aiff_open(): unsupported sample size %u",
aiff->bits_per_sample);
aiff_close(aiff);
return false;
}
if (aiff->fmtHdr.sampleRate == 0 || aiff->fmtHdr.sampleRate > 480000)
{
fprintf(stderr, "aiff_open(): unsupported sampling rate %ld",
(long)aiff->fmtHdr.sampleRate);
aiff_close(aiff);
return false;
}
aiff->block_align = aiff->bits_per_sample / 8 * aiff->fmtHdr.channels;
aiff->file_block = aiff->block_align;
if (!aiff->data_ofs)
{
fprintf(stderr, "aiff_open(): bad aiff file, no SSND chunk found");
aiff_close(aiff);
return false;
}
if (fileHdr.type == aiff_FormTypeAIFF)
{
if (aiff->fmtHdr.extTypeID != 0)
{
fprintf(stderr, "aiff_open(): unsupported extension 0x%08x",
(unsigned)aiff->fmtHdr.extTypeID);
aiff_close(aiff);
return false;
}
aiff->compType = aifc_None;
}
else if (fileHdr.type == aiff_FormTypeAIFC)
{
if (aiff->fmtHdr.extTypeID != aiff_CompressionTypeSDX2)
{
fprintf(stderr, "aiff_open(): unsupported compression 0x%08x",
(unsigned)aiff->fmtHdr.extTypeID);
aiff_close(aiff);
return false;
}
aiff->compType = aifc_Sdx2;
aiff->file_block /= 2;
if (aiff->fmtHdr.channels > MAX_CHANNELS)
{
fprintf(stderr, "aiff_open(): number of channels (%u) too large",
(unsigned)aiff->fmtHdr.channels);
aiff_close(aiff);
return false;
}
}
aiff->data_size = aiff->fmtHdr.sampleFrames * aiff->file_block;
if (sdata_size < aiff->data_size)
{
fprintf(stderr, "aiff_open(): sound data size %u is less than "
"computed %u\n",
(unsigned)sdata_size, (unsigned)aiff->data_size);
aiff->fmtHdr.sampleFrames = sdata_size / aiff->file_block;
aiff->data_size = aiff->fmtHdr.sampleFrames * aiff->file_block;
}
if (aiff->compType == aifc_Sdx2 && aiff->bits_per_sample != 16)
{
fprintf(stderr, "aiff_open(): unsupported sample size %u for SDX2",
(unsigned)aiff->fmtHdr.sampleSize);
aiff_close(aiff);
return false;
}
fseek(aiff->fp, aiff->data_ofs, SEEK_SET);
aiff->max_pcm = aiff->fmtHdr.sampleFrames;
aiff->cur_pcm = 0;
aiff->last_error = 0;
return true;
}
void aiff_close(aiff_File *aiff)
{
if (aiff->fp)
{
fclose (aiff->fp);
aiff->fp = NULL;
}
}
int aiff_readData(aiff_File *aiff, void *buf, uint32_t bufsize)
{
switch (aiff->compType)
{
case aifc_None:
return aiff_decodePCM(aiff, buf, bufsize);
case aifc_Sdx2:
return aiff_decodeSDX2(aiff, buf, bufsize);
default:
assert(false && "Unknown compession type");
return 0;
}
}
static int aiff_decodePCM(aiff_File *aiff, void *buf, uint32_t bufsize)
{
uint32_t dec_pcm;
uint32_t size;
dec_pcm = bufsize / aiff->block_align;
if (dec_pcm > aiff->max_pcm - aiff->cur_pcm)
dec_pcm = aiff->max_pcm - aiff->cur_pcm;
dec_pcm = fread(buf, aiff->file_block, dec_pcm, aiff->fp);
aiff->cur_pcm += dec_pcm;
size = dec_pcm * aiff->block_align;
if (aiff->bits_per_sample == 8)
{ // AIFF files store 8-bit data as signed
// and we need it unsigned
uint8_t* ptr = (uint8_t*)buf;
uint32_t left;
for (left = size; left > 0; --left, ++ptr)
*ptr ^= 0x80;
}
else if (aiff->bits_per_sample == 16)
{ // AIFF files store 16-bit data big-endian
// and we need it in machine order
uint8_t* ptr = (uint8_t*)buf;
uint32_t left;
for (left = size / 2; left > 0; --left, ptr += 2)
*(int16_t*)ptr = (ptr[0] << 8) | ptr[1];
}
return size;
}
static int aiff_decodeSDX2(aiff_File *aiff, void* buf, uint32_t bufsize)
{
uint32_t dec_pcm;
int8_t *src;
int16_t *dst = buf;
uint32_t left;
uint32_t size;
dec_pcm = bufsize / aiff->block_align;
if (dec_pcm > aiff->max_pcm - aiff->cur_pcm)
dec_pcm = aiff->max_pcm - aiff->cur_pcm;
src = (int8_t*)buf + bufsize - (dec_pcm * aiff->file_block);
dec_pcm = fread(src, aiff->file_block, dec_pcm, aiff->fp);
aiff->cur_pcm += dec_pcm;
size = dec_pcm * aiff->block_align;
for (left = dec_pcm; left > 0; --left)
{
int i;
int32_t *prev = aiff->prev_val;
for (i = aiff->fmtHdr.channels; i > 0; --i, ++prev, ++src, ++dst)
{
int32_t v = (*src * abs(*src)) << 1;
if (*src & 1)
v += *prev;
// Saturate the value. This is just a safety measure, as it
// should never be necessary in SDX2.
if (v > 32767)
v = 32767;
else if (v < -32768)
v = -32768;
*prev = v;
*dst = v;
}
}
return size;
}