Re: AudioFile -> AudioConverter -> float PCM
Re: AudioFile -> AudioConverter -> float PCM
- Subject: Re: AudioFile -> AudioConverter -> float PCM
- From: Art Gillespie <email@hidden>
- Date: Sat, 13 Aug 2005 09:01:18 -0700
On Aug 1, 2005, at 11:10 AM, Marc Poirier wrote:
I am trying to do the following: I want to use AudioFile to get
the contents of an audio file, then use AudioConverter to put it
all into arrays of PCM 32-bit float audio data (1 array per audio
channel).
<snip>
Anyway, I'm sure that I'm not the only one who has wanted to do
this task, so I was wondering if anyone might have some example
code for this that they'd like to share?
Here's my code (uncensored and not cleaned up, sorry ) that does
that. The function uses AudioFile to open the specified file, and
AudioConverter to load it into 'canonical' internal format at the
current operating sample rate. It also contains a workaround for an
edge case: old AIFF files that are block aligned aren't handled
correctly by AudioFile.
HTH.
OSStatus SampleManagerLoadSampleFromFSRef (
SampleManagerRef inRef,
FSRef* inFile,
SampleManagerID* outID )
{
/* check parameters */
if ( NULL == inRef )
return kSampleManagerErr_InvalidParameter;
OSStatus err = noErr;
UInt32 fileFormat;
UInt32 dataSize;
UInt64 bytesInSample;
UInt64 packetsInSample;
void* tmpBuffer;
AudioFileID aFile;
AudioStreamBasicDescription srcDesc;
/* what internal format do we want the data in */
CAStreamBasicDescription dstDesc;
AudioConverterRef theConverter;
SampleRef theSample;
/* get audio file attributes */
err = AudioFileOpen ( inFile, fsRdPerm, 0, &aFile );
if ( noErr != err )
{
LOGN1 ( kLogLevelError, "Couldn't open audio file: %d", err );
return err;
}
dataSize = sizeof ( UInt32 );
err = AudioFileGetProperty ( aFile,
kAudioFilePropertyFileFormat, &dataSize, &fileFormat );
dataSize = sizeof ( AudioStreamBasicDescription );
err = AudioFileGetProperty ( aFile,
kAudioFilePropertyDataFormat, &dataSize, &srcDesc );
dataSize = sizeof ( UInt64 );
err = AudioFileGetProperty ( aFile,
kAudioFilePropertyAudioDataByteCount, &dataSize, &bytesInSample );
err = AudioFileGetProperty ( aFile,
kAudioFilePropertyAudioDataPacketCount, &dataSize, &packetsInSample );
SInt64 pOffset;
dataSize = sizeof ( SInt64 );
err = AudioFileGetProperty ( aFile,
kAudioFilePropertyDataOffset, &dataSize, &pOffset );
GSLog ( kLogLevelDebug, "pOffset: %lld", pOffset );
if ( noErr != err )
{
LOGN1 ( kLogLevelError, "Couldn't get audio file property: %
d", err );
AudioFileClose ( aFile );
return err;
}
/* we only allow samples of certain size */
if ( kIDMaxSampleLen < bytesInSample )
{
AudioFileClose ( aFile );
return kSampleManagerErr_SampleTooLarge;
}
/* create new SampleRef */
theSample = (SampleRef) malloc ( sizeof ( Sample ) ) ;
theSample->_ref = 0;
theSample->_numFrames = 0;
theSample->_data = 0;
theSample->_numChannels = srcDesc.mChannelsPerFrame;
if ( srcDesc.mChannelsPerFrame == 2 )
theSample->processStereo = (void*)&DefaultStereoFunction;
else
theSample->processStereo = (void*)&DefaultMonoToStereoFunction;
theSample->processMono = (void*)&DefaultMonoFunction;
/* read the file into temporary memory */
tmpBuffer = malloc ( bytesInSample );
memset(tmpBuffer, 0, bytesInSample);
UInt32 outByteSize = bytesInSample;
UInt32 readOffset = 0;
/* WORKAROUND: check for offset in SSND if this is AIFF */
/* There is an apparent bug in AudioFile that doesn't correctly
report the offset and length correctly for AIFF files that have
block alignment.
*/
if ( fileFormat == kAudioFileAIFFType )
{
OSStatus err;
UInt32 maxPathSize = 1024;
UInt8 cstr_path[maxPathSize];
err = FSRefMakePath(inFile, (UInt8*)cstr_path, maxPathSize );
assert(err == noErr);
FILE* aifFile = fopen((const char*)cstr_path, "r");
assert(aifFile); //wtf? we shouldn't have gotten here if
the file is bogus
//skip past the FORM xxxx AIFF header - 12 bytes
fseek ( aifFile, 12, SEEK_CUR );
while ( true )
{
char chunkID[5];
chunkID[4] = 0;
long chunkLen;
size_t rd = fread( chunkID, 4, 1, aifFile );
if ( EOF == rd )
{
break;
}
/* FIXME: Endian-ness conversion for X86 */
rd = fread ( &chunkLen, 4, 1, aifFile );
if ( EOF == rd )
{
break;
}
if ( strcmp(chunkID, "SSND") == 0 )
{
fread ( &readOffset, 4, 1, aifFile );
GSLog ( kLogLevelDebug, "OFFSET: %d", readOffset );
break;
}
else
{
fseek ( aifFile, chunkLen, SEEK_CUR );
}
}
fclose(aifFile);
}
outByteSize -= readOffset;
err = AudioFileReadBytes( aFile, false, readOffset,
&outByteSize, (void*)tmpBuffer );
UInt32 nPackets = packetsInSample;
if ( noErr != err )
{
free ( tmpBuffer );
free ( theSample );
LOGN1 ( kLogLevelError, "AudioFileReadBytes Failed: %d", err );
return err;
}
if ( bytesInSample != outByteSize )
{
LOGN2 ( kLogLevelError, "!!!!!!!!! AudioFileReadBytes did
not read the entire file: %d : %d", bytesInSample, outByteSize );
}
AudioFileClose(aFile);
/* do the conversion */
UInt32 ioOutSize = (UInt32) bytesInSample;
dstDesc.SetCanonical(srcDesc.mChannelsPerFrame, true);
dstDesc.mSampleRate = inRef->_sr;
err = AudioConverterNew( &srcDesc, &dstDesc, &theConverter );
if ( noErr != err )
{
free ( tmpBuffer );
free ( theSample );
LOGN1 ( kLogLevelError, "AudioConverterNew Failed: %d", err );
return err;
}
dataSize = sizeof ( UInt32 );
err = AudioConverterGetProperty( theConverter,
kAudioConverterPropertyCalculateOutputBufferSize,
&dataSize, (void*)
&ioOutSize );
if ( noErr != err )
{
free ( tmpBuffer );
free ( theSample );
AudioConverterDispose ( theConverter );
LOGN1 (kLogLevelError, "AudioConverterGetProperty (calculate
output buffer size) failed: %d", err );
return err;
}
/* convert into SampleRef's buffer */
theSample->_numFrames = ioOutSize/dstDesc.mBytesPerFrame;
/* 4 extra floats per channel as guard bytes */
theSample->_data = (Float32*) malloc (ioOutSize + 4*sizeof
(Float32)*srcDesc.mChannelsPerFrame);
memset(theSample->_data, 0, ioOutSize + 4*sizeof(Float32)
*srcDesc.mChannelsPerFrame);
/* carefully now! we move the _data pointer four floats*num
channels forward for our guard frames */
/* REMEMBER THIS WHEN YOU FREE _data DUMBASS!!!!! */
theSample->_data += 2*srcDesc.mChannelsPerFrame;
RetainSample ( theSample );
convdata theData;
theData.data = tmpBuffer;
theData.counter = bytesInSample;
theData.dataSize = bytesInSample;
err=AudioConverterFillBuffer (
theConverter,
MyConverterDataInputProc,
(void*)&theData,
&ioOutSize,
theSample->_data );
if ( noErr != err )
{
free ( tmpBuffer );
free ( theSample->_data );
free ( theSample );
theSample->_data = NULL;
AudioConverterDispose ( theConverter );
LOGN1 ( kLogLevelError, "AudioConverterFillBuffer failed: %
d", err );
return err;
}
AudioConverterDispose(theConverter);
free (tmpBuffer);
/* create new unique SampleManagerID for dictionary key */
UInt32 newId = inRef->_key++;
*outID = CFNumberCreate ( kCFAllocatorDefault,
kCFNumberLongType, &newId );
/* store SampleRef in dictionary, return SampleManagerID */
CFDictionaryAddValue ( inRef->_samples, *outID, theSample );
return noErr;
}
_______________________________________________
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