我目前正在开发一个 Rust 库/CLI/UI,用于管理 Focusrite Scarlett USB 音频设备特别是在 Linux 上。这些设备通常有许多不同的内部设置(我的 18i8 有近 300 个单独的控件),所以我试图在我的设计中考虑所有这些,以便用户可以轻松更改路由配置、输入/输出/混合增益、静音切换等
值得庆幸的是,Linux 已经通过一些 modprobe 配置和最新的内核支持这些设备:
/etc/modprobe.d/scarlett.conf:
options snd_usb_audio vid=0x1235 pid=0x8214 device_setup=1
对于不同的型号和可能不同的主要硬件版本,需要将上述值设置为不同的 USB 产品 ID,以启用对这些设备的支持。
我现在已经学习 ALSA API 了几周了,我学到了很多东西:
snd_ctl_t
、snd_hctl_t
和 snd_mixer_t
API 之类的东西,用户空间中基本上没有代码。所有结构都是不透明的,函数(系统调用)与这些结构引用一起使用来获取详细信息。内核对内存进行了一些魔法并将其提供给用户空间。无论如何,这就是我的困境:我可以列出 ALSA 设备并迭代它们,通过
snd_ctl_card_info_t
我可以获得一些有用的属性,例如:
name
:思嘉 18i8 USBid
:USBlongname
:Focusrite Scarlett 18i8 USB,USB-0000:00:14.0-2.2,高速mixername
:USB 混音器components
:USB1235:8214driver
:USB 音频但是,这似乎是我可以从可用 API 中获得的“最”的。我可以通过从 components
字段中的 USB 供应商/产品 ID 中提取模型类型来获取模型类型,并且我可以通过解析
longname
来找到实际的 USB 句柄,但是:
可能连接了多个具有相同 USB 供应商/产品 ID 的设备:这是将设备链接在一起以添加输入/输出的常用技术。我担心提取
usb-*
识别每个设备,希望有某种序列号,以便卡A(18i8)的配置永远不会无意中应用于卡B(18i8),并且我假设有某种USB设备包含每个设备的序列号的字段。
编辑
:我可以通过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⟩
设备节点(以及
/dev/snd/
中与该卡关联的其他设备节点)名称中的数字。由此,您可以获取sysfs树中的相应节点,并遍历父节点链以获取您想要的任何标识符;直接打开 /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}')