From: <av...@us...> - 2011-05-14 17:27:43
|
Revision: 3609 http://sc2.svn.sourceforge.net/sc2/?rev=3609&view=rev Author: avolkov Date: 2011-05-14 17:27:36 +0000 (Sat, 14 May 2011) Log Message: ----------- Added WAVE file output to AIFF converter; fixed AIFF decoding bug which caused 8 extra samples in the output Modified Paths: -------------- trunk/tools/aif/Makefile trunk/tools/aif/aif2wav.sh Added Paths: ----------- trunk/tools/aif/aif2wav.c trunk/tools/aif/aif2wav.dsp trunk/tools/aif/aiff.c trunk/tools/aif/aiff.h trunk/tools/aif/wav.c trunk/tools/aif/wav.h Removed Paths: ------------- trunk/tools/aif/aif2raw.c trunk/tools/aif/aif2raw.h Modified: trunk/tools/aif/Makefile =================================================================== --- trunk/tools/aif/Makefile 2011-05-13 23:55:45 UTC (rev 3608) +++ trunk/tools/aif/Makefile 2011-05-14 17:27:36 UTC (rev 3609) @@ -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 Deleted: trunk/tools/aif/aif2raw.c =================================================================== --- trunk/tools/aif/aif2raw.c 2011-05-13 23:55:45 UTC (rev 3608) +++ trunk/tools/aif/aif2raw.c 2011-05-14 17:27:36 UTC (rev 3609) @@ -1,278 +0,0 @@ -/* - * aif to raw converter. By Serge van den Boom (sv...@st...), - * 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); -} - - Deleted: trunk/tools/aif/aif2raw.h =================================================================== --- trunk/tools/aif/aif2raw.h 2011-05-13 23:55:45 UTC (rev 3608) +++ trunk/tools/aif/aif2raw.h 2011-05-14 17:27:36 UTC (rev 3609) @@ -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 */ -}; - Copied: trunk/tools/aif/aif2wav.c (from rev 3596, trunk/tools/aif/aif2raw.c) =================================================================== --- trunk/tools/aif/aif2wav.c (rev 0) +++ trunk/tools/aif/aif2wav.c 2011-05-14 17:27:36 UTC (rev 3609) @@ -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 (sv...@st...) 20020816 + * AIFF decoder modularization and WAVE ouput by Alex Volkov (co...@us...) + * + * 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); +} + Added: trunk/tools/aif/aif2wav.dsp =================================================================== --- trunk/tools/aif/aif2wav.dsp (rev 0) +++ trunk/tools/aif/aif2wav.dsp 2011-05-14 17:27:36 UTC (rev 3609) @@ -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 Property changes on: trunk/tools/aif/aif2wav.dsp ___________________________________________________________________ Added: svn:eol-style + CRLF Modified: trunk/tools/aif/aif2wav.sh =================================================================== --- trunk/tools/aif/aif2wav.sh 2011-05-13 23:55:45 UTC (rev 3608) +++ trunk/tools/aif/aif2wav.sh 2011-05-14 17:27:36 UTC (rev 3609) @@ -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 Copied: trunk/tools/aif/aiff.c (from rev 3596, trunk/tools/aif/aif2raw.c) =================================================================== --- trunk/tools/aif/aiff.c (rev 0) +++ trunk/tools/aif/aiff.c 2011-05-14 17:27:36 UTC (rev 3609) @@ -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 (sv...@st...) 20020816, + * modularization by Alex Volkov (co...@us...) + * + * 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; +} Copied: trunk/tools/aif/aiff.h (from rev 3596, trunk/tools/aif/aif2raw.h) =================================================================== --- trunk/tools/aif/aiff.h (rev 0) +++ trunk/tools/aif/aiff.h 2011-05-14 17:27:36 UTC (rev 3609) @@ -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 */ Copied: trunk/tools/aif/wav.c (from rev 3608, trunk/tools/abx/wav.c) =================================================================== --- trunk/tools/aif/wav.c (rev 0) +++ trunk/tools/aif/wav.c 2011-05-14 17:27:36 UTC (rev 3609) @@ -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; +} Copied: trunk/tools/aif/wav.h (from rev 3608, trunk/tools/abx/wav.h) =================================================================== --- trunk/tools/aif/wav.h (rev 0) +++ trunk/tools/aif/wav.h 2011-05-14 17:27:36 UTC (rev 3609) @@ -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 */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |