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:
+3
-3
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user