alsa sample rate跟踪 <1>

alsa sample rate跟踪 <1>alsasamplerate跟踪本计划全部放在一篇中,后来发现太长。因此截取成四篇,一口气看800多行,确实够烦的!之前以为alsalib中的rateplugin之所以被调用,是因为在asound.conf中指定了硬件的samplerate,例如rate48000。但实际测试下来,发现不是这么回事。无论asound.conf中是否有rate48000,驱动中得到

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

alsa sample rate跟踪 <1>

本计划全部放在一篇中,后来发现太长。
因此截取成四篇,一口气看800多行,确实够烦的!

之前以为alsa lib中的rate plugin之所以被调用,是因为在asound.conf中指定了硬件的sample rate,例如rate 48000。
但实际测试下来,发现不是这么回事。
无论asound.conf中是否有rate 48000,驱动中得到的params中的rate都是48000。

这是什么原因呢?一时想不明白。
先把rate经过的路线画出来吧。
rate包含在params中,那就顺着params看吧。
我们直接接触的是声卡驱动,params相关的函数就是XXXX_hw_params了。
在函数XXXX_hw_params中把sample rate打印出来,发现已经是48000了。
通过dump_stack把kernel中的调用堆栈打印出来,如下:

[   65.259240] [<8003bd9c>] (unwind_backtrace+0x0/0xf8) from [<7f312248>] (imx_3stack_surround_hw_params+0x38/0x4c8 [snd_soc_imx_saf7741])
[   65.274646] [<7f312248>] (imx_3stack_surround_hw_params+0x38/0x4c8 [snd_soc_imx_saf7741]) from [<7f1c3d50>] (soc_pcm_hw_params+0x4c/0x1a8 [snd_soc_core])
[   65.291480] [<7f1c3d50>] (soc_pcm_hw_params+0x4c/0x1a8 [snd_soc_core]) from [<7f116e6c>] (snd_pcm_common_ioctl1+0x844/0xea0 [snd_pcm])
[   65.304804] [<7f116e6c>] (snd_pcm_common_ioctl1+0x844/0xea0 [snd_pcm]) from [<7f1178f0>] (snd_pcm_playback_ioctl1+0x40/0x3e0 [snd_pcm])
[   65.317841] [<7f1178f0>] (snd_pcm_playback_ioctl1+0x40/0x3e0 [snd_pcm]) from [<800f0000>] (do_vfs_ioctl+0x80/0x54c)
[   65.328629] [<800f0000>] (do_vfs_ioctl+0x80/0x54c) from [<800f0504>] (sys_ioctl+0x38/0x5c)
[   65.337306] [<800f0504>] (sys_ioctl+0x38/0x5c) from [<800350c0>] (ret_fast_syscall+0x0/0x30)

 

我们只关心params相关。
snd_pcm_common_ioctl1的部分内容:

static int snd_pcm_common_ioctl1(struct file *file,
     struct snd_pcm_substream *substream,
     unsigned int cmd, void __user *arg)
{
...
 case SNDRV_PCM_IOCTL_HW_PARAMS:
  return snd_pcm_hw_params_user(substream, arg);
...
}

 

函数snd_pcm_hw_params_user的实现:

static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
      struct snd_pcm_hw_params __user * _params)
{
 struct snd_pcm_hw_params *params;
 int err;

 params = memdup_user(_params, sizeof(*params));
 if (IS_ERR(params))
  return PTR_ERR(params);

 err = snd_pcm_hw_params(substream, params);
 if (copy_to_user(_params, params, sizeof(*params))) {
  if (!err)
   err = -EFAULT;
 }

 kfree(params);
 return err;
}

 

在kernel中被传来传去的params原来就诞生在snd_pcm_hw_params_user函数中。
结合函数snd_pcm_common_ioctl1可知,user层调用了一个ioctl,command为SNDRV_PCM_IOCTL_HW_PARAMS,并将params作为arg参数传给内核。
内核中将arg重新copy到params并使用。

