第一篇:alsa音频总结
Linux音频驱动总结
参考文章:http://blog.csdn.net/droidphone/
http://blog.chinaunix.net/uid/22917448.html
分析只列出部分重要代码,具体请参考linux3.0内核代码。
Alsa架构整体来说十分复杂,但对于驱动移植来说我们仅仅只需要关心ASOC就足够了。在学习asoc之前我们先了解一些专业术语:
ASoC currently supports the three main Digital Audio Interfaces(DAI)found on SoC controllers and portable audio CODECs today, namely AC97, I2S and PCM.ASoC现在支持如今的SoC控制器和便携音频解码器上的三个主要数字音频接口,即AC97,I2S,PCM(与pcm音频格式注意区分,前者是一种音频接口,后者是一种输入声卡的音频格式)。
AC97 AC97 ====
AC97 is a five wire interface commonly found on many PC sound cards.It is now also popular in many portable devices.This DAI has a reset line and time multiplexes its data on its SDATA_OUT(playback)and SDATA_IN(capture)lines.The bit clock(BCLK)is always driven by the CODEC(usually 12.288MHz)and the frame(FRAME)(usually 48kHz)is always driven by the controller.Each AC97 frame is 21uS long and is divided into 13 time slots.AC97是一种个人电脑声卡上常见的五线接口。现在在很多便携设备中也很流行。这个数字音频接口有一个复位线,分时在SDATA_OUT(回放)和SDATA_IN(捕获)线上传送数据。位时钟常由解码器驱动(通常是12.288MHz).帧时钟(通常48kHz)总是由控制器驱动。每个AC97帧21uS,并分为13个时间槽。
I2S I2S ===
I2S is a common 4 wire DAI used in HiFi, STB and portable devices.The Tx and Rx lines are used for audio transmission, whilst the bit clock(BCLK)and left/right clock(LRC)synchronise the link.I2S is flexible in that either the controller or CODEC can drive(master)the BCLK and LRC clock lines.Bit clock usually varies depending on the sample rate and the master system clock(SYSCLK).LRCLK is the same as the sample rate.A few devices support separate ADC and DAC LRCLKs, this allows for simultaneous capture and playback at different sample rates.I2S是一个4线数字音频接口,常用于HiFi,STB便携设备。Tx 和Rx信号线用于音频传输。而位时钟和左右时钟(LRC)用于同步链接。I2S具有灵活性,因为控制器和解码器都可以控制位时钟和左右时钟。位时钟因采样率和主系统时钟而有不同。LRCLK与采样率相同。少数设备支持独立的ADC和DAC的LRCLK。这使在不同采样率情况下同步捕获和回放成为可能。
I2S has several different operating modes:-I2S有几个不同的操作模式:
o I2SMSB is transmitted on transition of LRC.左对齐模式:MSB在LRC传送时传送。
o Right JustifiedMSB is transmitted on falling edge of first BCLK after FRAME/SYNC.模式A-MSB在FRAME/SYNC后第一个BCLK的下降沿传送。
o Mode Busing I2C, 3 Wire(SPI)or both APIs 3)Mixers and audio controls 4)Codec audio operations 1)解码器数字音频接口和PCM配置。
2)解码器控制IO-使用I2C,3总线(SPI)或两个都有。3)混音器和音频控制。4)解码器音频操作。
Optionally, codec drivers can also provide:-解码器驱动可以选择性提供:
5)DAPM description.6)DAPM event handler.7)DAC Digital mute control.5)动态音频电源管理描述。6)动态音频电源管理事件控制。7)数模转换数字消音控制。SoC DAI Drivers 板级DAI驱动 ===============
Each SoC DAI driver must provide the following features:-每个SoC DAI驱动都必须提供如下性能:
1)Digital audio interface(DAI)description 1)数字音频接口描述
2)Digital audio interface configuration 2)数字音频接口配置 3)PCM's description 3)PCM描述
4)SYSCLK configuration 4)系统时钟配置
5)Suspend and resume(optional)5)挂起和恢复(可选的)
以上由君子翻译,本人实在没办法比他描述的更好了,所以把重要的部分提取出来直接copy。在这里对君子表示由衷感谢,赋上君子注。君子注:
您现在所阅读的,是君子阅读Linux音频SoC驱动时,写下的文档译文。
君子写些译文,一方面是作为自己的笔记,帮助记忆,另一方面也希望能对他人有所帮助。如果您能于君子的译文中有所收获,则吾心甚慰
现在我们开始分析ASOC:
ASoC被分为Machine、Platform和Codec三大部分。其中的Machine驱动负责Platform和Codec之间的耦合和设备或板子特定的代码。
看起来挺复杂,其实需要我们做的事情并不多,大部分内核已经完成。下面我们分析哪些是我们需要自己做的:
codec驱动:负责音频解码。这部分代码完全无平台无关,设备原厂提供,我们只需要把它加进内核编译就好了。platform驱动:与处理器芯片相关,这部分代码在该芯片商用之前方案产商提供的demo板已完全确定了,也就是说我们只需要使用就可以了。
machine驱动:好了,到了最关键的地方了,machine驱动是耦合platform和codec驱动,同时与上层交互的代码。由于上层是标准的alsa架构,所以下层接口肯定要做了统一,所以我很负责的告诉你,这部分由machine本身的platform驱动和platform设备组成(请跟asoc的platform驱动区别),platform驱动内核帮我们完成了,所以你无须过多的关心你的驱动怎么跟上层alsa怎么衍接的问题,我们只需要注册一个machine的platform设备以及完成platform和codec耦合就ok
asoc的关系图如下:(以下适应于linux3.0。linux2.6会有所不同)
上图把asoc架构显示的淋漓尽致,如果你分析了asoc你就会发现上图描述的结构以及函数真的一个都跑不了。
Machie:
Machine platform device:(~/sound/soc/samsung/smdk_wm8994.c)
1.smdk_snd_device = platform_device_alloc(“soc-audio”,-1);2.if(!smdk_snd_device)3.return-ENOMEM;4.5.platform_set_drvdata(smdk_snd_device, &smdk);6.7.ret = platform_device_add(smdk_snd_device);
1.static struct snd_soc_dai_link smdk_dai[] = { 2.{ /* Primary DAI i/f */
3..name = “WM8994 AIF1”, 4..stream_name = “Pri_Dai”, 5..cpu_dai_name = “samsung-i2s.0”, 6..codec_dai_name = “wm8994-aif1”, 7..platform_name = “samsung-audio”, 8..codec_name = “wm8994-codec”, 9..init = smdk_wm8994_init_paiftx, 10..ops = &smdk_ops, 11.}, { /* Sec_Fifo Playback i/f */
12..name = “Sec_FIFO TX”, 13..stream_name = “Sec_Dai”, 14..cpu_dai_name = “samsung-i2s.4”, 15..codec_dai_name = “wm8994-aif1”, 16..platform_name = “samsung-audio”, 17..codec_name = “wm8994-codec”, 18..ops = &smdk_ops, 19.}, 20.};21.22.static struct snd_soc_card smdk = { 23..name = “SMDK-I2S”, 24..owner = THIS_MODULE, 25..dai_link = smdk_dai,26..num_links = ARRAY_SIZE(smdk_dai), 27.};
通过snd_soc_card结构,又引出了Machine驱动的另外两个个数据结构:
snd_soc_dai_link(实例:smdk_dai[])snd_soc_ops(实例:smdk_ops)
snd_soc_dai_link看名字就知道,很明显它是起耦合链接作用的。它指定了Platform、Codec、codec_dai、cpu_dai的名字,稍后Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai。
snd_soc_ops连接Platform和Codec的dai_link对应的ops操作函数,本例就是smdk_ops,它只实现了hw_params函数:smdk_hw_params。
到此为止,最主要的部分machine的平台设备注册我们完成了。
下面我们关注machine平台驱动部分(这部分内核不需要我们实现,但我们需要知道它是怎么工作的)
ASoC的platform_driver在以下文件中定义:sound/soc/soc-core.c。还是先从模块的入口看起:
[cpp] view plaincopy 1.static int __init snd_soc_init(void)2.{ 3.......4.return platform_driver_register(&soc_driver);5.}
soc_driver的定义如下:
[cpp] view plaincopy
1./* ASoC platform driver */
2.static struct platform_driver soc_driver = { 3..driver = {
4..name = “soc-audio”, //确保你注册machine平台设备和它保持一致 5..owner = THIS_MODULE, 6..pm = &soc_pm_ops, 7.},8..probe = soc_probe, 9..remove = soc_remove, 10.};
初始化入口soc_probe()
soc_probe函数本身很简单,它先从platform_device参数中取出snd_soc_card,然后调用snd_soc_register_card,通过snd_soc_register_card,为snd_soc_pcm_runtime数组申请内存,每一个dai_link对应snd_soc_pcm_runtime数组的一个单元,然后把snd_soc_card中的dai_link配置复制到相应的snd_soc_pcm_runtime中,最后,大部分的工作都在snd_soc_instantiate_card中实现,下面就看看snd_soc_instantiate_card做了些什么: 该函数首先利用card->instantiated来判断该卡是否已经实例化,如果已经实例化则直接返回,否则遍历每一对dai_link,进行codec、platform、dai的绑定工作,下只是代码的部分选节,详细的代码请直接参考完整的代码树。static int soc_probe(struct platform_device *pdev){
struct snd_soc_card *card = platform_get_drvdata(pdev);//别忘记了machine的platform_set_drvdata //取出snd_soc_card
.............ret = snd_soc_register_card(card);//注册............}
下面我们看snd_soc_register_card()函数:
int snd_soc_register_card(struct snd_soc_card *card){
。。。。
card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime)*
(card->num_links + card->num_aux_devs),GFP_KERNEL);//为snd_soc_pcm_runtime数组申请内存,每一个dai_link对应一个snd_soc_pcm_runtime数组单元。。。。
for(i = 0;i < card->num_links;i++)
card->rtd[i].dai_link = &card->dai_link[i];//把snd_soc_card中的dai_link复制到相应的snd_soc_pcm_runtime。。。。
snd_soc_instantiate_cards();//将调用snd_soc_instantiate_card()//最为重要 }
下面我们分析snd_soc_instantiate_card()函数:
static void snd_soc_instantiate_card(struct snd_soc_card *card){
。。。。
if(card->instantiated){ //判断该卡是否已经实例化,如果是就返回
mutex_unlock(&card->mutex);
return;
}
/* bind DAIs */
for(i = 0;i < card->num_links;i++)//否则遍例每一对dai_link,进行codec,flatrom,dai的绑定工作
soc_bind_dai_link(card, i);/* ******************************************************************************************************************************************************************
ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list,系统中所有的Codec、DAI、Platform都在注册时连接到这三个全局链表上。soc_bind_dai_link函数逐个扫描这三个链表,根据card->dai_link[]中的名称进行匹配,匹配后把相应的codec,dai和platform实例赋值到card->rtd[]中(snd_soc_pcm_runtime)。经过这个过程后,snd_soc_pcm_runtime:(card->rtd)中保存了本Machine中使用的Codec,DAI和Platform驱动的信息。
*****************************************************************************************************************************************************************/
/* bind completed ? */
if(card->num_rtd!= card->num_links){
mutex_unlock(&card->mutex);
return;
}
。。。。。
/* card bind complete so register a sound card */
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card)。。。。。
card->snd_card->dev = card->dev;
card->dapm.bias_level = SND_SOC_BIAS_OFF;
card->dapm.dev = card->dev;
card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list);//初始化codec缓存,创建声卡实例
。。。。。。。//下面是最重要的probe匹配工作
if(card->probe){
ret = card->probe(card);
if(ret < 0)
goto card_probe_error;
}
。。。。。
ret = soc_probe_dai_link(card, i, order);//主要的耦合链接工作在此函数完成。。。。。
ret = soc_probe_aux_dev(card, i)。。。。。
if(card->late_probe){//最后的声卡初始化工作,ret = card->late_probe(card);
}
。。。。。
ret = snd_card_register(card->snd_card);//然后调用标准的alsa驱动的声卡函数进行声卡注册
。。。。。
}
到这里声卡已经注册好了,声卡可以正常工作了,我们再分析最后一个函数,也就是它们是怎么匹配的 soc_probe_dai_link():
static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order){
。。。。。
/* probe the cpu_dai */
if(!cpu_dai->probed &&
cpu_dai->driver->probe_order == order){
if(!try_module_get(cpu_dai->dev->driver->owner))
return-ENODEV;
if(cpu_dai->driver->probe){
ret = cpu_dai->driver->probe(cpu_dai);
if(ret < 0){
printk(KERN_ERR “asoc: failed to probe CPU DAI %sn”, cpu_dai->name);
module_put(cpu_dai->dev->driver->owner);
return ret;
}
}
cpu_dai->probed = 1;
/* mark cpu_dai as probed and add to card dai list */
list_add(&cpu_dai->card_list, &card->dai_dev_list);
}
/* probe the CODEC */
if(!codec->probed &&
codec->driver->probe_order == order){
ret = soc_probe_codec(card, codec);
if(ret < 0)
return ret;
}
/* probe the platform */
if(!platform->probed &&
platform->driver->probe_order == order){
ret = soc_probe_platform(card, platform);
if(ret < 0)
return ret;
}
/* probe the CODEC DAI */
if(!codec_dai->probed && codec_dai->driver->probe_order == order){
if(codec_dai->driver->probe){
ret = codec_dai->driver->probe(codec_dai);
if(ret < 0){
printk(KERN_ERR “asoc: failed to probe CODEC DAI %sn”, codec_dai->name);
return ret;
}
}
/* mark codec_dai as probed and add to card dai list */
codec_dai->probed = 1;
list_add(&codec_dai->card_list, &card->dai_dev_list);
}
/* complete DAI probe during last probe */
if(order!= SND_SOC_COMP_ORDER_LAST)return 0;
。。。。。
/* create the pcm */
ret = soc_new_pcm(rtd, num);//如果上面都匹配成功将创建标准的alsa的pcm逻辑设备
。。。。。
}
好了,到此为止我们最主要的部分machine部分分析完成了。接着是codec驱动部分:
Codec简介
在移动设备中,Codec的作用可以归结为4种,分别是:
对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号
对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把模拟的声音信号转变CPU能够处理的数字信号
对音频通路进行控制,比如播放音乐,收听调频收音机,又或者接听电话时,音频信号在codec内的流通路线是不一样的
对音频信号做出相应的处理,例如音量控制,功率放大,EQ控制等等
ASoC对Codec的这些功能都定义好了一些列相应的接口,以方便地对Codec进行控制。ASoC对Codec驱动的一个基本要求是:驱动程序的代码必须要做到平台无关性,以方便同一个Codec的代码不经修改即可用在不同的平台上。以下的讨论基于wolfson的Codec芯片WM8994,kernel的版本3.3.x。
描述Codec的最主要的几个数据结构分别是:snd_soc_codec,snd_soc_codec_driver,snd_soc_dai,snd_soc_dai_driver,其中的snd_soc_dai和snd_soc_dai_driver在ASoC的Platform驱动中也会使用到,Platform和Codec的DAI通过snd_soc_dai_link结构,在Machine驱动中进行绑定连接。
Codec的注册
因为Codec驱动的代码要做到平台无关性,要使得Machine驱动能够使用该Codec,Codec驱动的首要任务就是确定snd_soc_codec和snd_soc_dai的实例,并把它们注册到系统中,注册后的codec和dai才能为Machine驱动所用。以WM8994为例,对应的代码位置:/sound/soc/codecs/wm8994.c,模块的入口函数注册了一个platform driver:
[html] view plaincopy
1.static struct platform_driver wm8994_codec_driver = { 2..driver = { 3..name = “wm8994-codec”, //注意machine device里面和这里保持一致 4..owner = THIS_MODULE, 5.}, 6..probe = wm8994_probe, 7..remove = __devexit_p(wm8994_remove), 8.};9.10.module_platform_driver(wm8994_codec_driver);有platform driver,必定会有相应的platform device,platform device其实在我们之前讲过的machine device注册时已经引入了。
[html] view plaincopy
1.static int __devinit wm8994_probe(struct platform_device *pdev)2.{
3.return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994, 4.wm8994_dai, ARRAY_SIZE(wm8994_dai));5.}
其中,soc_codec_dev_wm8994和wm8994_dai的定义如下(代码中定义了3个dai,这里只列出第一个):
[html] view plaincopy
1.static struct snd_soc_codec_driver soc_codec_dev_wm8994 = { 2..probe = wm8994_codec_probe, 3..remove = wm8994_codec_remove, 4..suspend = wm8994_suspend, 5..resume = wm8994_resume,6..set_bias_level = wm8994_set_bias_level, 7..reg_cache_size = WM8994_MAX_REGISTER, 8..volatile_register = wm8994_soc_volatile, 9.};
[html] view plaincopy
1.static struct snd_soc_dai_driver wm8994_dai[] = { 2.{
3..name = “wm8994-aif1”, 4..id = 1, 5..playback = {
6..stream_name = “AIF1 Playback”, 7..channels_min = 1, 8..channels_max = 2, 9..rates = WM8994_RATES, 10..formats = WM8994_FORMATS, 11.},12..capture = {
13..stream_name = “AIF1 Capture”, 14..channels_min = 1, 15..channels_max = 2, 16..rates = WM8994_RATES, 17..formats = WM8994_FORMATS, 18.},19..ops = &wm8994_aif1_dai_ops, 20.}, 21.......22.}
可见,Codec驱动的第一个步骤就是定义snd_soc_codec_driver和snd_soc_dai_driver的实例,然后调用snd_soc_register_codec函数对Codec进行注册。
snd_soc_register_codec()函数是machine driver提供的,只要注册成功后codec提供的操作函数就能正常提供给machine driver使用了。int snd_soc_register_codec(struct device *dev,const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai){ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL)。。。。。。
/* create CODEC component name */ codec->name = fmt_single_name(dev, &codec->id);/*Machine驱动定义的snd_soc_dai_link中会指定每个link的codec和dai的名字,进行匹配绑定时就是通过和这里的名字比较,从而找到该Codec的 */
// 然后初始化它的各个字段,多数字段的值来自上面定义的snd_soc_codec_driver的实例soc_codec_dev_wm8994: codec->write = codec_drv->write;codec->read = codec_drv->read;codec->volatile_register = codec_drv->volatile_register;codec->readable_register = codec_drv->readable_register;codec->writable_register = codec_drv->writable_register;codec->dapm.bias_level = SND_SOC_BIAS_OFF;codec->dapm.dev = dev;codec->dapm.codec = codec;codec->dapm.seq_notifier = codec_drv->seq_notifier;codec->dev = dev;codec->driver = codec_drv;codec->num_dai = num_dai;mutex_init(&codec->mutex);
/* allocate CODEC register cache */ if(codec_drv->reg_cache_size && codec_drv->reg_word_size){ reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;codec->reg_size = reg_size;/* it is necessary to make a copy of the default register cache
* because in the case of using a compression type that requires
* the default register cache to be marked as __devinitconst the
* kernel might have freed the array by the time we initialize
* the cache.*/ if(codec_drv->reg_cache_default){ codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,reg_size, GFP_KERNEL);if(!codec->reg_def_copy){ ret =-ENOMEM;goto fail;} } }
。。。。。。/* register any DAIs */ if(num_dai){ ret = snd_soc_register_dais(dev, dai_drv, num_dai);//通过snd_soc_register_dais函数对本Codec的dai进行注册 if(ret < 0)goto fail;}
mutex_lock(&client_mutex);list_add(&codec->list, &codec_list);/*最后,它把codec实例链接到全局链表codec_list中,并且调用snd_soc_instantiate_cards是函数触发Machine驱动进行一次匹配绑定操作 */
snd_soc_instantiate_cards();mutex_unlock(&client_mutex)。。。。。}
好了,在这里我们的codec驱动也分析完了,其实这部分都是与平台无关代码,一般也不需要改动,这部分我们从设备原厂拿到代码后丢上去就可以了,只是我们在写machine device的时候要注意和这里的名字匹配。接下来是asoc的platform驱动:
Platform驱动的主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DAI)把音频数据传送给Codec进行处理,最终由Codec输出驱动耳机或者是喇叭的音信信号。在具体实现上,ASoC有把Platform驱动分为两个部分:snd_soc_platform_driver和snd_soc_dai_driver。其中,platform_driver负责管理音频数据,把音频数据通过dma或其他操作传送至cpu dai中,dai_driver则主要完成cpu一侧的dai的参数配置,同时也会通过一定的途径把必要的dma等参数与snd_soc_platform_driver进行交互。
snd_soc_platform_driver的注册
通常,ASoC把snd_soc_platform_driver注册为一个系统的platform_driver,不要被这两个想像的术语所迷惑,前者只是针对ASoC子系统的,后者是来自Linux的设备驱动模型。我们要做的就是:
定义一个snd_soc_platform_driver结构的实例;
在platform_driver的probe回调中利用ASoC的API:snd_soc_register_platform()注册上面定义的实例;
实现snd_soc_platform_driver中的各个回调函数;
以kernel3.3中的/sound/soc/samsung/dma.c为例:
[cpp] view plaincopy
1.static struct snd_soc_platform_driver samsung_asoc_platform = { 2..ops = &dma_ops, 3..pcm_new = dma_new, 4..pcm_free = dma_free_dma_buffers, 5.};6.7.static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev)8.{ 9.return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);10.} 11.12.static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev)13.{ 14.snd_soc_unregister_platform(&pdev->dev);15.return 0;16.} 17.18.static struct platform_driver asoc_dma_driver = { 19..driver = { 20..name = “samsung-audio”, 21..owner = THIS_MODULE, 22.}, 23.24..probe = samsung_asoc_platform_probe, 25..remove = __devexit_p(samsung_asoc_platform_remove), 26.};27.28.module_platform_driver(asoc_dma_driver);snd_soc_register_platform()该函数用于注册一个snd_soc_platform,只有注册以后,它才可以被Machine驱动使用。它的代码已经清晰地表达了它的实现过程:
为snd_soc_platform实例申请内存;
从platform_device中获得它的名字,用于Machine驱动的匹配工作; 初始化snd_soc_platform的字段;
把snd_soc_platform实例连接到全局链表platform_list中;
调用snd_soc_instantiate_cards,触发声卡的machine、platform、codec、dai等的匹配工作;
cpu的snd_soc_dai driver驱动的注册
dai驱动通常对应cpu的一个或几个I2S/PCM接口,与snd_soc_platform一样,dai驱动也是实现为一个platform driver,实现一个dai驱动大致可以分为以下几个步骤:
定义一个snd_soc_dai_driver结构的实例;
在对应的platform_driver中的probe回调中通过API:snd_soc_register_dai或者snd_soc_register_dais,注册snd_soc_dai实例;
实现snd_soc_dai_driver结构中的probe、suspend等回调;
实现snd_soc_dai_driver结构中的snd_soc_dai_ops字段中的回调函数;
snd_soc_register_dai 这个函数在上一篇介绍codec驱动的博文中已有介绍
具体不再分析,这个驱动也不需要用户做任务修改,所以只要知道它的作用就已经够了。就像电话机一样,我们只要知道电话怎么打就够了,至于它怎么连接我们并不太需要关心。
第二篇:音频分类总结(算法综述)
总结音频分类的算法
刚开始对音频分割还有特征提取有些自己的想法,感觉应该能够分清楚,但是当开始查阅文献的时候,发现对他们两个的概念越来越模糊。很多时候他们是重叠的。后来我在一篇文献里找到这句话。觉得应该是这个道理:
音频数据的分类是一个模式识别的问题,它包括两个基本方面:特征选择和分类。
音频分割是在音频分类的基础上从音频流中提取出不同的音频类别,也就是说在时间轴上对音频流按类别进行划分。分类是分割的前提和基础。对音频流的准确分割是最终的目的。
于是我找了一下比较典型的分类算法
比较典型的音频分类算法包括最小距离方法、支持向量机、神经网络、决策树方法和隐马尔可夫模型方法等。
1.最小距离法。(典型的音频分类算法)最小距离分类法的优点是概念直观,方法简单,有利于建立多维空间分类方法的几何概念。在音频分类中应用的最小距离分类法有k近邻(k—Nearest Neighbor,简称K—NN)方法和最近特征线方法(Nearest Feature,简称NFL))等。
k近邻方法的思想是根据未知样本X最近邻的k个样本点的类别来确定X的类别。为此,需要计算X与所有样本x。的距离d(x,x。),并且从中选出最小的k个样本作为近邻样本集合KNN,计算其中所有属于类别Wj的距离之和,并且按照以下判别规则进行分类:C(x)argminC{W1,...,Wn}
d(x,xi),其中,C为类别集合由于k近邻方法利用了更多的样本信息确定它的类别,k取大一些有利于减少噪声的影响。但是由于k近邻方法中需要计算所有样本的距离,因此当样本数目非常大的时候,计算量就相当可观。取k=l时,k近邻方法就退化为最近邻方法。
最近特征线方法是从每一类的样本子空间中选取一些原型(Prototype)特征点,这些特征点的两两连线称为特征线(Feature Line),这些特征线的集合用来表示原先每一类的样本子空间。
设类C的原型特征点集合:,其中Nc为类C的原型特征点数目,则对应的特征线的数目为Sc,而类C的特征线集合
{|XicXjc|1i,jNc,ij} i≠jl构成类C的特征线空间,它是类C的特征子空间。—般所选取的原型特征点的数目比较少,因此特征线的数目也比较少。未知样本X与特征线XicXjc的距离定义为x在XicXjc上的投影距离,如图4所示,而X与类别C的距离为X与类C的特征线空间中的所有特征线的最短距离。
2.神经网络(Neural Network)。
在使用神经网络进行音频分类时,可以令输入层的节点与音频的特征向量相对应,而输出层的节点对应于类别Ci。,如图5所示。在训练时,通过对训练样本集中的样本进行反复学习来调节网络,从而使全局误差函数取得最小值。这样,就可以期望该网络能够对新输入的待分类样本T输出正确的分类Ci。
3.支持向量机(support Vector Machine,简称为SVM)。
支持向量机是Vapnik等人提出的以结构风险最小化原理(Stuctural Risk Minimization Principle)为基础的分类方法。该方法最初来自于对二值分类问题的处理,其机理是在样本空间中寻找—个将训练集中的正例和反例两类样本点分割开来的分类超平面,并取得最大边缘(正样本与负样本到超平面的最小距离),如图6所示。该方法根据核空间理论将低维的输入空间数据通过某种非线性函数(即核函数)映射到—个高维空间中,并且线性判决只需要在高维空间中进行内积运算,从而解决了线性不可分的分类问题。
根据不同的分类问题,可以选用不同的核函数,常用的核函数有三种:
① 项式核函数:
② 径向基核函数:
③ Sigmoid核函数:
SVM训练算法主要有三类:二次规划算法,分解算法,增量算法。
4.决策树方法
决策树是一种结构简单、搜索效率高的分类器。这类方法以信息论为基础,对大量的实例选择重要的特征建立决策树,如图7所示。
最优决策树的构造是一个NP完全(NPComepleteness)问题,其设计原则可以形式化地表示为
其中T为特定的决策树结构,F和d分别为分枝结,为在数据集合点的特征子集和决策规则,D为所有的训练数据,D上选取特征集合F和决策规则d训练得到的结构为T的决策树的分类错误的条件概率。因此,决策树的构造过程可以分为三个问题:选取合适的结构,为分枝结点选取合适的特征子集和决策规则。常用的决策树构造方法有非回溯的贪心(Greedy)算法和梯度上升算法。
5.隐马尔可夫模型(Hidden Markov Model,简HMM)方法。
隐马尔可夫模型(HMM)的音频分类性能较好,它的分类对象是语音(speech)、音乐(music)以及语音和音乐的混合(speech+music)共3类数据,根据极大似然准则判定它们的类别,最优分类精度可达90.28%。
HMM本质上是一种双重随机过程的有限状态自动机(stochastic finite-state automata),它具有刻画信号的时间统计特性的能力。双重随机过程是指满足Markov分布的状态转换Markov链以及每一状态的观察输出概率密度函数,共两个随机过程。HMM可以用3元组来表示:入;(A,B,),其中A是状态Si到Sj的转换概率矩阵,B是状态的观察输出概率密度,是状态的初始分布概率。
第三篇:高通音频调试总结
高通音频调试总结
----夏珊珊
之前会议电话项目我们设计的方案是:外部的codec内带音频处理dsp接6270模块工作。外部codec+6270与高通的codec+dsp工作方式大致相同。所以调节音频的工作原理可以以高通内部的原理来作依据。
在调节会议电话的时候我们遇到了一个很大的问题,底噪。我们在这个问题上纠结了很久。调节了mic的滤波电路,高通的AGC参数,TX,RX filter 参数,都没有明显的改善,后来我们把mic断开接地,发现tx端还是有很大的噪音,截取输入到高通的音频噪音比较明显,从而我们确定了这个噪音是由外部的codec所引入的。调整音频的时候分析噪音来源比较重要,这样相应调整各部分增益来使噪音源影响尽量减小。
对于噪音处理,发现不管使用高通的AGC压制噪音还是使用外部CODEC带的DSP处理噪音都对音质有很大的损伤。所以建议在调整音频之前先最大限度的保证结构和硬件设计的优化性,毕竟软件可以对数字噪音处理比较理想,但是对于模拟噪音就不是万能的了。具体对于噪音的处理后续会在文档中提到。
高通音频通道及调整
基本概念
回音:Near end 端不说话,far end说话了后经过上图的path,经过喇叭播放后在空中回荡,又被mic收回去,在far-end听到了自己的声音。
Echo path:从Echo Canceller出来,经过gain、a/d转换 到speaker 经外面的环境,然后又被mic收回,通过一系列的通道到Echo Canceller。
Acoustic echo path:从speaker 出来,在环境中回荡后再进mic
从上图可以看到:
如果TX进来的ECHO跟我们估测的ECHO相近,Ataptive filter相减TX进来的echo可以消除回音。
Ataptive filter:用于模拟echo。
PCD(Path Change Detect):当使用者在移动,acoustic echo path也会改变。SPDET:用于检测是far end speaker讲话或者near end speaker,防止near end speaker讲话的时候被抑制掉
理想的状态是TX进来的echo,跟我们估测echo相近,相减就为0,但是实际上不可能,所以需要一个DENS消除非线性的回音,我们选择0~4KHZ是因为这个范围的声音是人声范围。
调整顺序:
设置音量等级和AGC gain→EC gain和 limit→codec和mic的gain→Ec parameter。高通的default volume 基本上可以使用于各个普通的场合。
AGC gain 我们首先调整外围的gain,比如tx agc、txvolume,AGC处理噪音比较有效,但是会相应的牺牲tx端的音质及音量大小。如果这个噪音会随着Rx_Volume变化,在拔出手柄或者静音Rx_CODEC_GAIN(0x0000),噪音明显减弱,那么这个噪音是数字噪音,可以使用RxAGC减弱,具体的操作方法是:
设置Rx AGC工作在静态增益模式(compFlinkAIGFlag=0x0000); 减弱‘rx_agc_static_gain’为0dB(compFlinkStaticGain=0x2000); 增加‘rx_agc_exp_thres’ 到-40dBm0mu(expFlinkThreshold=0x1180).同样TX端的数字噪音也可以调整TX AGC 消除,调整的方式于RX AGC相同。在音频通路上,建议调整增益的地方是codectxgain 和txvolume,这样做的目的是防止送入codec处理的音频信号太大出现削顶失真,使EC无法很好的模拟回音并处理掉回音。所以我们尽量在EC处理完毕后对信号进行放大。
EC gain和limit 外围的gain调整完毕后调整EC block gain(input gain、output gain)在调整的时候,rx volume 是调整到最大处理,这样做为了避免rx 方向上声音太小,扬声器声音不够大,不易于测试回音。
Nlpp limit:当input太大的时候,rx收到的声音特别大声,但是spk不总是这么大声,这样使ECHO收到的东西太多失真,设置limit的话使突强的时候使进入EC的echo不要太多。AF limit:控制TX方向的,EC 无法收敛,或者收敛的速度太慢,收到的东西突强太多,这样使用limit 解决,用于限制突然大声的信号。
Codec及mic的gain 随后设置codec和mic的gain,文章开端曾提到若模块有噪音,噪音的来源必须找到,并相对于此来设置codec及mic的gain。我们的应用噪音是来自于codec芯片本身,所以对于mic增益的降低对噪音是没有益处的,因为噪音会随着ADC的放大而放大,衰减而减弱。Mic增益小,相对的ADCgain必须放大才能让tx端听到清晰的声音,这样反而把噪音放大了。所以为了让产生的噪音最小,我们尝试把mic的增益放大,ADC gain衰减来减弱噪音,达到比较好的效果。所以调整这部分的增益需要根据具体的情况,具体的模块相应调整。
Ec parameters 对回音来说,结构及材料也有很大的影响因素。我们在设计的时候必须要考虑到这些因素才能更好的实现音质效果。比如SPK与mic必须尽量的拉开距离;mic腔体不能太大,mic使用专门的泡膜包起来;机壳的材质最好使用吸引的材料,防止大声音播放的时候机壳震动影响mic等等,这些在前期的时候最好设计考虑到。
关于EC参数,高通有几组默认的回音参数,从Speaker phone 到bluetooth 几个等级。通常尝试的时候从普通的模式到aggressive尝试,ECHO canceller的肯定会伤害到double talk的能力,所以可以不用不压抑太多就不要压抑太多。如果尝试模式的参数没有echo,就选择压制的比较小的那组参数。总之是在Double talk 和echo canceller取得一个平衡。
细调
如果使用aggressive那组参数,echo还是没有消除,那么查找echo path delay Echo会随着echo path改变,echo path有长短。当echo path delay设置不好,会使echo收敛不好。
如果不知道要设置多少的话就先设置为0,然后慢慢向上调整。
调整进入AF的参数
调整进入AF的两个进入EC的input的大小,他们的大小关系必须在一定的范围,AF才能正常的收敛。
X[K]> Z[K],AF才能正常的收敛。从网路端送来的信号,ECHO是从环境处理后的声音,肯定是稍微有点小,但是如果经过codec处理后就可能比X大,那么就使用Inputgain降低,然后增大OutputGain。
EC已经收敛了,如果有非线性的echo无法消除,通过设置 DENS_tail_portion: DENS_tail_alpha: DENS_NL_atteu:
这几个参数设置越大,echo 消除能力越好,但是影响double talk 高通给出的参数适用于大部分的场合,只需要在默认参数的基础上微调就可以了。这些参数的调整如果使用工具调整就比较方便了。下面就讲讲音频调试工具。
音频调试工具
音频调试工具的比较(这个是引用了钟明同学的文档,他的高通文档讲解的比较清晰了,我对其引用补充下吧 O(^_^)O)
AT Command: 引用了6100的使用的AT命令作个简要的介绍。设置回音的ECHO命令AT+ECHO和AT+ECHO1可以设置回音的28个参数。
AT+CLVL: 音量级别设置 AT+RXVOL: RX端音量设置 AT+CMUT:静音设置 AT+CMIC: mic音量设置 AT+SIDET:侧边音设置
AT+ECHO:设置手持与免提模式下的回声各个参数 AT+ECHO1:设置蓝牙耳机与普通耳机的回声参数
QACT 需要导入正确的audio_cal.xml,通常这个文件在工程里带有 使用步骤
1.配置QPST,使使端口出现在active Phones tab。
如果设备没有连接上或者XML文件导入错误,在QACT v1.x的版本会弹出这个窗口。表示只能在PC上调整,而无法在线的把数据导入到模块。
导入正确的xml文件
如果连接成功,可以看到以下图片,选择“否”,也就是不把XML 中默认的结果导到模块里面去。(我们这里只是调试,不要导入.XML 中默认的值)
我们在里面会调整的比较多的是: 调整codec的gains
Graphical拉AGC 参数,从Data获取参数
拉TX,RX filter 曲线
选择对应的path,device,拉出曲线后可见右边的7个参数,对应于代码里voc_pcm_path_cal_type结构体中的tx_iir_filter。
QACT在线调试必须通话挂机后才生效。而且拉TX,RX filter无法模拟模块里原来的声音曲线,调节音质曲线个人比较倾向于使用Qfilt。
QFILT 使用音频分析仪器获取未处理的(TX/RX filter全部设置为0)频响曲线。把这个曲线数据保存为*.EXP格式。
之前在龙旗做测试的时候发现使用仪器获取曲线数据无法直接保存为.EXP格式,保存为.ASM格式,将保存的数据去掉100之前及4000之后的数据,加上固定的格式如下:
# 09-27-06 15:32:32.49 Hz dBPa/V 100 0.239521 105.83 0.174744 112 0.105024 118.322 0.0793721 125 0.0562545 132.288 0.0526554 140 0.0522274 149.666 0.0886258 160 0.144394 169.706 0.17004 180 0.128156 189.737 0.0954074 „„„„ 3768.29 0.286294 4000 FAIL 保存为.EXP格式,红色的是RX的首尾固定格式,Tx的首尾固定格式如下:
# 09-29-06 15:05:11.04 Hz dBV/Pa „„ FAIL 使用QFILT导入对应的RX或者TX数据,导入数据之前必须配置右边的相关设置。选择Test Mode,Test Class,Test Path及Filter Type 0.676438
导入文件后的初始化曲线,这个曲线跟使用仪器测出来的频响曲线一致。
通过调整滤波曲线后的图如下:绿色是调整后的曲线,黄色的是原始的曲线,红色的滤波器的调整曲线。我们调整曲线的目的是确保调整后曲线在两条白色的曲线之间,且比较平滑。
调整到合适的曲线则点击Get Cofficients 获取调整的参数
在实际测试的时候如果把这个参数写入程序然后编译下载效率太慢了,这个时候可以直接使用QDV把这些实时的数据写入到模块,在通话的过程中实时生效,使用测试仪器测试使用调整后的参数曲线是否能通过测试。
QDV QDV使用需要导入正确的rpt文件。这个文件可以跟高通提SR获取。
之前遇到使用了错误的rpt文件导致有些参数设置不正确,所以一定要确保使用正确的文件。
启动QDV,首先看到以下的界面:
MEMA , MEMB , MEMC , MEMI值一定要设置正确,这个值可以通过查看代码获取。设置完成后进入以下界面
它的工具条如下所示
选择导入.rpt文件。
选择完.rpt文件后 点击 打开一个Text view 界面,右击选择需要修改的参数。
选择new可以导入一个新的参数。
导入后如图,选中变量后点击
可以修改变量值。
调整EC block中参数,配置完成后,必须写ecParametersUpdated使回音参数生效。如下图,设定了Echo参数后需要设置ecParametersUpdated为FFFF使其生效,设置完成后它会自动跳变为0000.同样,对于TX filtr和RX filter也需要写一个load参数(txPcmFiltLoad和rxPcmFiltLoad)FFFF使写入的参数生效,同样这个参数生效后会自动跳回0000。
第四篇:音频部实训学习总结
今年9月份进入传媒实践中心的音频部,通过这一个月的实训学习,我学会了把我们在学校所学的理论知识,运用到客观实际中,实训,就是把自己所学到的理论知识有用武之地,只学不实践,那么所学的就等于零。理论应该与实践相结合,另一方面,实践可以为以后找工作打基础。通过这段时间的学习,我学到了一些在课堂上学不到的东西,因为环境不同,接触到的人与事不同,从中学到的东西自然就不一样。要学会从实践中学习,从学习中实践。而且我国的经济飞速发展,在拥有越来越多的机会多的是,也有了更多的挑战,对于人才的要求就会越来越高,我们不知要学好课堂所学到的知识,不断从学习中,实践中学习其他知识,不断从各方面武装自己,才能在竞争中突出自己,表现自己。
这一个月的工作过程使我受益很大,学习了关于声音的基础知识,如声音的传播,特性,频率范围等,还在学校的活动中认识了调音台的操作。这不仅让我开阔了眼界,最主要的是懂得了如何更好的为人处世。在短暂的实训过程中,我深深的感觉到自己所学的知识的肤浅和在实践运用中知识的匮乏,刚开始的一段时间里,对一些工作无从下手,茫然不知所措,这让我感到非常的难过,在学校总以为自己学的不错,一旦接触到时间,才发现自己知道的是多么少,这时才真正领悟到学无止境的含义。
第五篇:高通音频增益调试总结
高通音频调试总结
1、综述
该文档主要描述了手机打开免提通话的时候,如何解决固话端出现的啸音、噪音问题。
2、环境 项目:xxx 硬件平台:MSM7X27A 软件版本:android2.3.5, AMSS11452302
3、调试流程
(1)咨询高通FAE,明确哪些参数需要调整
FAE给出的建议是:针对啸音,调整codec_rx_gain、codec_tx_gain参数;针对杂音,调整rx_agc_static_gain、rx_agc_exp_thres、rx_agc_compr_thres、tx_agc_exp_thres、tx_agc_compr_thres参数;(2)使用QACT工具,对上述参数进行调试 QACT是高通提供的音频校准工具,可以使用该工具直接在线修改各类音频参数,调试十分方便(使用方法详见安装文件目录下的文档《80-VM407-1_E_Audio_Calibration_Tool_User_Guide.pdf》)。
使用该工具在线调试的基本思路是:适当降低增益(codec_rx_gain、codec_tx_gain),并调整AGC的门限值以及静态增益(rx_agc_exp_thres、rx_agc_compr_thres、tx_agc_exp_thres、tx_agc_compr_thres、rx_agc_static_gain参数),以达到消除啸音、噪音的目的。在线调试完成后,还可以用这个工具将调好的audio_cal.xml文件直接生成代码,具体也请参考上述文档。(3)修改代码 代码路径:modem_proc/multimedia/audio/vocoder/src/voccal.c 在结构体voc_pcm_on_chip_speaker_cal_umts_qrd中,分别修改各个参数,代码如下:
CAL_MEMORY voc_pcm_path_cal_type voc_pcm_on_chip_speaker_cal_umts_qrd = {
VOC_EC_VER_ECNS,/* ec_version */
VOC_EC_AEC,/* ec_mode */
VOC_NS_ON,/* ns_enable */
0x656e,/* tx_gain */ 0x1000,/* dtmf_tx_gain */ // codec_tx_gain由0x71cf修改为0x2328 0x2328, /* codec_tx_gain */ // codec_rx_gain由0xb460修改为0x1770
0x1770,/* codec_rx_gain */ 0x0000,/* codec_st_gain */ …… ……
#ifdef FEATURE_AUDIO_AGC /* agc_param */ /* rx_agc_static_gain由0x8000修改为0x4000,rx_agc_exp_thres由0x1b00修改为0xe42,rx_agc_compr_thres由0x2000修改为0x1f40,tx_agc_exp_thres由0xf86修改为0x09c4,tx_agc_compr_thres由0x1bde修改为0x1964 */ { 0x4000, 0x0000, 0xe42, 0xffb0, 0x1f40, 0xffff, 0x0000, 0x0000, 0x2000, 0x0000, 0x09c4, 0xffc0, 0x1964, 0xffff }, voc_cal_adv_agc_param,voc_cal_avc_param,#endif /* FEATURE_AUDIO_AGC */ …… …… };