我目前正在开发一个Rust库/CLI/UI,用于管理Focusrite Scarlett个USB音频设备specifically on Linux.这些设备通常有many种不同的内部设置(我的18i8有近300个单独的控制),所以我试图在我的设计中考虑所有这些设置,以便用户可以轻松地更改布线配置、输入/输出/混合增益、静音切换等.

值得庆幸的是,Linux已经通过一些modProbe配置和最新的内核支持这些设备:

/etc/modprobe.d/scarlett.conf:

options snd_usb_audio vid=0x1235 pid=0x8214 device_setup=1

对于不同型号和可能不同的主要硬件版本,需要将上述值设置为不同的USB产品ID,以启用对这些设备的支持.

我一直在研究ALSA API,现在感觉已经weeks了,我学到了相当多:

  1. 有人用C语言编写了a GTK4 UI个用于管理Scarlett设备的代码,我基本上已经阅读了整个代码库,尽管有这么多困难,我仍然能够弄清楚它到底在做什么/正在使用哪些ALSA API.
  2. ALSA文档is abysmal at best,所以在这个项目之后,我将 compose 博客文章描述我的痛苦,解释其中的微妙之处,并试图回馈ALSA文档,以挽救其他人几天/几周/几个月的生命试图弄清楚这些事情.
  3. ALSA用户空间库通常是simply typedefs of syscalls to the kernel,这意味着对于像snd_ctl_tsnd_hctl_tsnd_mixer_tAPI这样的东西,用户空间中基本上没有代码.所有 struct 都是不透明的,函数(Syscall)与这些 struct 引用一起使用以获取详细信息.内核使用内存变魔术,并将其提供给用户空间.
  4. 谢天谢地,一个勇敢而慷慨的灵魂移植了ALSA API to Rust的大部分.我还将为这个项目贡献文件,以帮助其他人在他们的道路上.

无论如何,我的困境是这样的:我可以列出ALSA设备并迭代它们,通过snd_ctl_card_info_t我可以获得一些有用的属性,比如:

  • name:转换器18i8 USB
  • id:USB
  • longname:Focusite Scarlett 18i8 USB,USB-0000:00:14.0-2.2,高速
  • mixername:USB混音器
  • components:USB1235:8214
  • driver:USB音频

然而,这似乎是我可以从可用的API中获得的most个API.我可以通过从components字段中的USB供应商/产品ID中提取型号类型,并且可以通过解析longname找到实际的USB句柄,但是:

  1. 可能有多个连接相同USB供应商/产品ID的设备:这是将设备链接在一起以添加输入/输出的常见技术.
  2. 我关心的是提取usb-*插件的stability和USB供应商/产品ID,也许这可能会随着内核版本的不同而改变并变得无效.
  3. 我需要identify个设备,希望有某种序列号,这样卡A(18i8)的配置就不会不经意地应用到卡B(18i8),我假设有某种类型的USB设备字段,其中包含每个设备的序列号.

因此,is there a Linux API which will allow me to get a USB device file from an ALSA handle of some kind?或者,我是否可能遗漏了什么,从而允许从ALSA API中获取序列号?


EDIT:我可以通过lsusb找到序列号,字段名是iSerial:

$ sudo lsusb -d 1235:8214 -vv | grep iSerial
  iSerial          2 4K1A0P443EPEW7

我似乎确实需要达到root才能获得实际的字段值,但我可能可以通过在udev条规则中向我的用户授予对设备的权限来解决这个问题.如果我只能从ALSA API获得稳定的USB设备路径,那么我可以使用libusb或其他API来提取iSerial字段.

推荐答案

使用卡片的索引,由snd_ctl_card_info_get_card返回.卡索引是声卡/dev/snd/controlC⟨idx⟩设备 node (以及与/dev/snd/中的卡相关联的其他设备 node )名称中显示的数字.从这里,您可以获得sysfs树中的相应 node ,并遍历父 node 链以获得您想要的任何标识符;可以直接打开/sys下的文件,也可以通过udev打开.

下面是使用PyALSA和GUdev绑定到Python的演示,但是您应该能够用绑定到ALSA(Libasound)和udev(libudev或GUdev)的任何语言编写类似的东西.

#!/usr/bin/env python3
import gi
gi.require_version('GUdev', '1.0')
from gi.repository import GUdev
from pyalsa import alsacard

client = GUdev.Client()

for index in alsacard.card_list():
    print(f'index={index!r} longname={alsacard.card_get_longname(index)!r}')

    device = client.query_by_subsystem_and_name('sound', 'controlC' + str(index))
    usb_device = device.get_parent_with_subsystem('usb', 'usb_device')
    if not usb_device:
        print('    seems not to be an USB device')
        continue

    vidpid = f'''{
        usb_device.get_sysfs_attr('idVendor')
    }:{
        usb_device.get_sysfs_attr('idProduct')
    }'''
    serial = usb_device.get_sysfs_attr('serial')

    print(f'    VID:PID={vidpid} serial={serial!r}')

Linux相关问答推荐

将参数#0更改为shell脚本不工作

使用sed或awk映射自定义和任意函数

使用Bash从文件名中删除日期名称

Aarch64在Linux上是否有红色区域,如果有,是16个字节还是128个字节?

无法下载Centos 7上的存储库的元数据

如何从大页分配标准数组内存

用于替换 struct 文档中文本的 Bash 脚本

更改 awk 中的上一个重复行

在 Windows 上通过 SSH 运行 django 应用程序

在 Linux 下将 TCP 流量重定向到 UNIX 域套接字

我如何从 Ubuntu 上的源代码自己构建 python?

如何在 Linux 中通过控制台输出启动 Tomcat?

比较linux中两个未排序的列表,列出第二个文件中的唯一性

是否有一个 linux 命令来确定与给定进程 ID 关联的窗口 ID?

如何以编程方式禁用硬件预取?

如何链接到 GCC 中特定版本的共享库

Linux命令将一个文件复制到多个文件

如何将初始输入通过管道传输到随后将是交互式的进程中?

如何安排 tcpdump 在特定时间段内运行?

Linux 上的最大文件/目录数?