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
+3 -3
View File
@@ -1,7 +1,7 @@
aif2raw: aif2raw.c aif2raw.h
gcc -W -Wall -g -O0 aif2raw.c -o aif2raw
aif2wav: aif2wav.c aiff.h aiff.c wav.h wav.c port.h
gcc -W -Wall -g -O0 aif2wav.c aiff.c wav.c -o aif2wav
clean:
rm aif2raw
rm aif2wav aif2wav.exe
-278
View File
@@ -1,278 +0,0 @@
/*
* aif to raw converter. By Serge van den Boom (svdb@stack.nl),
* 20020816
* Doesn't convert all .aif files in general, only AIFF-C, 16 bits
* SDX2-compressed.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aif2raw.h"
void convert_aif(FILE *in, FILE *out);
void write_data(const uint8_t *data, ssize_t size, int numChannels,
FILE *out);
int
main(int argc, char *argv[]) {
FILE *in, *out;
if (argc != 3) {
fprintf(stderr, "aif2wav <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_aif(in, out);
fclose(in);
fclose(out);
return EXIT_SUCCESS;
}
/* local to file byte order */
inline uint32_t
ltof32(uint32_t val) {
return (val >> 24) |
((val & 0x00ff0000L) >> 8) |
((val & 0x0000ff00L) << 8) |
(val << 24);
}
/* file to local byte order */
inline uint32_t
ftol32(uint32_t val) {
return (val >> 24) |
((val & 0x00ff0000L) >> 8) |
((val & 0x0000ff00L) << 8) |
(val << 24);
}
/* local to file byte order */
inline uint16_t
ltof16(uint16_t val) {
return (val >> 8) | (val << 8);
}
/* file to local byte order */
inline uint16_t
ftol16(uint16_t val) {
return (val >> 8) | (val << 8);
}
#if 0
/* local to file byte order */
inline void
ltofld(char *dest, long double val) {
}
#endif
/* file to local byte order */
inline long double
ftolld(const char *val) {
long double result;
((char *) &result)[0] = val[9];
((char *) &result)[1] = val[8];
((char *) &result)[2] = val[7];
((char *) &result)[3] = val[6];
((char *) &result)[4] = val[5];
((char *) &result)[5] = val[4];
((char *) &result)[6] = val[3];
((char *) &result)[7] = val[2];
((char *) &result)[8] = val[1];
((char *) &result)[9] = val[0];
((char *) &result)[10] = 0x00;
((char *) &result)[11] = 0x00;
return result;
}
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);
}
}
struct aifc_Chunk *
read_chunk(FILE *in) {
struct aifc_Chunk *result;
size_t numread;
result = malloc(sizeof (struct aifc_ChunkHeader));
numread = fread(result, sizeof (struct aifc_ChunkHeader), 1, in);
if (numread == 0) {
free(result);
if (ferror(in))
fprintf(stderr, "Fread failed.\n");
return NULL;
}
result = realloc(result, sizeof (struct aifc_ChunkHeader) +
ftol32(result->ckSize));
numread = fread(&result->ckData, ftol32(result->ckSize), 1, in);
if (numread == 0) {
free(result);
if (ferror(in))
fprintf(stderr, "Fread failed.\n");
return NULL;
}
return result;
}
/* number to increase number of sections by if all preallocated
sections are full */
#define CHUNK_NUM_INC 5
struct aifc_Chunk **
read_chunks(FILE *in) {
struct aifc_Chunk **chunks;
struct aifc_ContainerChunk *form;
int maxchunks;
int numchunks;
/* The FORM chunk contains all the other chunks. */
form = malloc(sizeof (struct aifc_ContainerChunk));
read_data((char *) form, sizeof (struct aifc_ContainerChunk), in);
if (form->formType != aifc_FormTypeAIFC) {
fprintf(stderr, "File is not an AIFF-C file.\n");
free(form);
exit(EXIT_FAILURE);
}
free(form);
numchunks = 0;
maxchunks = 0;
chunks = NULL;
while (1) {
if (numchunks >= maxchunks) {
maxchunks += CHUNK_NUM_INC;
chunks = realloc(chunks,
(maxchunks + 1) * sizeof (struct aifc_Chunk *));
}
chunks[numchunks] = read_chunk(in);
if (chunks[numchunks] == NULL)
break;
numchunks++;
}
chunks = realloc(chunks,
(numchunks + 1) * sizeof (struct aifc_Chunk *));
return chunks;
}
void
free_chunks(struct aifc_Chunk **chunks) {
int i;
for (i = 0; chunks[i] != NULL; i++)
free(chunks[i]);
free(chunks);
}
void
convert_aif(FILE *in, FILE *out) {
struct aifc_Chunk **chunks;
struct aifc_FormatVersionChunk *fverChunk;
struct aifc_ExtCommonChunk *commChunk;
struct aifc_SoundDataChunk *ssndChunk;
int i;
fverChunk = NULL;
commChunk = NULL;
ssndChunk = NULL;
chunks = read_chunks(in);
for (i = 0; chunks[i] != NULL; i++) {
if (chunks[i]->ckID == aifc_FormVersionID && fverChunk == NULL) {
fverChunk = (struct aifc_FormatVersionChunk *) chunks[i];
} else if (chunks[i]->ckID == aifc_CommonID && commChunk == NULL) {
commChunk = (struct aifc_ExtCommonChunk *) chunks[i];
} else if (chunks[i]->ckID == aifc_SoundDataID && ssndChunk == NULL) {
ssndChunk = (struct aifc_SoundDataChunk *) chunks[i];
}
}
if (fverChunk == NULL) {
fprintf(stderr, "No format version chunk found.\n");
exit(EXIT_FAILURE);
}
if (commChunk == NULL) {
fprintf(stderr, "No common chunk found.\n");
exit(EXIT_FAILURE);
}
if (ssndChunk == NULL) {
fprintf(stderr, "No sound data chunk found.\n");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Number of channels: %d\n",
ftol16(commChunk->numChannels));
fprintf(stderr, "Number of sample frames: %d\n",
ftol32(commChunk->numSampleFrames));
fprintf(stderr, "Number of bits per sample: %d\n",
ftol16(commChunk->sampleSize));
fprintf(stderr, "Sample rate: %.2LfHz\n",
ftolld((char *) &commChunk->sampleRate));
if (ftol16(commChunk->sampleSize) != 16) {
fprintf(stderr, "Only 16-bits files are supported.\n");
exit(EXIT_FAILURE);
}
if (commChunk->compressionType != aifc_CompressionTypeSDX2) {
fprintf(stderr, "Only SDX2 compression is supported.\n");
exit(EXIT_FAILURE);
}
write_data(ssndChunk->data + ftol32(ssndChunk->offset),
ftol32(ssndChunk->ckSize) - ftol32(ssndChunk->offset),
ftol16(commChunk->numChannels), out);
free_chunks(chunks);
}
void
write_data(const uint8_t *data, ssize_t size, int numChannels, FILE *out) {
const uint8_t *srcptr, *endptr;
uint16_t *buffer, *dstptr;
int i;
int16_t *last;
endptr = data + size;
srcptr = data;
buffer = malloc(size * sizeof (uint16_t));
dstptr = buffer;
last = malloc(numChannels * sizeof(int16_t));
memset(last, '\0', numChannels * sizeof(uint16_t));
while (srcptr < endptr) {
for (i = 0; i < numChannels; i++) {
if (*srcptr & 1) {
*dstptr = last[i] +
((*(int8_t *)srcptr * abs(*(int8_t *)srcptr)) << 1);
} else
*dstptr = (*(int8_t *)srcptr * abs(*(int8_t *)srcptr)) << 1;
last[i] = *dstptr;
dstptr++;
srcptr++;
}
}
fwrite(buffer, size * sizeof (uint16_t), 1, out);
free(last);
free(buffer);
}
-69
View File
@@ -1,69 +0,0 @@
#include <stdint.h>
typedef uint32_t aifc_ID;
typedef uint8_t aifc_Extended[10];
/* This should actually be a 80-bits integer, but
* long double takes 96 bits, even though only 80 are used */
#define aifc_MAKE_ID(x1, x2, x3, x4) \
(((x4) << 24) | ((x3) << 16) | ((x2) << 8) | (x1))
#define aifc_FormID aifc_MAKE_ID('F', 'O', 'R', 'M')
#define aifc_FormVersionID aifc_MAKE_ID('F', 'V', 'E', 'R')
#define aifc_CommonID aifc_MAKE_ID('C', 'O', 'M', 'M')
#define aifc_SoundDataID aifc_MAKE_ID('S', 'S', 'N', 'D')
#define aifc_FormTypeAIFF aifc_MAKE_ID('A', 'I', 'F', 'F')
#define aifc_FormTypeAIFC aifc_MAKE_ID('A', 'I', 'F', 'C')
#define aifc_CompressionTypeSDX2 aifc_MAKE_ID('S', 'D', 'X', '2')
struct aifc_ChunkHeader {
aifc_ID ckID;
uint32_t ckSize;
};
struct aifc_Chunk {
aifc_ID ckID; /* Chunk ID */
uint32_t ckSize; /* Chunk size, excluding header */
uint8_t ckData[]; /* placeholder for data */
};
struct aifc_ContainerChunk {
aifc_ID ckID; /* "FORM" */
uint32_t ckSize; /* number of bytes of data */
aifc_ID formType; /* type of file, "AIFC" */
};
struct aifc_FormatVersionChunk {
aifc_ID ckID; /* "FVER" */
uint32_t ckSize; /* 4 */
uint32_t timestamp; /* date of format version, in Mac format */
};
struct aifc_ExtCommonChunk {
aifc_ID ckID; /* "COMM" */
uint32_t ckSize; /* size of chunk data */
uint16_t numChannels; /* number of channels */
uint32_t numSampleFrames;
/* number of sample frames */
uint16_t sampleSize;
/* number of bits per sample */
aifc_Extended sampleRate;
/* number of frames per second */
aifc_ID compressionType;
/* compression type ID */
char *compressionName;
/* compression type name */
} __attribute__ ((packed));
struct aifc_SoundDataChunk {
aifc_ID ckID; /* "SSND" */
uint32_t ckSize; /* size of chunk data */
uint32_t offset; /* offset to sound data */
uint32_t blocksize; /* size of alignment blocks */
uint8_t data[]; /* placeholder for data */
};
+101
View File
@@ -0,0 +1,101 @@
/*
* 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 to WAVE converter.
* By Serge van den Boom (svdb@stack.nl) 20020816
* AIFF decoder modularization and WAVE ouput by Alex Volkov (codepro@usa.net)
*
* Doesn't convert all .aif files in general, only AIFF-C, 16 bits
* SDX2-compressed, and AIFF 8-/16-bit PCM.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aiff.h"
#include "wav.h"
void convert_aiff_to_wave(aiff_File *aiff, wave_File *wave);
int
main(int argc, char *argv[]) {
wave_File wave;
aiff_File aiff;
if (argc != 3) {
fprintf(stderr, "aif2wav <infile> <outfile>\n");
return EXIT_FAILURE;
}
if (!aiff_open(&aiff, 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_aiff_to_wave(&aiff, &wave);
wave_close(&wave);
aiff_close(&aiff);
return EXIT_SUCCESS;
}
void
convert_aiff_to_wave(aiff_File *aiff, wave_File *wave) {
uint32_t bufsize;
uint8_t *buf;
uint32_t bytes;
wave_setFormat(wave, aiff->fmtHdr.channels, aiff->fmtHdr.sampleSize,
aiff->fmtHdr.sampleRate);
bufsize = 0x10000;
buf = malloc(bufsize);
if (!buf) {
perror("alloc buffer");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Number of channels: %u\n",
(unsigned)aiff->fmtHdr.channels);
fprintf(stderr, "Number of sample frames: %u\n",
(unsigned)aiff->fmtHdr.sampleFrames);
fprintf(stderr, "Number of bits per sample: %u\n",
(unsigned)aiff->fmtHdr.sampleSize);
fprintf(stderr, "Sample rate: %d Hz\n",
(int)aiff->fmtHdr.sampleRate);
for (bytes = aiff_readData(aiff, buf, bufsize); bytes > 0; ) {
if (wave_writeData(wave, buf, bytes) != bytes) {
fprintf(stderr, "Cannot write wave: %s\n", strerror(wave->last_error));
break;
}
bytes = aiff_readData(aiff, buf, bufsize);
}
if (bytes == 0 && aiff->last_error != 0)
fprintf(stderr, "Cannot read aiff: %s\n", strerror(aiff->last_error));
free(buf);
}
+116
View File
@@ -0,0 +1,116 @@
# Microsoft Developer Studio Project File - Name="aif2wav" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=aif2wav - 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 "aif2wav.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 "aif2wav.mak" CFG="aif2wav - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "aif2wav - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "aif2wav - 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)" == "aif2wav - 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)" == "aif2wav - 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 "aif2wav - Win32 Release"
# Name "aif2wav - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\aif2wav.c
# End Source File
# Begin Source File
SOURCE=.\aiff.c
# End Source File
# Begin Source File
SOURCE=.\aiff.h
# 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
+8 -11
View File
@@ -1,25 +1,22 @@
#!/bin/sh
AIF2RAW="./aif2raw"
SOX="sox"
AIF2WAV="./aif2wav"
echo "Converting all aif files in the current directory to wav files."
echo "This script looks for aif2raw in the current dir. If it's somewhere"
echo "else, edit it to point the variable AIF2RAW to the correct location."
echo "The same goes for sox, which is expected somewhere in the path."
echo "This script looks for aif2wav in the current dir. If it's somewhere"
echo "else, edit it to point the variable AIF2WAV to the correct location."
echo "It's just supposed to work once on a specific set of files, and hence"
echo "is pretty fragile."
echo "It is assumed that all aif files have a sample rate of 11025."
echo "If this is not the case, files won't be converted correctly."
echo "The sample rate is reported, so you can see if it goes wrong."
echo "Press ENTER when ready."
read DUMMY
for FILE in *.aif; do
for FILE in `find . -type f -name "*.[aA][iI][fF]"`; do
echo "File $FILE"
# This is lame and does not handle "Aif", but I
# do not want to mess with it too much
BASE="${FILE%%.aif}"
"$AIF2RAW" "$FILE" "${BASE}.raw"
"$SOX" -c 2 -r 44100 -w -s "${BASE}.raw" "${BASE}.wav"
BASE="${BASE%%.AIF}"
"$AIF2WAV" "$FILE" "${BASE}.wav"
echo
done
+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;
}
+122
View File
@@ -0,0 +1,122 @@
/*
* 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 */
#ifndef AIFF_H_INCL
#define AIFF_H_INCL
#include <stdint.h>
#include "port.h"
typedef uint32_t aiff_ID;
#define aiff_MAKE_ID(x1, x2, x3, x4) \
(((x1) << 24) | ((x2) << 16) | ((x3) << 8) | (x4))
#define aiff_FormID aiff_MAKE_ID('F', 'O', 'R', 'M')
#define aiff_FormVersionID aiff_MAKE_ID('F', 'V', 'E', 'R')
#define aiff_CommonID aiff_MAKE_ID('C', 'O', 'M', 'M')
#define aiff_SoundDataID aiff_MAKE_ID('S', 'S', 'N', 'D')
#define aiff_FormTypeAIFF aiff_MAKE_ID('A', 'I', 'F', 'F')
#define aiff_FormTypeAIFC aiff_MAKE_ID('A', 'I', 'F', 'C')
#define aiff_CompressionTypeSDX2 aiff_MAKE_ID('S', 'D', 'X', '2')
typedef struct
{
aiff_ID id; /* Chunk ID */
uint32_t size; /* Chunk size, excluding header */
} aiff_ChunkHeader;
#define AIFF_CHUNK_HDR_SIZE (4+4)
typedef struct
{
aiff_ChunkHeader chunk;
aiff_ID type;
} aiff_FileHeader;
typedef struct
{
uint32_t version; /* format version, in Mac format */
} aiff_FormatVersionChunk;
typedef struct
{
uint16_t channels; /* number of channels */
uint32_t sampleFrames; /* number of sample frames */
uint16_t sampleSize; /* number of bits per sample */
int32_t sampleRate; /* number of frames per second */
/* this is actually stored as IEEE-754 80bit in files */
} aiff_CommonChunk;
#define AIFF_COMM_SIZE (2+4+2+10)
typedef struct
{
uint16_t channels; /* number of channels */
uint32_t sampleFrames; /* number of sample frames */
uint16_t sampleSize; /* number of bits per sample */
int32_t sampleRate; /* number of frames per second */
aiff_ID extTypeID; /* compression type ID */
char extName[32]; /* compression type name */
} aiff_ExtCommonChunk;
#define AIFF_EXT_COMM_SIZE (AIFF_COMM_SIZE+4)
typedef struct
{
uint32_t offset; /* offset to sound data */
uint32_t blockSize; /* size of alignment blocks */
} aiff_SoundDataChunk;
#define AIFF_SSND_SIZE (4+4)
typedef enum
{
aifc_None,
aifc_Sdx2,
} aiff_CompressionType;
#define MAX_CHANNELS 16
typedef struct
{
// read-only
int last_error;
aiff_ExtCommonChunk fmtHdr;
aiff_CompressionType compType;
// internal
FILE *fp;
unsigned bits_per_sample;
unsigned block_align;
unsigned file_block;
uint32_t data_ofs;
uint32_t data_size;
uint32_t max_pcm;
uint32_t cur_pcm;
int32_t prev_val[MAX_CHANNELS];
} aiff_File;
bool aiff_open(aiff_File*, const char *filename);
void aiff_close(aiff_File*);
int aiff_readData(aiff_File*, void *buf, uint32_t bufsize);
#endif /* AIFF_H_INCL */
+416
View File
@@ -0,0 +1,416 @@
/*
* 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 (dataLeft != 0)
{
fprintf(stderr, "wave_open(): bad or unsupported wave file, "
"size in header does not match read chunks");
}
wave->bytesPerSample = (wave->fmtHdr.bitsPerSample + 7) >> 3;
if (wave->bytesPerSample == 3)
{
fprintf(stderr, "wave_open(): 24-bit data is not fully supported\n");
}
fseek(wave->fp, wave->data_ofs, SEEK_SET);
wave->max_pcm = wave->data_size / wave->fmtHdr.blockAlign;
wave->cur_pcm = 0;
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->bytesPerSample = (bitsPerSample + 7) >> 3;
if (wave->bytesPerSample == 3)
{
fprintf(stderr, "wave_setFormat(): 24-bit data is not fully supported\n");
}
wave->fmtHdr.samplesPerSec = freq;
wave->fmtHdr.blockAlign = wave->bytesPerSample * 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;
uint8_t *ptr = (uint8_t*)buf;
uint32_t size;
uint32_t left;
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;
size = pcm * wave->fmtHdr.blockAlign;
// WAVE files store data little-endian and we need it in machine order
// Let the compiler optimize this away if it is not necessary
switch (wave->bytesPerSample)
{
case 2:
for (left = size / 2; left > 0; --left, ptr += 2)
*(int16_t*)ptr = (ptr[1] << 8) | ptr[0];
break;
case 3:
// TODO
break;
case 4:
for (left = size / 4; left > 0; --left, ptr += 4)
*(int32_t*)ptr = (ptr[3] << 24) | (ptr[2] << 16) | (ptr[1] << 8) | ptr[0];
break;
}
return size;
}
uint32_t wave_writeData(wave_File *wave, void *buf, uint32_t bufsize)
{
uint32_t pcm;
uint8_t *ptr = (uint8_t*)buf;
uint32_t size;
uint32_t left;
pcm = bufsize / wave->fmtHdr.blockAlign;
size = pcm * wave->fmtHdr.blockAlign;
// WAVE files store data little-endian and we have it in machine order
// Let the compiler optimize this away if it is not necessary
switch (wave->bytesPerSample)
{
case 2:
for (left = size / 2; left > 0; --left, ptr += 2)
{
int16_t v = *(int16_t*)ptr;
ptr[0] = (v & 0xff);
ptr[1] = (v >> 8);
}
break;
case 3:
// TODO
break;
case 4:
for (left = size / 4; left > 0; --left, ptr += 4)
{
int32_t v = *(int32_t*)ptr;
ptr[0] = (v & 0xff);
ptr[1] = (v >> 8);
ptr[2] = (v >> 16);
ptr[3] = (v >> 24);
}
break;
}
pcm = fwrite(buf, wave->fmtHdr.blockAlign, pcm, wave->fp);
wave->cur_pcm += pcm;
wave->max_pcm = wave->cur_pcm;
size = pcm * wave->fmtHdr.blockAlign;
wave->data_size += size;
return size;
}
+82
View File
@@ -0,0 +1,82 @@
/*
* 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;
unsigned bytesPerSample;
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 */