Perl

通過插入的 USB 埠區分多個 USB 音效卡

  • December 15, 2020

我想要的是能夠始終如一地區分多個 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()

我的方法是否正確,我只是遺漏了一些細節,或者這是完全錯誤的方向?

根據@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 埠辨識它們,但目前我只有一個音效卡所以我可以’ t 檢查不同型號或供應商的路徑是否發生變化。因此,搜尋1.2.1描述連接到我設備的特定 USB 埠的 USB 集線器的特定埠的模式等。

我還沒有設法使用 udev 規則執行它 - 顯然你需要 root 訪問權限才能寫入 /sys/devices/...(這是有道理的)但是儘管查看了幾個答案,我還是無法完成它 - 可能是因為我在 Raspberry 上執行 Raspbian Jessie Pi,可能是因為我對 Linux 不太了解。

但是,我的案例不一定要求在連接設備時執行該腳本 - 只需在啟動時執行它就足夠了,所以最後一件事是使用sudo crontab -e並添加一行來編輯 crontab

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

,我可以使用 Java 程式碼訪問特定的 USB 音效卡AudioSystem.getMixerInfo()並使用播放聲音AudioSystem.getClip(mixerInfo)

你在正確的軌道上。udev 有很多可能出錯的地方。udev 規則NAME="..."不再起作用的原因是核心不再允許您以這種方式重命名設備。符號連結的創建與SYMLINK+=一般工作有關,但我不知道 alsa 是否對它們感興趣。

因此,我認為可能正確的解決方案是在標題為“辨識兩個相同的音頻設備”部分的連結文章中給出的建議。使用規則 with匹配設備,並為該設備提供唯一名稱,然後您將在或中找到該名稱。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是. 因此,在 udev 中,僅當您在正確的目錄中匹配時才有效,即在我的情況下。這就是 udev 規則說的原因(卡號可能會更改,因此它被萬用字元 glob 字元“?”替換)。card0``ATTR{id}=``/sys``/sys/devices/.../sound/card0``DEVPATH=="/sys/devices/.../sound/card?"

有關更完整的範例,請參閱連結的上述部分,該部分為您提供了完整的規則文件85-my-usb-audio.rules

引用自:https://unix.stackexchange.com/questions/623895