Audio Unit 實(shí)現(xiàn)音頻播放功能

播放音頻流程圖
使用Audio Unit播放音頻的時(shí)候,我們使用一個(gè)I/O Unit就可以完成了,整體步驟和錄制時(shí)差不多,具體如下:
- 設(shè)置好AudioComponentDescription,確定我們使用的Audio Unit類型
- 獲取Audio Unit實(shí)例,我們有兩種獲取方式,通過AUGraph獲取,通過AudioComponent獲取。
- 設(shè)置Audio Unit的屬性,告訴系統(tǒng)我們需要使用Audio Unit的哪些功能以及需要采集什么樣的數(shù)據(jù)。
- 開始播放和停止播放。
- 從回調(diào)函數(shù)中將音頻數(shù)據(jù)傳給播放器。
初始化
- (instancetype)initWithAsbd:(AudioStreamBasicDescription)asbd {
self = [super init];
if (self) {
_asbd = asbd;
_queue = dispatch_queue_create("zf.audioPlayer", DISPATCH_QUEUE_SERIAL);
[self setupDescription];
[self getAudioUnits];
[self setupAudioUnits];
}
return self;
}
設(shè)置AudioComponentDescription
- (void)setupAcd {
_ioUnitDesc.componentType = kAudioUnitType_Output;
//vpio模式
_ioUnitDesc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
_ioUnitDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
_ioUnitDesc.componentFlags = 0;
_ioUnitDesc.componentFlagsMask = 0;
}
獲取Audio Unit實(shí)例
通過AUGraph獲取實(shí)例
- (void)getAudioUnits {
OSStatus status = NewAUGraph(&_graph);
printf("create graph %d \n", (int)status);
AUNode ioNode;
status = AUGraphAddNode(_graph, &_ioUnitDesc, &ioNode);
printf("add ioNote %d \n", (int)status);
//instantiate the audio units
status = AUGraphOpen(_graph);
printf("open graph %d \n", (int)status);
//obtain references to the audio unit instances
status = AUGraphNodeInfo(_graph, ioNode, NULL, &_ioUnit);
printf("get ioUnit %d \n", (int)status);
}
通過AudioComponent獲取實(shí)例
- (void)createInputUnit {
AudioComponent comp = AudioComponentFindNext(NULL, &_ioUnitDesc);
if (comp == NULL) {
printf("can't get AudioComponent");
}
OSStatus status = AudioComponentInstanceNew(comp, &(_ioUnit));
printf("creat audio unit %d \n", (int)status);
}
設(shè)置Audio Unit屬性
- (void)setupAudioUnits {
OSStatus status;
//設(shè)置io輸入格式
status = AudioUnitSetProperty(_ioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&_asbd,
sizeof(_asbd));
CheckError(status, "set ioUnit StreamFormat");
NSTimeInterval bufferDuration = kSampleTime;
NSError *error;
[[AVAudioSession sharedInstance] setPreferredIOBufferDuration:bufferDuration error:&error];
//設(shè)置輸入回調(diào)
AURenderCallbackStruct rcbs;
rcbs.inputProc = &InputRenderCallback;
rcbs.inputProcRefCon = (__bridge void *_Nullable)(self);
status = AudioUnitSetProperty(_ioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&rcbs,
sizeof(rcbs));
CheckError(status, "set render callback");
}
開始播放
注釋的部分是不使用AUGraph的方式。
- (void)startRecord {
dispatch_async(_queue, ^{
OSStatus status;
// status = AudioUnitInitialize(self.ioUnit);
// printf("AudioUnitInitialize %d \n", (int)status);
// status = AudioOutputUnitStart(self.ioUnit);
// printf("AudioOutputUnitStart %d \n", (int)status);
status = AUGraphInitialize(self.graph);
printf("AUGraphInitialize %d \n", (int)status);
status = AUGraphStart(self.graph);
printf("AUGraphStart %d \n", (int)status);
});
}
停止播放
- (void)stopRecord {
dispatch_async(_queue, ^{
OSStatus status;
status = AUGraphStop(self.graph);
printf("AUGraphStop %d \n", (int)status);
});
}
回調(diào)中AURenderCallback填充數(shù)據(jù)
static OSStatus InputRenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
ZFAudioUnitPlayer *player = (__bridge ZFAudioUnitPlayer *)inRefCon;
[player.dataSource readDataToBuffer:ioData length:inNumberFrames];
return noErr;
}
dataSource,從其他地方獲取數(shù)據(jù)。這里是從文件中讀取的數(shù)據(jù),使用的是ExtAudioFile相關(guān)的API。
ExtAudioFile的使用-簡(jiǎn)書地址
完整代碼請(qǐng)到我的Github中下載-項(xiàng)目地址