天天看點

iOS 通過 Audio Unit 播放音頻資料

作者:音視訊開發T哥

1、關于AudioUnit

Audio Unit 是iOS系統音頻架構的最底層了,這一層架構是最接近硬體層的,也是開發者目前能操作最的層的API架構了。

iOS 通過 Audio Unit 播放音頻資料

Audio Unit

這裡先解釋一下DSP(digital signal processing)數字信号處理,音頻信号是需要通過裝置采樣之後變成的數字信号,以友善資料的傳輸和記錄。目前最常用的是PCM格式的音頻資料信号,因為這種高保真的信号友善後續的處理,還有就是它保留了資料的完整性。

2、關于音頻流參數

1、采樣率 每秒鐘采得聲音樣本的次數,聲音是一種能量波,有振幅和頻率,人的耳朵可以聽到的頻率在20-Hz~20kHz之間的聲波,是以采樣率越高,擷取的到的頻率資訊就更為豐富,由于人耳的分辨率很有限,太高的頻率并不能分辨出來。22050 的采樣頻率是常用的,44100已是CD音質,超過48000或96000的采樣對人耳已經沒有意義。

常用的采樣率:

8000 Hz - 電話所用采樣率
22050 Hz - 無線電廣播所用采樣率
32000 Hz - miniDV 數位視訊 camcorder、DAT (LP mode)所用采樣率
44100 Hz - 音頻 CD, 也常用于 MPEG-1 音頻(VCD,SVCD,MP3)所用采樣率
47250 Hz - 商用 PCM 錄音機所用采樣率
48000 Hz - miniDV、數字電視、DVD、DAT、電影和專業音頻所用的數字聲音所用采樣率
50000 Hz - 商用數字錄音機所用采樣率
96000 Hz或者 192000 Hz - DVD-Audio、一些 LPCM DVD 音軌、BD-ROM(藍CD光牒)音軌、和 HD-DVD (高清晰度 DVD)音軌所用所用采樣率           

2、采樣位數 采樣位數,他是衡量聲音播到變化的一個參數,它的數值越大,分辨率就越高,錄制和回放的聲音就越接近真實。常見的聲霸卡主要有8位和16位兩種,如今市面上所有的主流産品都是16位及以上的聲霸卡。

每個采樣資料記錄的是振幅, 采樣精度取決于采樣位數的大小:
1 位元組(也就是8bit) 隻能記錄 256 個數, 也就是隻能将振幅劃分成 256 個等級;
2 位元組(也就是16bit) 可以細到 65536 個數, 這已是 CD 标準了;
4 位元組(也就是32bit) 能把振幅細分到 4294967296 個等級, 實在是沒必要了.           

3、通道數 即聲音的通道的數目,目前使用較多的是單聲道和立體聲,相當于從多位置采集聲音。 4、比特率 每秒的傳輸速率(位速, 也叫比特率)。如705.6kbps 或 705600bps, 其中的 b 是 bit, ps 是每秒的意思,表示每秒705600bit的容量。不同的音頻格式編碼,對PCM都有一個壓縮比,是以比特率一般等于原始比特率/音頻壓縮比。 5、幀長 幀長記錄了一個聲音單元位元組為機關,其長度為:樣本長度 * 通道數 = 幀長 6、幀數 每秒資料分為都少幀: 幀長 * 幀數 * 8 = 比特率

3、Audio Unit 工作時的腦圖和流程圖

iOS 通過 Audio Unit 播放音頻資料

AudioUnit 涉及到的知識圖.png

整體的一個流程圖如下:

iOS 通過 Audio Unit 播放音頻資料

Audio Unit Flow.png

C++音視訊學習資料免費擷取方法:關注音視訊開發T哥,點選「連結」即可免費擷取2023年最新C++音視訊開發進階獨家免費學習大禮包!

4、使用流程

1、iOS 涉及音頻使用和會話都需要使用到 AudioSessionInitialize去初始化音頻會話對象。

// set audio session
    NSError *error = nil;
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
    [audioSession setActive:YES error:&error];           

2、配置音頻元件Audio Unit 并描述輸出的單元

