3

我需要将声音文件加载到基于 Cocoa 的 OpenAL 应用程序中。

进步:

  • OpenAL 实用程序函数 alutLoadWAVFile 已被弃用;alut 标头不再包含在 Mac OS X SDK 中。根据技术说明,实际代码仍然存在以实现二进制兼容性。但是,如果我尝试为函数添加声明,代码将编译但链接器将中止,并抱怨找不到 alutLoadWAVFile 的符号。(我链接到 OpenAL.framework)。

  • 然而,Apple OpenAL 示例代码仍然使用这个符号。当我清理示例代码项目时,它可以很好地编译和链接。然而,没有声明要找到的函数。(附带问题:那么它如何构建和链接?)

所以,我在 Apple 找到了 George Warner 的一些代码,其中包含 alutCreateBufferFromFile 和 alutLoadMemoryFromFile 的替换函数。虽然能够直接从大多数任何类型的音频文件创建 OpenAL 缓冲区,但该代码似乎只支持 8 位单声道声音文件。16 位立体声或单声道 44khz 文件会产生令人讨厌的嘶嘶声和削波。(文件没问题;Quicktime 可以正常播放。)

因此,我的问题是:有人可以指点我一些适用于 OpenAL 缓冲区的 .wav 加载代码/帮助 Cocoa/Carbon 吗?谢谢你。

4

3 回答 3

5

我相信你已经解决了这个问题,但是对于通过谷歌找到这个的人来说,这里有一些几乎没有经过测试的 WAV 加载代码。它可以工作,但你最好在使用真正的东西之前仔细检查内存泄漏等等。

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;
}
于 2011-01-01T02:22:47.167 回答
1

使用Audio ServicesAudioFileReadBytes中的功能。示例可以在Finch 声音引擎中找到,参见Sound+IO类别。

于 2009-07-08T18:50:12.393 回答
0

这可能是一个明显的建议,但由于您没有提及它:您是否按照Apple 的技术说明中的建议尝试了http://www.openal.org/上的库?

至于示例代码如何链接和构建,它不是找到原型(如果你打开 -Wall,你会得到一个隐式函数声明警告),而是 OpenAL.framework——至少在他们使用的 SDK 中示例项目——实际上导出 _alutLoadWAVFile,您可以使用 nm 检查。您得到的确切链接错误是什么,您使用的是什么 SDK?

于 2009-04-30T14:00:47.807 回答