是为了更好地控制音效。
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