如何使用Audio Unit 播放音頻

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

播放音頻流程圖

使用Audio Unit播放音頻的時(shí)候,我們使用一個(gè)I/O Unit就可以完成了,整體步驟和錄制時(shí)差不多,具體如下:

  1. 設(shè)置好AudioComponentDescription,確定我們使用的Audio Unit類型
  2. 獲取Audio Unit實(shí)例,我們有兩種獲取方式,通過AUGraph獲取,通過AudioComponent獲取。
  3. 設(shè)置Audio Unit的屬性,告訴系統(tǒng)我們需要使用Audio Unit的哪些功能以及需要采集什么樣的數(shù)據(jù)。
  4. 開始播放和停止播放。
  5. 從回調(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)目地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容