Re: Trying to concatenate two wav files on an iPhone is ExtAudioFile the best way?
Re: Trying to concatenate two wav files on an iPhone is ExtAudioFile the best way?
- Subject: Re: Trying to concatenate two wav files on an iPhone is ExtAudioFile the best way?
- From: Paul Scott <email@hidden>
- Date: Wed, 18 Aug 2010 21:13:11 -0700
Here's code I wrote years ago for a now defunct company. It takes a
list of input filenames (.WAV files) and an output file name (.WAV
file), and combines the input files into a single output file. It isn't
iPhone, it isn't Objective C, it isn't CoreAudio. But it does combine
.WAV files by re-wrapping them into a single file. It was written in
Borland C++, but with a tiny bit of tweaking it can work with any C++
compiler. Maybe it will be of use to someone.
Paul
//---------------------------------------------------------------------------
#ifndef CombinerH
#define CombinerH
//---------------------------------------------------------------------------
#define WAVBSIZE 32760
void ProcessTrafficFiles( TList *, AnsiString *, AnsiString * );
void CombineWaves( TList *, AnsiString *, bool );
typedef struct _WAVERIFF * PWAVERIFF;
typedef struct _WAVERIFF {
char id[4];
int length;
char wave[4];
} WAVERIFF;
typedef struct _WAVEFMT * PWAVEFMT;
typedef struct _WAVEFMT {
char id[4];
int length;
short fmt;
short channels;
int samplesps;
int avgbytesps;
short align;
short bitsps;
} WAVEFMT;
typedef struct _WAVEDATA * PWAVEDATA;
typedef struct _WAVEDATA {
char id[4];
int length;
char data[];
} WAVEDATA;
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
/*
-------------------------------------------------------------------------
CombineWaves()
Accepts a TList of AnsiString full-path file names, an AnsiString
output
full-path file name, and a MultiBlock flag.
Processing is as follows:
Remove from the TList any file name without a .wav extension. If no
files
remain, then quit.
For each input file, copy the wave data into the output file
according to
the MulitBlock flag.
If the MultiBlock flag is false, then all wave data is combined into a
single data block in the output file. The first file in the TList sets
the output format, and the remaining input files must have the same
format. The format blocks are not checked for consistency.
If the MultiBlock flag is true, then the input files may have different
formats, such as sampling rate, stereo, etc. The format block from each
file is copied to the output file, so that the .wav player can change
formats on-the-fly. This is the more powerful option, but may not be
compatible with all .wav players. In fact, I'm not aware of any that
support this, but it's here nonetheless.
-------------------------------------------------------------------------
*/
void CombineWaves( TList *FileList, AnsiString *OutFile, bool MultiBlock
) {
WAVERIFF riff; // Wave RIFF CHUNK Layout
WAVEFMT fmt; // Wave FMT CHUNK Layout
WAVEDATA data; // Wave DATA CHUNK Layout
FILE *iWave, *oWave; // Input and Output Stream Pointers
char buffer[WAVBSIZE];
bool bFormatWritten = false;
bool bDataWritten = false;
int n, total = 0;
// Remove items from list without a .wav extension
for ( int i = 0; i < FileList->Count; i++ ) {
AnsiString s( *((AnsiString *)(FileList->Items[i])) );
if ( s.LowerCase().AnsiPos( AnsiString( ".wav" ) ) != s.Length()
- 3 ) {
FileList->Delete( i-- );
continue;
}
}
// Return if nothing to process
if ( FileList->Count < 1 ) return;
// Open the Ouput File
AnsiString oPath = *OutFile;
if ( ( oWave = fopen( oPath.c_str(), "w+b" ) ) == NULL ) {
AnsiString s = "Error opening '"; s += oPath; s += "'";
//Application->MessageBox( s.c_str(), "Error Message", IDOK );
}
else {
setvbuf( oWave, NULL, _IOFBF, 32767 );
// Write the RIFF Header
strncpy( riff.id, "RIFF", 4 );
strncpy( riff.wave, "WAVE", 4 );
riff.length = 0;
fwrite( &riff, sizeof( riff ), 1, oWave );
// Copy wave data from each input file to output file
for ( int i = 0; i < FileList->Count; i++ ) {
AnsiString iPath = *((AnsiString*) FileList->Items[i]);
if ( ( iWave = fopen( iPath.c_str(), "rb" ) ) == NULL ) {
# if defined( WRITELOG )
fprintf( log, "%s Can't open %s\n", CTS, iPath.c_str() );
fflush( log );
# endif
continue;
}
setvbuf( iWave, NULL, _IOFBF, 32767 );
// Write the FMT header
// If MultiBlock, write the FMT header for each input wave file,
// otherwise, only write one FMT header from the first input file
if ( ( ! bFormatWritten ) | MultiBlock ) {
if ( !LocateHeader( iWave, (PWAVEDATA) &fmt, "fmt " ) )
continue;
bFormatWritten = true;
fread( &fmt.length, 1, sizeof( fmt.length ), iWave );
if ( ! ( fmt.length < WAVBSIZE ) ) continue;
fread( &buffer, 1, fmt.length, iWave );
fseek( oWave, 0L, SEEK_END );
fwrite( &fmt, 1, sizeof( fmt.id ) + sizeof( fmt.length ),
oWave );
fwrite( buffer, 1, fmt.length, oWave );
if ( MultiBlock ) total += fmt.length;
}
// Find the DATA block and add data length to total length
// Skip past any blocks between the FMT and DATA block, or
// break out if fread() returns less than we expect
if ( !LocateHeader( iWave, (PWAVEDATA) &data, "data" ) )
continue;
n = fread( &data.length, 1, sizeof( data.length ), iWave );
if ( n != sizeof( data.length ) ) continue;
total += data.length;
// Write the DATA header only for the first input file
// OR, for each input file when MultiBlock is specified
if ( ( !bDataWritten ) | MultiBlock ) {
bDataWritten = true;
fwrite( &data, 1, sizeof( data ), oWave );
if ( MultiBlock ) total += sizeof( data );
}
// Now, copy the actual data from input to output
// There is simple support here for a corrupt input wave file
with
// an excessive data length, so that we stop at end of file.
for ( n = 0; data.length > WAVBSIZE; data.length -= n ) {
if ( ( n = fread( buffer, 1, WAVBSIZE, iWave ) ) ==
WAVBSIZE ) {
fwrite( buffer, 1, n, oWave );
}
else {
if ( n ) fwrite( buffer, 1, n, oWave );
total -= ( WAVBSIZE - n );
data.length = 0;
break;
}
}
if ( data.length ) {
if ( ( n = fread( buffer, 1, data.length, iWave ) ) ==
data.length ) {
fwrite( buffer, 1, n, oWave );
data.length -= n;
}
else {
if ( n ) fwrite( buffer, 1, n, oWave );
total -= ( data.length - n );
data.length = 0;
}
}
fclose( iWave );
}
// If the total length is zero (which could happen on a read
error or
// when all the .wav files were missing) add dummy FMT and DATA
blocks
if ( ! total ) {
fseek( oWave, sizeof( riff ), SEEK_SET );
strncpy( fmt.id, "fmt ", 4 );
fmt.length = 16;
fmt.fmt = 1;
fmt.channels = 1;
fmt.samplesps = 22050;
fmt.avgbytesps = 44100;
fmt.align = 2;
fmt.bitsps = 16;
if ( MultiBlock ) total += sizeof( fmt );
fwrite( &fmt, 1, sizeof( fmt ), oWave );
strncpy( data.id, "data", 4 );
data.length = 2;
buffer[0] = buffer[1] = 0;
total += data.length;
if ( MultiBlock ) total += sizeof( data );
fwrite( &data, 1, sizeof( data ), oWave );
fwrite( buffer, 1, 2, oWave );
}
// Now that the data copy is done we need to update the length in
// the data header to reflect the total of all files copied
// EXCEPT in MultiBlock mode, which has the correct length already
// in the individual data blocks
if ( !MultiBlock ) {
LocateHeader( oWave, (PWAVEDATA) &fmt, "fmt " );
fread( &fmt.length, 1, sizeof( fmt.length ), oWave );
fseek( oWave, fmt.length, SEEK_CUR );
fread( &data, 1, sizeof( data ), oWave );
data.length = total;
fseek( oWave, sizeof( data )*-1, SEEK_CUR );
fwrite( &data, 1, sizeof( data ), oWave );
}
// And, finally, update the RIFF header to reflect the total
// output data length, and close up shop
fseek( oWave, 0L, SEEK_SET );
fread( &riff, 1, sizeof( riff ), oWave );
riff.length = total + ( MultiBlock ? 0 : 8 + sizeof( fmt ) + 4 );
fseek( oWave, 0L, SEEK_SET );
fwrite( &riff, 1, sizeof( data ), oWave );
fclose( oWave );
}
}
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden