7b0505facc
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3609 8092fc87-c524-0410-9efc-e669fe64eaf9
426 lines
10 KiB
C
426 lines
10 KiB
C
/*
|
|
* 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;
|
|
}
|