I need to load sound files to a Cocoa-based OpenAL app.
Progress:
The OpenAL utility function alutLoadWAVFile has been deprecated; the alut header is no longer included in Mac OS X SDKs. According to the TechNotes, the actual code is still there for binary compatibility. However, if I attempt to add a declaration for the function, the code will compile but the linker will abort, complaining that the symbol for alutLoadWAVFile could not be found. (I am linking to the OpenAL.framework).
Yet, Apple OpenAL sample code still uses this symbol. When I Clean the sample code project, it compiles and links just fine. Yet there is no declaration of the function to be found. (Side question: how can it build and link, then?)
So, I found some code by George Warner at Apple, containing replacement functions for alutCreateBufferFromFile and alutLoadMemoryFromFile. Although capable of creating an OpenAL buffer directly from most any kind of audio file, the code appears to support only 8bit mono sound files. 16bit stereo or mono 44khz files result in a nasty hissing sound and clipping. (The files are ok; Quicktime plays them just fine.)
Thus, my question: can someone please point me to some .wav loading code/help for Cocoa/Carbon, suitable for use with an OpenAL Buffer? Thankyou.
I'm sure you've solved this already, but for people who find this via Google, here's some barely tested WAV loading code. It works but you'd better double check for memory leaks and whatnot before using for something real.
static bool LoadWAVFile(const char* filename, ALenum* format, ALvoid** data, ALsizei* size, ALsizei* freq, Float64* estimatedDurationOut)
{
CFStringRef filenameStr = CFStringCreateWithCString( NULL, filename, kCFStringEncodingUTF8 );
CFURLRef url = CFURLCreateWithFileSystemPath( NULL, filenameStr, kCFURLPOSIXPathStyle, false );
CFRelease( filenameStr );
AudioFileID audioFile;
OSStatus error = AudioFileOpenURL( url, kAudioFileReadPermission, kAudioFileWAVEType, &audioFile );
CFRelease( url );
if ( error != noErr )
{
fprintf( stderr, "Error opening audio file. %d\n", error );
return false;
}
AudioStreamBasicDescription basicDescription;
UInt32 propertySize = sizeof(basicDescription);
error = AudioFileGetProperty( audioFile, kAudioFilePropertyDataFormat, &propertySize, &basicDescription );
if ( error != noErr )
{
fprintf( stderr, "Error reading audio file basic description. %d\n", error );
AudioFileClose( audioFile );
return false;
}
if ( basicDescription.mFormatID != kAudioFormatLinearPCM )
{
// Need PCM for Open AL. WAVs are (I believe) by definition PCM, so this check isn't necessary. It's just here
// in case I ever use this with another audio format.
fprintf( stderr, "Audio file is not linear-PCM. %d\n", basicDescription.mFormatID );
AudioFileClose( audioFile );
return false;
}
UInt64 audioDataByteCount = 0;
propertySize = sizeof(audioDataByteCount);
error = AudioFileGetProperty( audioFile, kAudioFilePropertyAudioDataByteCount, &propertySize, &audioDataByteCount );
if ( error != noErr )
{
fprintf( stderr, "Error reading audio file byte count. %d\n", error );
AudioFileClose( audioFile );
return false;
}
Float64 estimatedDuration = 0;
propertySize = sizeof(estimatedDuration);
error = AudioFileGetProperty( audioFile, kAudioFilePropertyEstimatedDuration, &propertySize, &estimatedDuration );
if ( error != noErr )
{
fprintf( stderr, "Error reading estimated duration of audio file. %d\n", error );
AudioFileClose( audioFile );
return false;
}
ALenum alFormat = 0;
if ( basicDescription.mChannelsPerFrame == 1 )
{
if ( basicDescription.mBitsPerChannel == 8 )
alFormat = AL_FORMAT_MONO8;
else if ( basicDescription.mBitsPerChannel == 16 )
alFormat = AL_FORMAT_MONO16;
else
{
fprintf( stderr, "Expected 8 or 16 bits for the mono channel but got %d\n", basicDescription.mBitsPerChannel );
AudioFileClose( audioFile );
return false;
}
}
else if ( basicDescription.mChannelsPerFrame == 2 )
{
if ( basicDescription.mBitsPerChannel == 8 )
alFormat = AL_FORMAT_STEREO8;
else if ( basicDescription.mBitsPerChannel == 16 )
alFormat = AL_FORMAT_STEREO16;
else
{
fprintf( stderr, "Expected 8 or 16 bits per channel but got %d\n", basicDescription.mBitsPerChannel );
AudioFileClose( audioFile );
return false;
}
}
else
{
fprintf( stderr, "Expected 1 or 2 channels in audio file but got %d\n", basicDescription.mChannelsPerFrame );
AudioFileClose( audioFile );
return false;
}
UInt32 numBytesToRead = audioDataByteCount;
void* buffer = malloc( numBytesToRead );
if ( buffer == NULL )
{
fprintf( stderr, "Error allocating buffer for audio data of size %u\n", numBytesToRead );
return false;
}
error = AudioFileReadBytes( audioFile, false, 0, &numBytesToRead, buffer );
AudioFileClose( audioFile );
if ( error != noErr )
{
fprintf( stderr, "Error reading audio bytes. %d\n", error );
free(buffer);
return false;
}
if ( numBytesToRead != audioDataByteCount )
{
fprintf( stderr, "Tried to read %lld bytes from the audio file but only got %d bytes\n", audioDataByteCount, numBytesToRead );
free(buffer);
return false;
}
*freq = basicDescription.mSampleRate;
*size = audioDataByteCount;
*format = alFormat;
*data = buffer;
*estimatedDurationOut = estimatedDuration;
return true;
}
Use the AudioFileReadBytes
function from Audio Services. Examples can be found in the Finch sound engine, see the Sound+IO category.
This may be an obvious suggestion, but since you didn't mention it: have you tried the library at http://www.openal.org/ as suggested in Apple's technote?
As for how the sample code links and builds, it's not finding a prototype (if you turn on -Wall, you'll get an implicit function declaration warning), but OpenAL.framework--at least in the SDK they are using in the sample project--does in fact export _alutLoadWAVFile, which you can check with nm. What's the exact link error you get, and what SDK are you using?
来源:https://stackoverflow.com/questions/805454/load-wav-file-for-openal-in-cocoa