带有 SNDRV_PCM_IOCTL_WRITEI_FRAMES 的 ALSA 断管

Posted

技术标签:

【中文标题】带有 SNDRV_PCM_IOCTL_WRITEI_FRAMES 的 ALSA 断管【英文标题】:ALSA Broken Pipe with SNDRV_PCM_IOCTL_WRITEI_FRAMES 【发布时间】:2021-11-19 04:29:24 【问题描述】:

我正在使用 alsa 内核。 我的配置:

SNDRV_PCM_HW_PARAM_ACCESS = SNDRV_PCM_ACCESS_RW_INTERLEAVED
SNDRV_PCM_HW_PARAM_FORMAT = SNDRV_PCM_FORMAT_S16_LE
SNDRV_PCM_HW_PARAM_SUBFORMAT = SNDRV_PCM_SUBFORMAT_STD
SNDRV_PCM_HW_PARAM_CHANNELS = 2
SNDRV_PCM_HW_PARAM_RATE = 48000

我有一个以 60fps 运行的垂直同步游戏循环:

int num_base_samples = 48000 * (1 / 60);
int num_samples =  num_base_samples * 2;
int16_t buffer[num_samples] = ;

while (true) 
  int16_t *samples = buffer;
  for (int sample_i = 0; sample_i < num_base_samples; sample_i++) 
    *samples++ = 0x33;
    *samples++ = 0x33;
  

  struct snd_xferi transfer = ;
  transfer.buf = buffer;
  transfer.frames = num_base_samples;
  int res = ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &transfer);
  if (res == -1 && ernno == EPIPE) 
    ioctl(fd, SNDRV_PCM_IOCTL_PREPARE);
  

SNDRV_PCM_IOCTL_WRITEI_FRAMES 的第一次迭代中,我没有收到任何错误。 所有后续迭代,我得到 Broken Pipe 错误。 所以,为了解决这个问题,在每一帧结束时,我检查EPIPE 并调用SNDRV_PCM_IOCTL_PREPARE 这消除了 Broken Pipe 错误,但听不到声音。 如何最好地解决这个问题?

【问题讨论】:

仅存储 0x33 会产生静音。您需要改变数据以创建波形 @CraigEstey 为什么会产生沉默?不就是波的幅度吗? @RyanMcClue 不是。这是每个样本的值。如果所有样本的值都相同,则没有波,它只是一条平线,表示静音。 @MarcoBonelli 查看我对 Craig Estey 的回答的评论 【参考方案1】:

仅存储 0x33 会产生静音。您需要改变数据以创建波形 - 克雷格·埃斯蒂 20 分钟前

这会产生一个平线图/波浪。

@CraigEstey 为什么会产生沉默?不就是波的幅度吗? – 瑞恩麦克卢 18 分钟前

不,正如马可所说...

@RyanMcClue 不是。这是每个样本的值。如果所有样本都具有相同的值,则没有波,它只是一条平线,表示静音。 – 马可·博内利 6 分钟前

您必须生成自己的波形。这是一个粗略的示例:

double rad = 0.0;
for (int sample_i = 0; sample_i < num_base_samples; sample_i++) 
    double val = sin(rad) * 32767;

    *samples++ = val;
    *samples++ = val;

    // some incremental value ...
    rad += 0.1;

    rad %= 2.0 * M_PI;


更新:

我认为您关于提供静音的单个样本值的概念是错误的。

不仅仅是一个概念[旁注:在使用 16 位签名立体声 PCM 模式之前,我已经为声音编写了商业应用程序]。 正在发生的事情。

PCM 设备需要波形。直接访问/dev/snd/pcm* 可能会遇到其他问题。见下文。

听我说:https://gist.github.com/ryan-mcclue/dc1ce8350125e9741faa14c7f9908e9d 与gcc alsa-kernel.c -o alsa-kernel &amp;&amp; ./alsa-kernel /dev/snd/pcmC0D0p 一起运行。它加载单个样本值(memset 调用)并听到声音。

您可能会听到类似声音的爆裂声,但缓冲区不是 [只是] 一个增益因素。它需要有所不同。不知道您的修改代码是什么,但弹出/点击可能(例如)看到 0x33 散布着零(例如超出缓冲区的末尾???)。

在我的系统上,pcmC0D0p 是我的模拟扬声器,pcmC1D0p 是我的 USB 耳机。从设置下的 GTK 声音菜单中,我必须选择 other 设备。否则,它归 ALSA 和/或 pulseaudio“所有”。例如,要使用耳机,我必须在菜单中选择模拟扬声器。然后,该应用程序可以访问耳机。否则,对应的/dev/snd/pcm* 设备将被视为忙。

