通过插入的 USB 端口区分多个 USB 声卡

通过插入的 USB 端口区分多个 USB 声卡

我想要的是能够一致地区分多个 USB 声卡,通过插入的 USB 端口识别它们,并使用这些知识在我的 Java 程序中的特定声卡上播放声音。

到目前为止,我还停留在第一部分——通过 USB 端口识别声卡。

我做的第一件事就是遵循以下建议这个问题并使用 Udev 规则为声卡分配名称,脚本来自这个网站

这些是我添加的 Udev 规则

KERNEL=="controlC[0-9]*", DRIVERS=="usb", PROGRAM="/usr/bin/alsa_name.pl %k", NAME="snd/%c{1}"
KERNEL=="hwC[D0-9]*", DRIVERS=="usb", PROGRAM="/usr/bin/alsa_name.pl %k", NAME="snd/%c{1}"
KERNEL=="midiC[D0-9]*", DRIVERS=="usb", PROGRAM="/usr/bin/alsa_name.pl %k", NAME="snd/%c{1}"
KERNEL=="pcmC[D0-9cp]*", DRIVERS=="usb", PROGRAM="/usr/bin/alsa_name.pl %k", NAME="snd/%c{1}"

这些是内容alsa_name.pl

use strict;
use warnings;
#
my $alsaname = $ARGV[0]; #udev called us with this argument (%k)
my $physdevpath = $ENV{PHYSDEVPATH}; #udev put this in our environment
my $alsanum = "cucu";
#you can find the physdevpath of a device with "udevinfo -a -p $(udevinfo -q path -n /dev/snd/pcmC0D0c)"
#
#
$physdevpath =~ s/.*\/([^\/]*)/$1/; #eliminate until last slash (/)
$physdevpath =~ s/([^:]*):.*/$1/; #eliminate from colon (:) to end_of_line
#
if($physdevpath eq "1-1.3.1")
{
       $alsanum="11";
}
if($physdevpath eq "1-1.3.2")
{
       $alsanum="12";
}
if($physdevpath eq "1-1.3.3")
{
       $alsanum="13";
}
if($physdevpath eq "1-1.3.4")
{
       $alsanum="14";
}

#
if($alsanum ne "cucu")
{
       $alsaname=~ s/(.*)C([0-9]+)(.*)/$1C$alsanum$3/;
}
#
print $alsaname;
exit 0;

现在,当我插入 USB 声卡并查看时,/var/log/syslog我发现它并不完全工作:

NAME="snd/%c{1}" ignored, kernel device nodes cannot be renamed; please fix it in /etc/udev/rules.d/99-com.rules:16

我尝试根据以下内容修改我的 Udev 规则这个存储库它提供了一个 Udev 规则:

SUBSYSTEM!="sound", GOTO="my_usb_audio_end"
ACTION!="add", GOTO="my_usb_audio_end"

DEVPATH=="/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.2/1-1.2:1.0/sound/card?", ATTR{id}="SPEAKER"
DEVPATH=="/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/sound/card?", ATTR{id}="HEADSET"

LABEL="my_usb_audio_end"

所以我使用了以前的脚本并修改了我的规则:

KERNEL=="pcmC[D0-9cp]*", DRIVERS=="usb", PROGRAM="/usr/bin/alsa_name.pl %k", ATTR{id}="snd/%c{1}

但现在syslog告诉我:

error opening ATTR{some_very_long_id} for writing: Permission denied

我也尝试过这个答案并做了

KERNEL=="pcmC[D0-9cp]*", DRIVERS=="usb", PROGRAM="/usr/bin/alsa_name.pl %k", SYMLINK+="snd/%c{1}

我在 中没有看到任何错误syslog,我认为这很好,但是当我用 列出播放设备时aplay -l,我看到的只是

card 1: Device [USB Audio Device], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0

无论我插入哪个 USB 端口,都没有任何变化。我在 Java 程序中也没有看到任何有用/可区分的信息AudioSystem.getMixerInfo()

我的方法是否正确,只是缺少一些细节,还是这是完全错误的方向?

答案1

你走在正确的轨道上。 udev 可能会出现很多问题。 udev 规则不再起作用的原因NAME="..."是内核不再允许您以这种方式重命名设备。与SYMLINK+=一般工作相关的符号链接的创建,但我不知道 alsa 是否对它们感兴趣。

所以我认为可能正确的解决方案是您中给出的建议链接文章在标题为识别两个相同的音频设备。使用规则 来DEVPATH==匹配设备,并ATTR{id}="ABC"为该设备指定一个唯一的名称,然后您将在aplay -l或中找到该名称cat /proc/asound/cards

首先,手动尝试同样的事情。我没有任何 USB 声卡,只有内置设备,所以如果我这样做:

find /sys/devices/ -name id | grep sound

它列出了许多名为“id”的项目,但其中一些是目录,并且是唯一的文件感兴趣的是/sys/devices/.../sound/card0/id.如果我cat这个文件它保存设备的名称(“PCH”)。如果我将一个字符串写入这个伪文件,它会更改其名称:

sudo sh -c 'printf "%s" MYCARD >/sys/devices/.../sound/card0/id'

这可以在 的输出中看到aplay -l。这就是你想要用 udev 做的事情; sysfs 伪文件id属性card0。所以在 udev 中,ATTR{id}=只有在正确的目录中匹配时才有效/sys,即/sys/devices/.../sound/card0在我的例子中。这就是为什么 udev 规则说DEVPATH=="/sys/devices/.../sound/card?"(卡号可能会改变,因此它被通配符通配符“?”替换)。

有关更完整的示例,请参阅链接的上述部分,其中为您提供了完整的规则文件85-my-usb-audio.rules

答案2

根据@meuh的回答,我设法让它发挥作用,尽管与我最初的计划略有不同。

写入>/sys/devices/.../sound/card0/id文件确实是可行的方法,因此我为此编写了一个小 bash 脚本

#!/bin/bash
for file in $(find /sys/devices/ -name id | grep sound | grep usb)
do
    for fragment in $(echo $file | tr "/" "\n")
    do
        if [[ $fragment == *"1.2.1"* ]]
        then
            printf "%s" "EXT_B1" > "$file"
        fi
        if [[ $fragment == *"1.2.2"* ]]
        then
            printf "%s" "EXT_B2" > "$file"
        fi
        # etc
    done
done 

这部分 -for fragment in $(echo $file | tr "/" "\n") - 可能可以做得更优雅,但我不能只使用完整的文件路径,因为我想使用任何声卡并仅通过 USB 端口识别它们,但目前我只有一张声​​卡,因此我无法检查该路径是否因不同型号或供应商而改变。因此,搜索1.2.1描述连接到我的设备的特定 USB 端口的 USB 集线器的特定端口的模式等。

我还没有设法使用 udev 规则运行它 - 显然你需要 root 访问权限才能写入 /sys/devices/...(这是有道理的),但尽管查看了几个答案,我还是无法完成它 - 也许是因为我在 Raspberry 上运行 Raspbian Jessie Pi,可能是因为我对Linux不太了解。

但是,我的用例并不一定要求在连接设备时运行该脚本 - 只需在启动时运行它就足够了,因此最后需要做的就是使用编辑 crontabsudo crontab -e并添加一行

@reboot /path/to/my/script.sh

,我可以使用Java代码访问特定的USB声卡AudioSystem.getMixerInfo()并使用播放声音AudioSystem.getClip(mixerInfo)

相关内容