内核中的代码还是比较清晰的。
阅读上面dump_stack的调用堆栈可知,内核中将params传递给函数XXXX_hw_params之前,并没有修改params。
也就是说XXXX_hw_params中的sample rate为48000,并不是在内核中产生的。
那就继续往上追踪,也就是alsa lib了。

alsa lib中的代码不是那么清晰。
当时,不是说代码写的不好(呵呵,能实现这些东东的,可都是大牛),是因为alsa lib中要处理很多种情况,并且要解析asound.conf文件,
根据音频文件参数,硬件参数,以及alsa配置文件(asound.conf是其中之一)来决定需要进行什么样的处理。
呵呵,说到底还是没能对alsa lib完全理解,所以它在面前仍然是一个庞然大物,没有庖丁解牛的感觉。
一时半会也不能把alsa lib全部搞透,暂时也没时间详细研究,先只关系params吧。

在alsa lib中搜索SNDRV_PCM_IOCTL_HW_PARAMS,发现其出现的地方:

static inline int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params)
{
 /* check for new hw_params structure; it's available from 2.0.2 version of PCM API */
 if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= pcm_hw->version)
  return ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params);
 return use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params);
}

 

上溯:

static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
 snd_pcm_hw_t *hw = pcm->private_data;
 int err;
 if (hw_params_call(hw, params) < 0) {
  err = -errno;
  SYSMSG("SNDRV_PCM_IOCTL_HW_PARAMS failed (%i)", err);
  return err;
 }
 params->info &= ~0xf0000000;
 params->info |= (pcm->monotonic ? SND_PCM_INFO_MONOTONIC : 0);
 err = sync_ptr(hw, 0);
 if (err < 0)
  return err;
 if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
  snd_pcm_set_appl_ptr(pcm, &hw->mmap_control->appl_ptr, hw->fd,
         SNDRV_PCM_MMAP_OFFSET_CONTROL);
 }
 return 0;
}

 

原来是在snd_pcm_hw_hw_params中发起的ioctl。
snd_pcm_hw_hw_params赋值给了结构体snd_pcm_hw_ops:

static const snd_pcm_ops_t snd_pcm_hw_ops = {
...
 .hw_params = snd_pcm_hw_hw_params,
...
}

看代码最怕这种,给函数都带上相同的面具,让你摸不清谁是谁。
揭开它们面具最好的方法就是让它们都报上名来,呵呵。
在所有的hw_params函数中各加一句log,把各自函数名打印出来,顺便把进入各函数时的sample rate也打印出来,以方便我们查询sample rate究竟是在哪儿被改变的。
结合kernel中代码,可知根据params取得sample rate的方式为:
(&params->intervals[SNDRV_PCM_HW_PARAM_RATE – SNDRV_PCM_HW_PARAM_FIRST_INTERVAL])->min
通过打印log,发现hw_params被调用的信息如下:

snd_pcm_hw_params - rate=48000
_snd_pcm_hw_params - rate=48000
snd_pcm_hw_hw_params - rate=48000
snd_pcm_hw_params - rate=16000
_snd_pcm_hw_params - rate=16000
snd_pcm_plug_hw_params - rate=16000
_snd_pcm_hw_params - rate=16000
snd_pcm_rate_hw_params - rate=16000
snd_pcm_generic_hw_params - rate=48000
_snd_pcm_hw_params - rate=48000
snd_pcm_direct_hw_params - rate=48000

 

函数snd_pcm_hw_params的实现:

/** \brief Install one PCM hardware configuration chosen from a configuration space and #snd_pcm_prepare it
 * \param pcm PCM handle
 * \param params Configuration space definition container
 * \return 0 on success otherwise a negative error code
 *
 * The configuration is chosen fixing single parameters in this order:
 * first access, first format, first subformat, min channels, min rate, 
 * min period time, max buffer size, min tick time
 *
 * After this call, #snd_pcm_prepare() is called automatically and
 * the stream is brought to \c #SND_PCM_STATE_PREPARED state.
 *
 * The hardware parameters cannot be changed when the stream is
 * running (active). The software parameters can be changed
 * at any time.
 */
