【Android 高性能音频】AAudio 音频流 PCM 采样 的 采样 缓冲 播放 的 连续机制 ( 数据回调机制 | 数据回调函数指针 | 实现数据回调函数 | 设置数据回调函数 )
文章目錄
- I . AAudio 音頻流 采樣 緩沖 播放 的連續(xù)機(jī)制
- II . AAudio 音頻流 數(shù)據(jù)回調(diào)函數(shù) 函數(shù)指針類型定義
- III . AAudio 音頻流 數(shù)據(jù)回調(diào)函數(shù) 實(shí)現(xiàn)
- IV . AAudio 音頻流 數(shù)據(jù)回調(diào)函數(shù) 設(shè)置
I . AAudio 音頻流 采樣 緩沖 播放 的連續(xù)機(jī)制
1 . AAudio 音頻流的 采樣 緩沖 播放 流程 : 樣本采樣完成后 , 存入緩沖區(qū) , 然后將其通過(guò) AAudio 播放出來(lái) , 采樣階段采集 nnn 個(gè)樣本 , 然后將其放入緩沖區(qū) , 將緩沖區(qū)的數(shù)據(jù) 寫(xiě)出到 AAudio 音頻流中播放出來(lái) ;
2 . 采樣速度高于播放速度 : 如果采樣采集多了 , 不能立刻播放 , 此時(shí)就會(huì)產(chǎn)生延遲 , 并且如果超出緩沖區(qū)大小 , 超出部分采樣就會(huì)溢出 , 造成數(shù)據(jù)損失 , 樣本不連續(xù) , 就會(huì)產(chǎn)生電流 ;
3 . 采樣速度低于播放速度 : 如果采樣少了 , 不能向 AAudio 音頻流中寫(xiě)入足夠的數(shù)據(jù) , 就會(huì)造成電流雜音等情況 ;
4 . 數(shù)據(jù)回調(diào)函數(shù) 引入 : 數(shù)據(jù)回調(diào)函數(shù)就是為了解決上述問(wèn)題 , 引入的機(jī)制 ;
5 . 數(shù)據(jù)回調(diào)函數(shù) 簡(jiǎn)介 :
- ① 采樣緩沖 : 采樣后 , 將采集的樣本存入緩沖區(qū) ;
- ② 播放采樣 : 將緩沖區(qū)中的樣本寫(xiě)入 AAudio 音頻流 ;
- ③ 調(diào)用回調(diào)函數(shù) : AAudio 音頻流如果播放完當(dāng)前數(shù)據(jù) , AAudio 就會(huì)自動(dòng)調(diào)用 開(kāi)發(fā)者按照 規(guī)范開(kāi)發(fā)的 回調(diào)函數(shù) 申請(qǐng)后續(xù)采樣數(shù)據(jù) ;
- ④ 回調(diào)函數(shù)內(nèi)容 : 開(kāi)發(fā)者自己實(shí)現(xiàn)該回調(diào)函數(shù) , 在這個(gè)函數(shù)中實(shí)現(xiàn)采樣 并將采樣設(shè)置給 AAudio 音頻流 , 之后繼續(xù)播放音頻采樣 ;
之后如果采樣播放完畢 , 繼續(xù)調(diào)用回調(diào)函數(shù) ;
下面會(huì)著重講解該數(shù)據(jù)回調(diào)函數(shù)的細(xì)節(jié)
II . AAudio 音頻流 數(shù)據(jù)回調(diào)函數(shù) 函數(shù)指針類型定義
數(shù)據(jù)回調(diào)函數(shù)原型 : AAudio 只定義了一個(gè)函數(shù)類型 , 該函數(shù)的實(shí)際內(nèi)容需要開(kāi)發(fā)者自己開(kāi)發(fā) , 一般是 采樣 , 然后 設(shè)置數(shù)據(jù)給 AAudio 音頻流 的操作 ;
typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(AAudioStream *stream,void *userData,void *audioData,int32_t numFrames);- ① 函數(shù)類型 : 該數(shù)據(jù)回調(diào)函數(shù)類型如下 , 其返回值是 aaudio_data_callback_result_t 類型 , 四個(gè)參數(shù)分別如下 ;
- ② 函數(shù)別名 : 使用 typedef 關(guān)鍵字為 上述函數(shù)指針類型 賦予一個(gè)類型別名 AAudioStream_dataCallback ;
- ③ 參數(shù)一 AAudioStream *stream : AAudio 音頻流指針 ;
- ④ 參數(shù)二 void *userData : 該參數(shù)用于傳遞一些額外數(shù)據(jù) , 與 AAudioStreamBuilder_setCallback() 中的 第三參數(shù) void *userData 參數(shù)指向的地址一致 ;
- ⑤ 參數(shù)三 void *audioData : 指向音頻采樣數(shù)據(jù)的指針 , AAudio 會(huì)自動(dòng)將該數(shù)據(jù)輸出或輸入到音頻流中 ;
- ⑥ 參數(shù)四 int32_t numFrames : 要處理的幀數(shù) , 需要將多少幀的 audioData 指針指向的音頻采樣數(shù)據(jù) , 輸入 或 輸出到 AAudio 音頻流中 ;
III . AAudio 音頻流 數(shù)據(jù)回調(diào)函數(shù) 實(shí)現(xiàn)
aaudio_data_callback_result_t (*)(AAudioStream *stream,void *userData,void *audioData,int32_t numFrames)
1 . 數(shù)據(jù)回調(diào)函數(shù)設(shè)置給 AAudio 音頻流 : AAudio 中通過(guò) AAudioStreamBuilder_setDataCallback() 函數(shù) , 將用戶自己實(shí)現(xiàn)的 AAudioStream_dataCallback 回調(diào)函數(shù)的函數(shù)指針設(shè)置給 AAudio 音頻流 , 當(dāng) AAudio 音頻流需要數(shù)據(jù)時(shí)會(huì)自動(dòng)回調(diào)該函數(shù) ;
2 . 輸出流回調(diào)函數(shù)實(shí)現(xiàn)內(nèi)容 : 在該函數(shù)中需要 按照 AAudio 音頻流的當(dāng)前數(shù)據(jù)格式 ( 通道數(shù)/每幀樣本數(shù) , 采樣率 ) , 采集 numFrames 幀的 PCM 音頻樣本數(shù)據(jù) ( 每幀的采樣數(shù)與通道數(shù)一致 ) , 將這些樣本數(shù)據(jù)寫(xiě)出到 void *audioData 指針指向的內(nèi)存中 , 之后這些數(shù)據(jù)會(huì)被自動(dòng)輸出到 AAudio 音頻流中 ;
3 . 輸入流回調(diào)函數(shù)實(shí)現(xiàn)內(nèi)容 : 在函數(shù)中需要從 void *audioData 指針指向的內(nèi)存中 , 讀取 numFrames 幀 ( 每幀的采樣數(shù)與通道數(shù)一致 ) 的采樣數(shù)據(jù) , 注意需要按照當(dāng)前的 采樣格式 ( 通道數(shù)/每幀樣本數(shù) , 采樣率 ) 計(jì)算讀取的字節(jié)大小 ;
4 . 采樣數(shù)據(jù)自動(dòng)傳輸 ( 不需要手動(dòng)干預(yù) ) : 在回調(diào)函數(shù)中 , 將 numFrames 幀的數(shù)據(jù)傳遞給 void *audioData , AAudio 在該回調(diào)函數(shù)執(zhí)行完畢后 , 會(huì)自動(dòng)將這些數(shù)據(jù) 讀/寫(xiě) 到 AAudio 音頻流中 , 不需要 開(kāi)發(fā)者 手動(dòng)調(diào)用 AAudioStream_read() 或 AAudioStream_write() 方法 , 一調(diào)用必出錯(cuò) ;
5 . 每次讀寫(xiě)的幀數(shù) int32_t numFrames :
- ① 固定幀數(shù) : 通過(guò)調(diào)用 AAudioStreamBuilder_setFramesPerDataCallback() 方法可以設(shè)置每次回調(diào)都讀寫(xiě)固定幀數(shù)的音頻采樣 ;
- ② 自由幀數(shù) : 如果用戶沒(méi)有指定幀數(shù) , 那么在每次回調(diào)函數(shù)中的 numFrames 幀數(shù)可以由用戶自己設(shè)置 ;
6 . 不能執(zhí)行耗時(shí)操作 : 在該回調(diào)函數(shù)中 , 不能執(zhí)行太耗時(shí)的操作 或 阻塞操作 , 如果阻塞時(shí)間超過(guò)了采樣播放的時(shí)間 , 就會(huì)造成后續(xù)采樣無(wú)法及時(shí) 讀取 或 寫(xiě)入 到 AAudio 音頻流中 , 出現(xiàn)音頻故障 ;
7 . 回調(diào)函數(shù)中不能執(zhí)行的操作 : 該回調(diào)函數(shù)的回調(diào)頻率很高 , 可能達(dá)到每秒幾百到幾千次 , 因此有很多 耗時(shí)操作 或 訪問(wèn)本地資源 的邏輯不能再該函數(shù)中運(yùn)行 , 盡可能只對(duì)內(nèi)存數(shù)據(jù)進(jìn)行操作 ;
- ① 內(nèi)存操作 : 使用 malloc() 或 new 分配堆內(nèi)存 , 極大可能造成內(nèi)存泄漏或內(nèi)存溢出 ;
- ② 文件操作 : 打開(kāi) open , 關(guān)閉 close , 讀取 read , 寫(xiě)出 write 等針對(duì)文件的操作 ;
- ③ 網(wǎng)絡(luò)操作 : 訪問(wèn)網(wǎng)絡(luò)操作 , 從網(wǎng)絡(luò)中讀取數(shù)據(jù) , 或向遠(yuǎn)程端口發(fā)送數(shù)據(jù) ;
- ④ 同步線程 : 線程間的同步操作會(huì)造成阻塞 ;
- ⑤ 休眠阻塞 : sleep 方法不能執(zhí)行 , 會(huì)造成阻塞 ;
- ⑥ 關(guān)音頻流 : 停止 或 關(guān)閉 流操作 會(huì)造成不可預(yù)知故障 ;
- ⑦ 讀寫(xiě)操作 : 該函數(shù)中不用刻意調(diào)用 AAudioStream_read() 和 AAudioStream_write() 方法進(jìn)行讀寫(xiě)操作 ;
8 . 回調(diào)函數(shù)中可以進(jìn)行的操作 :
- ① 調(diào)用 AAudioStream_getXXX() 類方法 : 如下圖中列舉的方法可以直接調(diào)用 , 獲取 AAudio 音頻流的各種屬性 ;
- ② 調(diào)用 AAudio_convertResultToText() 方法 : 將返回值轉(zhuǎn)為對(duì)應(yīng)的 ASCII 字符 ;
9 . 非阻塞技術(shù) : 如果需要在回調(diào)函數(shù)中 讀取 或 輸出 數(shù)據(jù) , 建議使用非阻塞技術(shù) , 如 FIFO 技術(shù) ;
IV . AAudio 音頻流 數(shù)據(jù)回調(diào)函數(shù) 設(shè)置
1 . 數(shù)據(jù)回調(diào)函數(shù)設(shè)置方法 :
- ① 函數(shù)原型 : 該方法用于設(shè)置 AAudio 音頻流回調(diào)函數(shù) , 當(dāng) AAudio 需要 讀取 / 寫(xiě)出數(shù)據(jù)時(shí) , 會(huì)自動(dòng)回調(diào)該 AAudioStream_dataCallback 類型 函數(shù) ;
- ② 參數(shù) 一 AAudioStreamBuilder *builder : AAudio 音頻流指針 ;
- ③ 參數(shù) 二 AAudioStream_dataCallback callback : AAudioStream_dataCallback 是函數(shù)指針類型 , 開(kāi)發(fā)者自己實(shí)現(xiàn)該函數(shù) , 然后將函數(shù)的地址當(dāng)做參數(shù)設(shè)置到此處 ;
- ④ 參數(shù) 三 void *userData : 該指針 與 AAudioStream_dataCallback 函數(shù)指針類型的第二個(gè)參數(shù)指向的地址是相同的 , 相當(dāng)于傳入一個(gè)用戶自定義的指針 , 可以是任意類型任意數(shù)據(jù)任意變量 ;
2 . 音頻數(shù)據(jù)傳遞方式 : 在 AAudioStream_dataCallback 函數(shù)指針類型的回調(diào)函數(shù)中 , 音頻流的數(shù)據(jù)不是通過(guò) AAudioStream_read 或 AAudioStream_write 方法進(jìn)行讀寫(xiě)的 , 只要將音頻采樣數(shù)據(jù)設(shè)置給函數(shù)的第三個(gè)參數(shù) audioData 指針即可 , 第四個(gè)參數(shù) numFrames 用于說(shuō)明讀寫(xiě)數(shù)據(jù)的幀數(shù) , 每幀的采樣數(shù) 就是 采樣的通道數(shù) ;
3 . 數(shù)據(jù)回調(diào)函數(shù)工作機(jī)制 :
- ① 第一次回調(diào) : 在 AAudio 音頻流調(diào)用 AAudioStream_requestStart() 方法后 , 會(huì)立刻回調(diào)該數(shù)據(jù)回調(diào)函數(shù) , 然后第一次 讀寫(xiě)采樣數(shù)據(jù)到 AAudio 音頻流中 ;
- ② 循環(huán)回調(diào) : 當(dāng) AAudio 音頻流 讀取或?qū)懗鰯?shù)據(jù)完畢后 , 會(huì)自動(dòng)回調(diào)該數(shù)據(jù)回調(diào)函數(shù) , 在回調(diào)函數(shù)中準(zhǔn)備下一次的采樣 , 讀寫(xiě)到 AAudio 音頻流中 , 之后繼續(xù)循環(huán) , 直到 AAudio 音頻流關(guān)閉銷毀 ;
- ③ 實(shí)時(shí)線程 : AAudio 擁有一個(gè)實(shí)時(shí)線程 , 該數(shù)據(jù)回調(diào)函數(shù)就是運(yùn)行在這個(gè)線程上的 ;
總結(jié)
以上是生活随笔為你收集整理的【Android 高性能音频】AAudio 音频流 PCM 采样 的 采样 缓冲 播放 的 连续机制 ( 数据回调机制 | 数据回调函数指针 | 实现数据回调函数 | 设置数据回调函数 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Android 高性能音频】AAudi
- 下一篇: 【Android 高性能音频】AAudi