//set audio component information
    AudioComponentDescription audioDesc;
    audioDesc.componentType = kAudioUnitType_Output;
    audioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
    audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
    audioDesc.componentFlags = 0;
    audioDesc.componentFlagsMask = 0;           

3、查找、建立對應的音頻輸出單元元件

AudioUnit audioUnit;
//set audio component information
    AudioComponentDescription audioDesc;
    audioDesc.componentType = kAudioUnitType_Output;
    audioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
    audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
    audioDesc.componentFlags = 0;
    audioDesc.componentFlagsMask = 0;

    //Finds the next component that matches a specified AudioComponentDescription structure after a specified audio component.
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &audioDesc);
    //create a new instance of an audio component
    AudioComponentInstanceNew(inputComponent, &audioUnit);
 //audio property
    UInt32 flag = 1;
    if (flag) {
        status = AudioUnitSetProperty(audioUnit,
                                      kAudioOutputUnitProperty_EnableIO,
                                      kAudioUnitScope_Output,
                                      OUTPUT_BUS,
                                      &flag,
                                      sizeof(flag));
    }           

4、配置對應需要播放的音頻資料格式内容

// format
    AudioStreamBasicDescription outputFormat;
    memset(&outputFormat, 0, sizeof(outputFormat));
    outputFormat.mSampleRate       = 44100; // 采樣率
    outputFormat.mFormatID         = kAudioFormatLinearPCM; // PCM格式
    outputFormat.mFormatFlags      = kLinearPCMFormatFlagIsSignedInteger; // 整形
    outputFormat.mFramesPerPacket  = 1; // 每幀隻有1個packet
    outputFormat.mChannelsPerFrame = 1; // 聲道數
    outputFormat.mBytesPerFrame    = 2; // 每幀隻有2個byte 聲道*位深*Packet數
    outputFormat.mBytesPerPacket   = 2; // 每個Packet隻有2個byte
    outputFormat.mBitsPerChannel   = 16; // 位深
    [self printAudioStreamBasicDescription:outputFormat];

    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Input,
                                  OUTPUT_BUS,
                                  &outputFormat,
                                  sizeof(outputFormat));
    if (status) {
        NSLog(@"AudioUnitSetProperty eror with status:%d", status);
    }           

5、指定播放源的相關資訊

// callback
    AURenderCallbackStruct playCallback;
    playCallback.inputProc = PlayCallback;
    playCallback.inputProcRefCon = (__bridge void *)self;
    AudioUnitSetProperty(audioUnit,
                         kAudioUnitProperty_SetRenderCallback,
                         kAudioUnitScope_Input,
                         OUTPUT_BUS,
                         &playCallback,
                         sizeof(playCallback));
    
    
    OSStatus result = AudioUnitInitialize(audioUnit);           

6、給目标播放元件輸入播放内容

static OSStatus PlayCallback(void *inRefCon,
                             AudioUnitRenderActionFlags *ioActionFlags,
                             const AudioTimeStamp *inTimeStamp,
                             UInt32 inBusNumber,
                             UInt32 inNumberFrames,
                             AudioBufferList *ioData) {
    yourPlayerClass *player = (__bridge yourPlayerClass *)inRefCon;//這裡擷取之前初始化時配置給播放元件的類對象
   // 這裡是靜音資料,需要播放更對内容可以往ioData->mBuffers輸入資料
  for (int iBuffer = 0; iBuffer < ioData->mNumberBuffers; ++iBuffer) {
        memset(ioData->mBuffers[iBuffer].mData, 0, ioData->mBuffers[iBuffer].mDataByteSize);
    }
   return noErr;
  }
}           

7、這裡想要補充說明一下 上面的配置和初始中,多次用到了AudioUnitSetProperty,這是一個設定音頻單元屬性的函數,它的幾個參數如下:

iOS 通過 Audio Unit 播放音頻資料

參數補充.png

上面用到了兩個宏定義 作用于設定AudioUnit的I/O口設定

#define INPUT_BUS 1
#define OUTPUT_BUS 0           

原文連結:iOS 通過 Audio Unit 播放音頻資料 - 簡書

繼續閱讀