我下载了你的应用程序。当我只使用您的原始代码(0x33)时,我听不到任何声音——只是沉默。当我添加我发布的sin 代码时,我会听到一个音调。我可以通过调整rad 的增量来改变频率。

我正在尝试调整它以连续加载帧,而不是仅仅 2 秒。 – 瑞恩麦克卢 23 小时前

如果是我,在添加循环之前,我会确保我得到的声音输出与我已经拥有的缓冲区数据相对应。


更新 #2:

我的立场是正确的。这是我正在使用的示例:https://gist.github.com/ryan-mcclue/eba7dd459418d7c4d9d5322827dc16cf Run with g++ alsa-vsync.cpp -lX11 -lXrender -lXrandr -lXfixes -lXpresent

Colorful ...这个版本探测一个开放的设备,所以它可能有pulseaudio的问题。您可以将正确的设备硬连线作为临时解决方法并按照我上面的建议进行操作,但可能有更好的方法...

我不知道 ALSA 拥有设备(究竟是什么拥有的?)。

pulseaudio 是一个声音服务器,充当声音应用程序的交通警察。它位于兼容的应用程序和较低级别的 ALSA 系统之间。应用程序通过 pulseaudio API 与守护进程而不是直接与 /dev/snd/pcm* 对话,请参阅:https://askubuntu.com/questions/581128/what-is-the-relation-between-alsa-and-pulseaudio-sound-architecture

看起来可能是这种情况,因为它选择了第二个子设备 pcmC0D3p(使用 pcmC0D0p 会导致程序停止)。那么,解决这个问题的唯一方法是使用 pulseaudio 或其他声卡吗? – 瑞恩麦克卢 14 小时前

这取决于您希望如何使用该设备。也就是说,只是一些个人测试/实验?或者,某种生产程序?因为你提到了另一个声卡,我假设它是供个人使用的,因为你不能在任意系统上安装另一个声卡。

通常可能是使用pulseaudio。即使您添加了另一张物理卡,它最初也会由 pulseaudio 配置/探测。

AFAICT,根据我的测试,守护程序使用O_EXCL 打开/dev/snd/pcm* 设备。但是,它似乎只打开一个“活动”设备[如图所示/从声音控制的设置菜单中选择]。

因此,快速的解决方法是按照我的做法进行操作。通过设置菜单,引导守护程序远离您要使用的设备。这为您确实想要使用的设备释放了/dev/snd/pcm*。或者,您可以通过systemd 禁用该服务

但是……

认为 pulseaudio API [因为它使用 ALSA 作为后端] 是 ALSA 兼容的。

从上面链接的图表中,有一个libalsa pulse,因此您也许无需太多修改就可以连接到pulseaudio。

【讨论】:

我相信您对单个样本值给出沉默的概念是错误的。以我的要点:gist.github.com/ryan-mcclue/dc1ce8350125e9741faa14c7f9908e9d 与gcc alsa-kernel.c -o alsa-kernel &amp;&amp; ./alsa-kernel /dev/snd/pcmC0D0p 一起运行。它加载单个样本值(memset 调用)并听到声音。我正在尝试调整它以连续加载帧,而不是仅仅 2 秒。 我的立场是正确的。这是我正在使用的示例:gist.github.com/ryan-mcclue/eba7dd459418d7c4d9d5322827dc16cf 使用g++ alsa-vsync.cpp -lX11 -lXrender -lXrandr -lXfixes -lXpresent 运行。我不知道 ALSA 拥有设备(究竟是什么拥有它?)。看起来可能是这种情况,因为它选择了第二个子设备pcmC0D3p(使用pcmC0D0p 会导致程序停止)。那么,解决这个问题的唯一方法是使用 pulseaudio 或其他声卡吗?

以上是关于带有 SNDRV_PCM_IOCTL_WRITEI_FRAMES 的 ALSA 断管的主要内容,如果未能解决你的问题,请参考以下文章

带有和不带有聚合的 sql 查询

如何翻转正面带有标签而背面带有另一个标签的视图 - 参见图片

CakePHP 如何处理带有/不带有 'id' 字段的 HABTM 表?

带有滚动的 Div 和带有绝对位置的内容

带有 RecyclerView 的 DialogFragment 比带有 Recyclerview 的 Fragment 慢

访问控制允许带有和不带有 www 的来源