qemu声卡模拟原理-声音播放(pa+ac97)

连载目录
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播放声音。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值