接続されているUSBポートで複数のUSBサウンドカードを区別する

Dec 11 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()

私のアプローチは正しく、詳細が欠けているだけですか、それともこれは完全に間違った方向ですか?

回答

1 meuh Dec 11 2020 at 17:27

あなたは正しい方向に進んでいます。udevでうまくいかないことがたくさんあります。udevルールが機能NAME="..."しなくなった理由は、カーネルがこの方法でデバイスの名前を変更することを許可しなくなったためです。SYMLINK+=仕事全般とのシンボリックリンクの作成ですが、alsaがそれらに興味を持っているかどうかはわかりません。

したがって、おそらく正しい解決策は、「2つの同一のオーディオデバイスを特定する」というタイトルのセクションのリンクされた記事に記載されているアドバイスだと思います。付きのルールを使用しDEVPATH==てデバイスを照合し、またはでATTR{id}="ABC"検索するデバイスに一意の名前を付けます。aplay -lcat /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

1 mag_zbc Dec 15 2020 at 22:16

@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つしかないので、 tモデルまたはベンダーごとにそのパスが変更されているかどうかを確認します。したがって1.2.1、私のデバイスの特定のUSBポートに接続されたUSBハブの特定のポートを説明するパターンなどを検索します。

私はudevルールを使用してそれを実行することができませんでした-書き込みにはルートアクセスが必要なようです/sys/devices/...(これは理にかなっています)が、いくつかの答えを調べたにもかかわらず、それを実行できませんでした-おそらく私はRaspberryでRaspbianJessieを実行しているためですPi、おそらくLinuxについてあまり知らないからでしょう。

ただし、私のユースケースでは、デバイスの接続時にスクリプトを実行する必要はありません。起動時にスクリプトを実行するだけで十分なので、最後に必要なのは、を使用してcrontabを編集しsudo crontab -e、行を追加することでした。

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

そして出来上がり、私が使用してJavaコードでアクセスspeciffic USBサウンドカードを取得することができますAudioSystem.getMixerInfo()使用してサウンドをプレイAudioSystem.getClip(mixerInfo)