연결된 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가 그것에 관심이 있는지 모르겠습니다.

따라서 올바른 해결책은 두 개의 동일한 오디오 장치 식별 섹션에서 링크 된 기사 에 제공된 조언이라고 생각합니다 . 규칙을 사용 하여 장치를 일치시키고 또는 에서 찾을 수있는 해당 장치에 고유 한 이름을 제공합니다 .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?"(카드 번호가 변경 될 수 있으므로 와일드 카드 glob 문자 "?"로 대체 됨).

보다 완전한 예는 전체 규칙 파일을 제공하는 링크의 위에서 언급 한 섹션을 참조하십시오 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 포트를 통해서만 식별하고 싶기 때문에 전체 파일 경로를 사용할 는 없지만 현재는 사운드 카드가 하나만 있으므로 할 수 있습니다. t 해당 경로가 다른 모델 또는 공급 업체에 대해 변경되는지 확인합니다. 따라서 1.2.1내 장치의 특정 USB 포트에 연결된 USB 허브의 특정 포트를 설명 하는 패턴 등을 검색 합니다.

나는 udev 규칙을 사용하여 실행하지 못했습니다-분명히 쓰기 위해 루트 액세스 권한이 필요 /sys/devices/...하지만 여러 답변을 살펴 보았지만 완료 할 수 없었습니다-아마도 Raspberry에서 Raspbian Jessie를 실행하고 있기 때문일 수 있습니다 Pi, 아마도 Linux에 대해 잘 모르기 때문일 것입니다.

그러나 내 사용 사례는 장치를 연결할 때 스크립트를 실행해야하는 것은 아닙니다. 부팅시 실행하는 것만으로도 충분하므로 마지막으로 필요한 것은 crontab을 사용하여 편집 sudo crontab -e하고 줄을 추가하는 것입니다.

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

그리고 짜잔 , 나는 사용하여 Java 코드에서 특정 USB 사운드 카드에 액세스하고 다음을 사용하여 AudioSystem.getMixerInfo()사운드를 재생할 수 있습니다.AudioSystem.getClip(mixerInfo)