是为了更好地控制音效。

OpenAL构成

由三个实体构成:

  • listener(听众)
  • source(声源)
  • buffer(缓存)

OpenAL与3D空间中的声音

采用3D笛卡尔坐标系,右手坐标系。

OpenAL API

OpenAL应用开发流程

开始、获得设备信息、获得环境信息、初始化缓冲区、初始化声源、将缓冲区和声源绑定、初始化听众、播放、释放内存、结束。

其中初始化缓存是最为麻烦的,其详细过程是:

开始、打开文件、获得实际音频文件大小、开辟音频内存数据空间、读取文件到内存中、关闭文件、从OpenAL中获得缓冲区id、内存中音频数据复制到缓冲区、清除内存中音频数据、结束。

实例:使用OpenAL播放音效

#import "ViewController.h"
#import <AudioToolbox/AudioFile.h>
#import <OpenAL/al.h>
#import <OpenAL/alc.h>

@interface ViewController ()

{
    ALCcontext *mContext;
    ALCdevice *mDevice;
    NSUInteger sourceID;
    NSUInteger bufferID;
    BOOL isPlaying;
    BOOL isLoop;
}

@property (weak, nonatomic) IBOutlet UIButton *btnPlay;

- (IBAction)play:(id)sender;
- (IBAction)switchLoop:(id)sender;

-(void)initOpenAL;
-(void)initBuffer;
-(AudioFileID)openAudioFile:(NSString *)fileName;
-(UInt32)audioFileSize:(AudioFileID)fileID;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    isPlaying = NO;
    [self initOpenAL];
    [self initBuffer];
    //从OpenAL中获得声源id
    alGenSources(1, &sourceID);
    //设置基本声源基本属性
    alSourcef(sourceID, AL_PITCH, 1.0f);//设置音高
    alSourcef(sourceID, AL_GAIN, 1.0f);//设置音量
    //将缓冲区和声源绑定
    alSourcei(sourceID, AL_BUFFER, bufferID);
}

-(void)initOpenAL
{
    //获得设备
    mDevice = alcOpenDevice(NULL);
    if (mDevice)
    {
        //使用设备创建一个环境对象
        mContext = alcCreateContext(mDevice, NULL);
        alcMakeContextCurrent(mContext);
    }
}

-(void)initBuffer
{
    //获得文件的完整路径
    NSString *fileName = [[NSBundle mainBundle] pathForResource:@"AlertChordStroke" ofType:@"wav"];
    //打开文件
    AudioFileID fileID = [self openAudioFile:fileName];
    //获得实际音频文件大小
    UInt32 fileSize = [self audioFileSize:fileID];
    //开辟音频内存数据空间
    unsigned char *outData = malloc(fileSize);
    //读取文件到内存中
    OSStatus result = noErr;
    result = AudioFileReadBytes(fileID, false, 0, &fileSize, outData);
    //关闭文件
    AudioFileClose(fileID);
    //返回结果为0说明成功
    if (result != 0)
    {
        NSLog(@"cannot load effect: %@", fileName);
    }
    //从OpenAL中获得缓冲区id
    alGenBuffers(1, &bufferID);
    //从内存中音频数据复制到缓冲区
    alBufferData(bufferID, AL_FORMAT_STEREO16, outData, fileSize, 44100);
    //清除内存中音频数据
    if (outData)
    {
        free(outData);
        outData = NULL;
    }
}

-(AudioFileID)openAudioFile:(NSString *)fileName
{
    AudioFileID outAFID;
    NSURL *afUrl = [NSURL fileURLWithPath:fileName];
    OSStatus result = AudioFileOpenURL((__bridge CFURLRef)afUrl, kAudioFileReadPermission, 0, &outAFID);
    if (result != 0)
    {
        NSLog(@"cannot open file : %@", fileName);
    }
    return outAFID;
}

-(UInt32)audioFileSize:(AudioFileID)fileID
{
    UInt64 outDataSize = 0;
    UInt32 thePropSize = sizeof(UInt64);
    OSStatus result = AudioFileGetProperty(fileID, kAudioFilePropertyAudioDataByteCount, &thePropSize, &outDataSize);
    if (result != 0)
    {
        NSLog(@"cannot find file size");
    }
    return (UInt32)outDataSize;
}

- (IBAction)switchLoop:(id)sender
{
    UISwitch *sw = sender;
    isLoop = sw.on;
    if (isLoop)
    {
        alSourcei(sourceID, AL_LOOPING, AL_TRUE);
    }
    else
    {
        alSourcei(sourceID, AL_LOOPING, AL_FALSE);
        alSourceStop(sourceID);
        isPlaying = NO;
        [_btnPlay setTitle:@"播放" forState:UIControlStateNormal];
    }
}

- (IBAction)play:(id)sender
{
    if (!isPlaying)
    {
        alSourcePlay(sourceID);
        if (isLoop)
        {
            isPlaying = YES;
            [_btnPlay setTitle:@"停止" forState:UIControlStateNormal];
        }
    }
    else
    {
        alSourceStop(sourceID);
        isPlaying = NO;
        [_btnPlay setTitle:@"播放" forState:UIControlStateNormal];
    }
}

@end

 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