QEMU 中音频模拟如何工作
事情有點棘手,但這里有一個粗略的描述:
QEMUSoundCard:建模一個給定的模擬的聲卡
SWVoiceOut:建模一個來自 QEMUSoundCard 的音頻輸出
SWVoiceIn:建模一個來自 QEMUSoundCard 的音頻輸入
HWVoiceOut:建模一個主機端的音頻輸出(后端)
HWVoiceIn:建模一個主機端的音頻輸入(后端)
每個聲音在采樣大小,字節序,速率等方面都可以有自己的設置。
對于一個給定聲卡的模擬典型的做法如下:
1/ 創建一個 QEMUSoundCard 對象,然后用 AUD_register_card() 注冊它
2/ 對于每個模擬的輸出,調用 AUD_open_out() 創建一個 SWVoiceOut 對象
3/ 對于每個模擬的輸入,調用 AUD_open_in() 創建一個 SWVoiceIn 對象
注意你必須給 AUD_open_out() 和 AUD_open_in() 傳遞一個回調函數;后面有更多相關內容。
每個 SWVoiceOut 與一個 HWVoiceOut 關聯,每個 SWVoiceIn 與一個 HWVoiceIn 關聯。
然而你可以讓多個 SWVoiceOut 與相同的 HWVoiceOut 關聯(相同的事情也發生在 SWVoiceIn/HWVoiceIn 中)。
聲音播放細節
每個 HWVoiceOut 也有以下這些:
一個固定大小的立體聲采樣的循環緩沖區(用于立體聲)。其采樣的格式為 floats 或 int64_t(依賴于構建配置)。
一個 samples 字段給出了(固定的)立體聲緩沖區中采樣對的個數。
一個目標轉換函數,稱為 clip(),用于從立體聲緩沖區中讀取并寫入一個平臺特有的聲音緩沖區(比如,Windows 上 WinWave 管理的緩沖區)。
一個 rpos,它循環緩沖區的偏移,指明了從立體聲緩沖區中的什么位置讀取下一個采樣數據,以通過 clip 用于下一次轉換。
一個 run_out 方法,每次調用以告知輸出后端從立體聲緩沖區發送采樣數據到主機的聲卡/聲音服務。這個方法也應該修改 rpos 并返回 played 采樣的數據的個數。下面有關于這個過程的一個更詳細的描述。
一個 write 方法回調用于從一個 SWVoiceOut 寫入一個模擬的聲音采樣數據的緩沖區到立體聲緩沖區。當前所有后端簡單地調用通用函數 audio_pcm_sw_write() 來實現它。
根據 malc,音頻子系統的最初的作者,如果可以的話,這用于允許一個后端使用平臺特有的函數來做相同的事情。
(簡單地說,所有的輸入后端具有一個 read 方法,它簡單地調用 audio_pcm_sw_read。)
每個 SWVoiceOut 有以下這些:
一個 conv() 函數用于從模擬的聲卡讀取聲音采樣數據,并復制/混合它們到對應的 HWVoiceOut 的立體聲緩沖區。
一個 total_hw_samples_mixed 對應于已經被混合到目標 HWVoiceOut 立體聲緩沖區的采樣的個數(從 HWVoiceOut 的 rpos 偏移開始)。注意:這是 HWVoiceOut 立體聲緩沖區中的采樣的計數,而不是模擬的硬件聲音采樣,它們可能具有不同的屬性(頻率,大小,尾端)。
一個 ratio 值,它是目標 HWVoiceOut 的頻率與 SWVoiceOut 的頻率的比值,乘以 (1 << 32) 的一個 64 位的整數。
因此,如果 HWVoiceOut 的頻率為 44kHz,而 SWVoiceOut 的頻率為 11kHz,則 ratio 將是 (44/11*(1 << 32)) = 0x4_0000_0000。
一個當 SWVoiceOut 創建時模擬的硬件提供的回調。這個函數用于混合 SWVoiceOut 的采樣到目的 HWVoiceOut 立體聲緩沖區(它也必須執行頻率插值,音量調整,等等。。。)。
這個回調通常調用音頻子系統中的另一個輔助函數 (AUD_write()) 來從模擬的硬件采樣緩沖區混合/調整音量。
這是一個小的圖形,更好地解釋了它:
SWVoiceOut: 模擬的硬件聲音緩沖區:|| (通過 AUD_write() 混合,該函數在用戶提供的回到中調用,| 回調本身在每次音頻始終滴答時調用 ).|vHWVoiceOut: 立體聲采樣循環緩沖區|| (通過 HWVoiceOut 的 `clip` 函數發送,它在 `run_out` 方法中調用,| 也在每次音頻時鐘滴答時調用)|v后端特有的聲音緩沖區audio/audio.c 中的 audio_timer() 函數被周期性地調用,且被用作一個執行聲音緩沖區的傳輸和混合的脈沖。對于音頻輸出語音更具體的如下:
對于每個 HWVoiceOut,找到活躍的 SWVoiceOut 的個數,以及已經被寫入緩沖區的 total_hw_samples_mixed 的最小個數。我們將這個值稱為立體聲緩沖區中 “live” 采樣的數量。
如果 live 為 0,如果需要,調用每個活躍的 SWVoiceOut 的回調來填充立體聲緩沖區,然后退出。
否則,調用 HWVoiceOut 對象的 run_out 方法。這將改變 rpos 的值,并返回播放的采樣的個數。然后所有活躍的 SWVoiceOuts 的 total_hw_samples_mixed 減去 played,回調被調用以重新填充立體聲緩沖區。
重要的是要注意SWVoiceOut回調:
接收一個 free 參數,其是可以被發送給硬件立體聲緩沖區的立體聲聲音采樣的個數(在比率調整前,比如,不是 SWVoiceOut 模擬的硬件聲音緩沖區中聲音采樣的個數)。
必須調用 AUD_write(sw, buff, count),其中 buff 指向模擬的聲音采樣,且它們的 count,必須 <= free 參數。
AUD_write() 的實現將調用目標 HWVoiceOut 的 write 方法,這反過來調用函數 audio_pcm_sw_write(),其在混合轉換到目標立體聲緩沖區之前執行標準的 比率/音量 調整。它也增加 SWVoiceOut 的 total_hw_samples_mixed 值。
audio_pcm_sw_write() 返回已經被混合入立體聲緩沖區的聲音采樣的 字節 數,AUD_write() 也是如此。
因此,最后我們有偽代碼:
every sound timer ticks:for hw in list_HWVoiceOut:live = MIN([sw.total_hw_samples_mixed for sw in hw.list_SWVoiceOut ])if live > 0:played = hw.run_out(live)for sw in hw.list_SWVoiceOut:sw.total_hw_samples_mixed -= playedfor sw in hw.list_SWVoiceOut:free = hw.samples - sw.total_hw_samples_mixedif free > 0:sw.callback(sw, free)錄音詳情
情況類似但順序相反,比如,HWVoiceIn 在它的立體聲緩沖區中獲取聲音采樣數據,且 SWVoiceIn 對象必須盡快消費它們。
原文 $QEMU/android/docs/AUDIO.TXT
打賞
Done.
總結
以上是生活随笔為你收集整理的QEMU 中音频模拟如何工作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QEMU 网络虚拟化
- 下一篇: 如何预编译 Android 模拟器专用内