int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
 int err;
 assert(pcm && params);
 err = _snd_pcm_hw_params(pcm, params);
 if (err < 0)
  return err;
 err = snd_pcm_prepare(pcm);
 return err;
}

snd_pcm_hw_params与_snd_pcm_hw_params调用关系以及明了了。
接着要看看:
1、snd_pcm_hw_hw_params,snd_pcm_plug_hw_params,snd_pcm_rate_hw_params,snd_pcm_generic_hw_params和snd_pcm_direct_hw_params是如何被调用的。
2、以及snd_pcm_hw_params和几处单独的_snd_pcm_hw_params是如何被调用的。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/181181.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 19种电压转换的电路设计方式

    19种电压转换的电路设计方式标准三端线性稳压器的压差通常是2.0-3.0V。要把5V可靠地转换为3.3V,就不能使用它们。压差为几百个毫伏的低压降(LowDropout,LDO)稳压器,是此类应用的理想选择。图1-1是基本LDO系统的框图,标注了相应的电流。从图中可以看出,LDO由四个主要部分组成:技巧一:使用LDO稳压器,5V向3.3V系统供电标准三端线性稳压器的压差通常是2.0-3.0V。要把5V可靠地转换为3.3V,就不能使用它们。压差为几百个毫伏的低压降(LowD…

    2022年6月2日
    35
  • debian 开机启动脚本_ubuntu 自启动脚本

    debian 开机启动脚本_ubuntu 自启动脚本在debian6中,加入开机启动脚本的方法与debian5不同了,直接做符号链接到runlevel已经不起作用了,提示缺少LSB信息,并且用insserv来替代update-rc.d:root@14:/etc/rc2.d#update-rc.d-n php_fastcgi.sh defaults           update-rc.d:usingdependencybas

    2022年10月19日
    0
  • 手把手教你学DSP:基于TMS320C55x[通俗易懂]

    手把手教你学DSP:基于TMS320C55x[通俗易懂]书名:手把手教你学DSP:基于TMS320C55x作者:陈泰红副书名:出版日期:2011年8月1日出版社:北京航空航天大学出版社页数:348    《手把手教你学DSP:基于TMS320C55x》是以TMS320C55x系列高性能低功耗DSP为主,主要介绍了以数字信号处理器(DSP)为核心的实时数字信号

    2022年4月29日
    41
  • 浏览器安装为知笔记插件(chrome浏览器)

    浏览器安装为知笔记插件(chrome浏览器)1.下载并安装驱动 wiz.cn/zh-cn/downloads-webclipper.html2.我选择的谷歌浏览器,因为不好翻墙,这里选择手动安装3.下载会得到一个crx文件。注意这里按照官方文档去添加到扩展程序会失败。我们要把这个文件后缀名改为.zip形式并解压,得到文件夹。注意:是直接将.crx改成.zip!!不是自己压缩成的4.接着,打开谷歌的“更多工具”…

    2022年8月18日
    35
  • 计算机逻辑与或非运算规则_与 或 非 三种逻辑符号计算机

    计算机逻辑与或非运算规则_与 或 非 三种逻辑符号计算机与&&或||非!请举例子说明C语言中的与、或、非是C语言的逻辑运算符。1、逻辑与在C语言中逻辑与用&&表示。举例:a&&b(其中a、b都代表一个条件)如果a和b都为真,则结果为真,如果a和.或非与非或与非都说下或:+与:*非:取反三个事件a,b,c或非就是先a+b,再取反与非就是先a*b,再取反&按位与|按位或~按位非^…

    2022年10月11日
    0
  • Windows Server 2016 NTP服务端和客户端配置[通俗易懂]

    Windows Server 2016 NTP服务端和客户端配置[通俗易懂]目标:A、B两台WindowsServer2016系统的服务器。B要做为NTP服务器使用,而只有A可以连到互联网。所以先将A作为NTP服务端,再将B作为既是服务端又是客户端。其它设备同B服务器同步时间。服务端:1.微软键+R键,进入“运行”,输入“regedit”,进入注册表2.HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSe…

    2022年7月15日
    21

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号