Unterscheiden Sie mehrere USB-Soundkarten anhand des angeschlossenen USB-Anschlusses

Dec 11 2020

Ich möchte in der Lage sein, mehrere USB-Soundkarten konsistent voneinander zu unterscheiden, sie anhand des angeschlossenen USB-Anschlusses zu identifizieren und dieses Wissen zu verwenden, um einen Sound auf einer bestimmten Soundkarte in meinem Java-Programm abzuspielen.

Bisher stecke ich beim ersten Teil fest - identifiziere Soundkarten am USB-Anschluss.

Als erstes habe ich den Ratschlägen in dieser Frage gefolgt und die Udev-Regeln verwendet, um Soundkarten mit dem Skript von dieser Site Namen zuzuweisen

Dies sind die Udev-Regeln, die ich hinzugefügt habe

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}"

und das sind die Inhalte von 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;

Wenn ich jetzt meine USB-Soundkarte einstecke und mir anschaue, /var/log/syslogsehe ich, dass es nicht genau funktioniert:

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

Ich habe versucht, meine Udev-Regeln basierend auf diesem Repository zu ändern , das eine Udev-Regel enthält:

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"

Also habe ich mein vorheriges Skript verwendet und meine Regel geändert:

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

aber jetzt syslogsagt mir:

error opening ATTR{some_very_long_id} for writing: Permission denied

Ich habe diese Antwort auch ausprobiert und getan

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

Ich sehe keine Fehler in syslog, was meiner Meinung nach gut ist, aber wenn ich Wiedergabegeräte mit aufliste aplay -l, sehe ich nur

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

und nichts ändert sich, unabhängig davon, an welchen USB-Anschluss ich ihn anschließe. Ich sehe auch keine nützlichen / unterscheidbaren Informationen in meinem Java-Programm, die verwenden AudioSystem.getMixerInfo()

Ist mein Ansatz korrekt und mir fehlen nur einige Details, oder ist dies eine völlig falsche Richtung?

Antworten

1 meuh Dec 11 2020 at 17:27

Du bist auf dem richtigen Weg. Es gibt viele Dinge, die mit udev schief gehen können. Der Grund, warum udev-Regeln mit NAME="..."nicht mehr funktionieren, ist, dass Sie mit dem Kernel Geräte auf diese Weise nicht mehr umbenennen können. Die Schaffung symbolischer Verknüpfungen mit der SYMLINK+=Arbeit im Allgemeinen, aber ich weiß nicht, ob alsa an ihnen interessiert ist.

Daher denke ich, dass die wahrscheinlich richtige Lösung der Rat ist, der in Ihrem verlinkten Artikel im Abschnitt " Identifizieren Sie zwei identische Audiogeräte" gegeben wird . Verwenden Sie eine Regel mit DEVPATH==, um mit dem Gerät übereinzustimmen und dem Gerät ATTR{id}="ABC"einen eindeutigen Namen zu geben, den Sie dann in aplay -loder finden cat /proc/asound/cards.

Versuchen Sie zunächst dasselbe manuell. Ich habe keine USB-Soundkarten, sondern nur ein eingebautes Gerät. Wenn ich das tue:

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

Es werden viele Elemente mit dem Namen "id" aufgelistet, aber einige davon sind Verzeichnisse, und die einzige Datei von Interesse ist /sys/devices/.../sound/card0/id. Wenn ich catdiese Datei habe, enthält sie den Namen des Geräts ("PCH"). Wenn ich einen String in diese Pseudodatei schreibe, ändert sich ihr Name:

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

und dies ist in der Ausgabe von zu sehen aplay -l. Dies ist, was Sie versuchen, mit udev zu tun; Die sysfs-Pseudodatei idist ein Attribut von card0. Funktioniert also in udev ATTR{id}=nur, wenn Sie im richtigen /sysVerzeichnis übereinstimmen , dh /sys/devices/.../sound/card0in meinem Fall. Aus diesem Grund heißt es in der udev-Regel DEVPATH=="/sys/devices/.../sound/card?"(die Kartennummer kann sich ändern, sodass sie durch das Platzhalterzeichen "?" Ersetzt wird).

Ein vollständigeres Beispiel finden Sie im oben genannten Abschnitt des Links, der eine vollständige Regeldatei enthält 85-my-usb-audio.rules.

1 mag_zbc Dec 15 2020 at 22:16

Basierend auf der Antwort von @ meuh habe ich es geschafft, es zum Laufen zu bringen, wenn auch etwas anders als ursprünglich geplant.

Das Schreiben in eine >/sys/devices/.../sound/card0/idDatei war in der Tat der richtige Weg, deshalb habe ich zu diesem Zweck ein kleines Bash-Skript geschrieben

#!/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 

Dieser Teil - for fragment in $(echo $file | tr "/" "\n")- hätte wahrscheinlich eleganter ausgeführt werden können, aber ich kann nicht einfach einen vollständigen Dateipfad verwenden, da ich eine beliebige Soundkarte verwenden und sie nur über den USB-Anschluss identifizieren möchte. Derzeit habe ich jedoch nur eine Soundkarte, damit ich sie verwenden kann. ' t Überprüfen Sie, ob sich dieser Pfad für verschiedene Modelle oder Anbieter ändert. Suchen Sie daher nach dem Muster 1.2.1usw., das den angegebenen Anschluss des USB-Hubs beschreibt, der mit dem angegebenen USB-Anschluss meines Geräts verbunden ist.

Ich habe es nicht geschafft, es mit udev-Regeln auszuführen - anscheinend benötigen Sie Root-Zugriff zum Schreiben /sys/devices/...(was sinnvoll ist), aber obwohl ich mehrere Antworten durchgesehen habe, konnte ich es nicht erledigen - vielleicht, weil ich Raspbian Jessie auf Raspberry ausführe Pi, vielleicht weil ich nicht viel über Linux weiß.

In meinem Anwendungsfall muss dieses Skript jedoch nicht unbedingt beim Anschließen des Geräts ausgeführt werden. Es reicht aus, es nur beim Booten auszuführen. Das letzte, was erforderlich war, war, crontab mit zu bearbeiten sudo crontab -eund eine Zeile hinzuzufügen

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

Und voila , ich kann mit Java-Code auf eine bestimmte USB-Soundkarte zugreifen AudioSystem.getMixerInfo()und Sound abspielenAudioSystem.getClip(mixerInfo)