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
用于下一次转换。
|<----------------- samples ----------------------->|| || rpos |||_______v___________________________________________|| | || | ||_______|___________________________________________|
一个
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 立体声缓冲区中的采样的计数,而不是模拟的硬件声音采样,它们可能具有不同的属性(频率,大小,尾端)。
______________| || SWVoiceOut2 ||______________|______________ || | || SWVoiceOut1 | | thsm<N> := total_hw_samples_mixed|______________| | for SWVoiceOut<N>| || ||<-----|------------thsm2-->|| | ||<---thsm1-------->| |_______|__________________v________|_______________ | |111111111111111111| v || |222222222222222222222222222| ||_______|___________________________________________|^| HWVoiceOut stereo bufferrpos
一个
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中音频模拟工作过程(十)
QEMUSoundCard:建模一个给定的模拟的声卡 SWVoiceOut:建模一个来自 QEMUSoundCard 的音频输出 SWVoiceIn:建模一个来自 QEMUSoundCard 的音频输 ...
- 计算机io工作方式,QEMU/KVM和VirtIO工作模式
https://blog.csdn.net/shengxia1999/article/details/52244119 KVM:Kernel-Based Virtual Machine 基于内核的虚拟 ...
- 一步步教你如何在Ubuntu虚拟机中安装QEMU并模拟模拟arm 开发环境(一)uImage u-boot
初次接触qemu是因为工作的需要,有时候下了班,可能需要在家研究一些东西,因为博主用到arm环境,这时候博主比较小气,不愿花钱买开发板,当然博主在这里给大家的建议是,如果要真正学懂arm构架的相关知识 ...
- 一步步教你:如何用Qemu来模拟ARM系统
这是道哥的第011篇原创 文章目录 前言 为什么需要ARM模拟系统 应用程序的开发 系统开发(BSP) Qemu是什么? Qemu的两种模式 Qemu 能做什么?或者说适合做什么? 在 Ubuntu1 ...
- qemu声卡模拟原理-基本使用方法
连载目录 1.qemu声卡模拟原理-基本使用方法 2.qemu声卡模拟原理-声卡初始化(pa+ac97) 3.qemu声卡模拟原理-声音播放(pa+ac97) qemu版本:5.2.50 qemu声卡 ...
- qemu声卡模拟原理-声卡初始化(pa+ac97)
连载目录 1.qemu声卡模拟 2.qemu声卡模拟原理-声卡初始化(pa+ac97) 3.qemu声卡模拟原理-声音播放(pa+ac97) qemu的整个声卡虚拟化都是围绕着audio.c这个文件展 ...
- qemu声卡模拟原理-声音播放(pa+ac97)
连载目录 1.qemu声卡模拟 2.qemu声卡模拟原理-声卡初始化(pa+ac97) 3.qemu声卡模拟原理-声音播放(pa+ac97) PCM数据 PCM数据格式这里抛砖引玉贴一点别人写的: P ...
- 使用 QEMU/KVM 模拟 ARM64
使用 QEMU/KVM 模拟 ARM64 下载arm64专用BIOS,用于启动 EFI (可扩展固件接口) 内核 https://releases.linaro.org/components/kern ...
- 在Win10下用Qemu模拟器模拟或虛拟树莓派,包括4B
在Win10下用Qemu模拟器模拟或虛拟树莓派,包括4B 为什么要用Qemu模拟树莓派,因为模拟出来后,可以带着到处走,U盘一拷,有电脑的地方就可以调试.调试出了问题,想重新来过,只要把之前的副本复制 ...
最新文章
- python模块学习---HTMLParser(解析HTML文档元素)
- ASP.NET2.0中的ClientScriptManager 类用法—如何添加客户端事件
- 深度学习100例 | 第51天-目标检测算法(YOLOv5)(入门篇)
- [学习windows/记录篇]安装TMG防火墙(三向外围)
- vconsole插件_移动端调试面板插件vconsole
- 如何在数据表中存取图片 - 回复 三足乌 的问题
- jQuery的ajax提交表单
- 微信灰度测试“相关阅读”功能 公众号文章下推荐延伸内容
- /etc/cron.deny 中关闭某一用户的 crontab
- Linux搭建SVN服务器
- Extended Kalman Filter vs. Error State Kalman Filter for Aircraft Attitude Estimation 翻译
- 维护设备的库存信息-SERIAL_EQBS_POST
- 坚持写博客,以身说法——我与CSDN的1234
- 你知道索引失效嘛?举例看看?
- 波卡Polkadot
- python音频特征提取_Python干货:如何使用Python对音频进行特征提取?
- java 多线程 售票_Java 多线程 之 火车站售票实例
- UIPATH 收发Outlook邮件
- 红队外围打点中CMS识别重点漏洞整理(一)
- 网站 服务器 和 空间大小,网站空间多大合适
热门文章
- 导入os模块执行文件和目录管理操作
- Spring+ActiveMQ+Mysql 配置JMS
- Java生成pgp密钥对_在Javascript中生成PGP密钥对,并使用加密的PGP私钥对文本进行签名...
- java 不能反序列化_java中的序列化与反序列化
- lambda 表达式中的 this 与普通情况下的 this 指向
- Altium Designer(AD18)常用操作和快捷方式
- Asp.net MVC Filter解析和实例说明
- linux中Tomcat 8 文件上传后无访问权限
- (三十)java多线程一
- RHEL5.4在线调整磁盘分区大小