连载目录
1.qemu声卡模拟
2.qemu声卡模拟原理-声卡初始化(pa+ac97)
3.qemu声卡模拟原理-声音播放(pa+ac97)
PCM数据
PCM数据格式这里抛砖引玉贴一点别人写的: PCM数据格式介绍更多详细的解释请参考度娘
PCM全称Pulse-Code Modulation,翻译一下是脉冲调制编码。
其实大可以不用关心英文释义,之所以这么命名是因为一些历史原因。
在音视频中,PCM是一种用数字表示采样模拟信号的方法。
要将一段音频模拟信号转换为数字表示,包含如下三个步骤:
Sampling(采样)
Quantization(量化)
Coding(编码)
#### PCM数据常用量化指标
采样率(Sample rate):每秒钟采样多少次,以Hz为单位。
位深度(Bit-depth):表示用多少个二进制位来描述采样数据,一般为16bit。
字节序:表示音频PCM数据存储的字节序是大端存储(big-endian)还是小端存储(little-endian),为了数据处理效率的高效,通常为小端存储。
声道数(channel number):当前PCM文件中包含的声道数,是单声道(mono)、双声道(stereo)?此外还有5.1声道等。
采样数据是否有符号(Sign):要表达的就是字面上的意思,需要注意的是,使用有符号的采样数据不能用无符号的方式播放。
以FFmpeg中常见的PCM数据格式s16le为例:它描述的是有符号16位小端PCM数据。
s表示有符号,16表示位深,le表示小端存储。
PCM采样到编码的过程如下:
qemu播放声音这个部分其实就是围绕PCM数据的转换和播放完成的。
之前声卡初始化说道了SWVoiceOut、HWVoiceOut两个结构体,在讲解声音播放之前有必要先介绍一下这两个对象。
QEMUSoundCard:建模一个给定的模拟的声卡
SWVoiceOut:建模一个来自 QEMUSoundCard 的音频输出
HWVoiceOut:建模一个主机端的音频输出(后端)
虚拟声卡的启动
虚拟声卡由虚拟机内部通过设备驱动启动,主要流程如下:
ac97.c:nabm_write->
ac97.c:nabm_writeb->
ac97.c:voice_set_active->
audio.c:AUD_set_active_out->
audio.c:audio_reset_timer
最后audio_reset_timer将启动一个定时器,不停的处理虚拟机发送出来的声音
虚拟声卡数据处理流程
声卡处理声音数据从定时器调用这里开始,主要流程如下:
audio.c:audio_timer->
audio.c:audio_run->
audio.c:audio_run_out->
audio.c:sw->callback.fn
ac97.c:po_callback->
ac97.c:transfer_audio->
ac97.c:write_audio->
ac97.c:pci_dma_read->
audio.c:AUD_write->
audio.c:audio_pcm_sw_write->
audio.c:st_rate_flow_mix
rate_template.h:NAME
audio.c:audio_pcm_hw_run_out
audio.c:hw->pcm_ops->put_buffer_out
ac97.c:qpa_get_buffer_out
ac97.c:qpa_put_buffer_out
以上就是一次完整的声音处理流程,最先是定时器发起处理一次声音,从虚拟机内部读取音频数据,然后交给后端处理。下面详细看一下整个过程。
从虚拟机内部读取音频数据
ac97.c:po_callback到ac97.c:write_audio开始读取虚拟机内部的数据,首先判断虚拟机是否有音频数据,picb为需要读取的数据量,picb由驱动写入,具体逻辑请自行参考ac97芯片手册。然后做数据转换和音量调整
在后面通过一个循环不停的读取并转换数据,直到虚拟机内部的音频数据全部读取出来。pci_dma_read实际上就是根据地址从guest的内存中读取数据,AUD_write完成数据的转换,下面将详细描述数据转换流程。
转换音频数据
音频数据转换首先从audio_pcm_sw_write函数开始,先通过虚拟设备注册的sw->conv做初步转换。
sw的conv函数在audio_template.h中注册的
最后sw->conv = mixeng_conv[1][1][0][1] = conv_natural_int16_t_to_stereo
conv_natural_int16_t_to_stereo在mixeng_template.h中定义实现
这里的音频转换主要是完成PCM格式的转换,比如有些声卡使用浮点格式,有些使用整形格式,最后转换到一个统一的格式处理。具体转换逻辑不做详细分析,请参考芯片手册。
我们来看下音量大小控制,这个逻辑比较通用也比较简单
这里直接将音量大小乘以PCM音频数据便完成了音量的转换。
最后st_rate_flow_mix函数完成PCM的码率转换,源码在rate_template.h中。具体的算法就不详细分析了,比较简单。
播放音频数据
音频数据转换完成后回到audio_run_out函数后面的audio_pcm_hw_run_out开始正真的播放音频数据。
最后通过pa的音频api pa_stream_write播放声音。