응용 프로그램을 음소거하는 스크립트


14

내 목표는 전체 시스템이 아닌 Spotify 응용 프로그램을 음소거 할 수 있도록하는 것입니다. 명령 사용 : ps -C spotify -o pid=Spotify의 프로세스 ID를 찾을 수 "22981"있습니다. 이 경우 ID는 입니다. 해당 프로세스 ID로 다음 목록에서 검색하고 싶습니다 pacmd list-sink-inputs. 이 명령은 다음과 같은 목록을 반환합니다.

eric@eric-desktop:~$ pacmd list-sink-inputs
Welcome to PulseAudio! Use "help" for usage information.
>>> 1 sink input(s) available.
    index: 0
    driver: <protocol-native.c>
    flags: START_CORKED 
    state: RUNNING
    sink: 1 <alsa_output.pci-0000_00_1b.0.analog-stereo>
    volume: 0: 100% 1: 100%
            0: -0.00 dB 1: -0.00 dB
            balance 0.00
    muted: no
    current latency: 1019.80 ms
    requested latency: 371.52 ms
    sample spec: s16le 2ch 44100Hz
    channel map: front-left,front-right
                 Stereo
    resample method: (null)
    module: 8
    client: 10 <Spotify>
    properties:
        media.role = "music"
        media.name = "Spotify"
        application.name = "Spotify"
        native-protocol.peer = "UNIX socket client"
        native-protocol.version = "26"
        application.process.id = "22981"
        application.process.user = "eric"
        application.process.host = "eric-desktop"
        application.process.binary = "spotify"
        window.x11.display = ":0"
        application.language = "en_US.UTF-8"
        application.process.machine_id = "058c89ad77c15e1ce0dd5a7800000012"
        application.process.session_id = "058c89ad77c15e1ce0dd5a7800000012-1345692739.486413-85297109"
        application.icon_name = "spotify-linux-512x512"
        module-stream-restore.id = "sink-input-by-media-role:music"

이제는 application.process.id = "22981"이 경우 싱크 입력 인덱스 와 상관 관계를 설정하고 싶습니다 index: 0. 이제 색인 번호를 사용하여 pacmd set-sink-input-mute 0 1Spotify를 음소거하고 Spotify pacmd set-sink-input-mute 0 0를 음소거 해제하려면 이 명령을 실행해야합니다 . 이러한 명령의 경우 첫 번째 숫자는 앞에서 찾은 색인 번호로 바꿔야하며 다음 숫자는 음소거를 켜거나 끄는 부울입니다. 이것을 스크립트에 모두 넣을 수 있으므로 Spotify 응용 프로그램을 음소거 / 음소거 해제하는 명령을 얻을 수 있습니까?

편집 : 아래의 두 스크립트 모두 예상대로 작동합니다. 누군가 토글을 추가하여 그에 따라 확인 muted: yes하거나 muted: no음소거하거나 음소거를 해제 할 수 있습니까?

편집 : 나는 glenn jackman의 스크립트를 수정하여 토글을 추가 할 수있었습니다.

#!/bin/bash

main() {
    local action=toggle
    while getopts :mu option; do 
        case "$option" in 
            m) action=mute ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))
    local pid=$(pidof "$1")
    if [[ -z "$pid" ]]; then
        echo "error: no running processes for: $1" >&2
    elif [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify an application name" 
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "Usage: $0 [-m | -u] appname"
    echo "Default: toggle mute"
    echo "Arguments:"
    echo "-m = mute application"
    echo "-u = unmute application"
    exit $1
}

toggle() {
    local status=$(get_status "$1")
    if [[ "$status" == "yes" ]]; then
      unmute "$1"
    elif [[ "$status" == "no" ]]; then
      mute "$1"
    fi
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }

adjust_muteness() { 
    local index=$(get_index "$1")
    local status=$(get_status "$1")
    [[ "$index" ]] && pacmd set-sink-input-mute "$index" $2 >/dev/null 
}

get_index() {
    local pid=$(pidof "$1")
    pacmd list-sink-inputs | 
    awk -v pid=$pid '
    $1 == "index:" {idx = $2} 
    $1 == "application.process.id" && $3 == "\"" pid "\"" {print idx; exit}
    '
}

get_status() {
   local pid=$(pidof "$1")
   pacmd list-sink-inputs | 
   awk -v pid=$pid '
   $1 == "muted:" {idx = $2} 
   $1 == "application.process.id" && $3 == "\"" pid "\"" {print idx; exit}
   '
}

main "$@"

왜 사용하지 pactl list sink-inputs않습니까? 네트워크를 통해 작동합니다.
Janus Troelsen

답변:


13

흥미로운 도전에 대한 나의 생각은 다음과 같습니다.

#!/bin/bash

main() {
    local action=mute
    while getopts :hu option; do 
        case "$option" in 
            h) usage 0 ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))

    if [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify an application name" 
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "usage: $0 [-h] [-u] appname"
    echo "where: -u = ummute application (default action is to mute)"
    exit $1
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }

adjust_muteness() { 
    local index=$(get_index "$1")
    [[ "$index" ]] && pacmd set-sink-input-mute "$index" $2 >/dev/null 
}

get_index() {
    local pid=$(pidof "$1")
    if [[ -z "$pid" ]]; then
        echo "error: no running processes for: $1" >&2
    else
        pacmd list-sink-inputs | 
        awk -v pid=$pid '
            $1 == "index:" {idx = $2} 
            $1 == "application.process.id" && $3 == "\"" pid "\"" {print idx; exit}
        '
    fi
}

main "$@"

이것은 또한 완벽하게 작동합니다
era878

@ era878, 토글이 기본 동작이라는 아이디어가 마음에 듭니다. 그러나 get_status함수는 상태가 적절한 응용 프로그램에 속하는지 교차 확인하지 않고 "음소거 된"줄만 찾습니다. get_index자세한 내용은 내 기능을 다시 읽으십시오 .
glenn jackman

3
nice awk skills :)
hytromo

@glennjackman, 예, 잠시 후 알아 냈습니다. 방금 게시 한 스크립트가 이제 올바르게 작동한다고 생각합니다.
era878

1
세부 사항 : awk -v var=val. Awk는 행을 1 개씩 반복하고, $1 == ...명령문 중 하나를 일치 시키려고 시도하고 일치하는 경우 대괄호 안에 코드를 실행 한 후 계속합니다. 첫 번째 명령문은 첫 번째 단어가 인 행에서 일치 index:하고 두 번째 단어 (SINK INDEX)를 idx변수 에 저장합니다. 따라서 awk가 두 번째 문 ( = , = , = ) 과 일치 할 때까지 idx다음 index: <SINK INDEX>줄로 덮어 씁니다 . 이 두 번째 명령문이 일치하면 awk가 인쇄되고 (첫 번째 명령문과 일치하는 마지막 행 ) 종료됩니다. $1application.process.id$2=$3<expected pid val>idxindex:
KrisWebDev

7

솔루션 주셔서 감사합니다! 여기에 제공된 스크립트를 사용하여 문제를 해결했습니다. 조금 수정해야했기 때문에 개선 된 버전에 합류했습니다.

원래 스크립트가 작동하지 않는 이유는 일부 응용 프로그램에 여러 개의 PID (예 : 여러 PID)가있을 수 있지만 그 중 하나만 소리를 내기 때문에 실제로 Pulseaudio에 연결되어 있기 때문입니다. 스크립트는 처음 발견 된 PID 만 사용했기 때문에 일반적으로 원하는 응용 프로그램을 / not / 음소거합니다.

여기에 인수가 PulseAudio에 등록 된 애플리케이션 이름 인 버전이 있습니다. pacmd list-sink-inputs명령 을 실행하고 application.name필드를 찾아서이 이름을 찾을 수 있습니다 .

다른 솔루션은 동일한 애플리케이션 이름을 가진 모든 PID를 음소거 / 음소거 해제하는 것입니다.

#!/bin/bash

# Adapter from glenn jackman on http://askubuntu.com/questions/180612/script-to-mute-an-application
# to depend directly on the name of the PulseAudio client
# rather than the application name (several instances of one application could
# run while only one is connected to PulseAudio)

# Possible further improvement: it could be useful to also mute all clients having
# the specified name. Here, only the first one is muted.

#!/bin/bash

main() {
    local action=mute
    while getopts :hu option; do
        case "$option" in
            h) usage 0 ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))

    if [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify the name of a PulseAudio client"
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "usage: $0 [-h] [-u] appname"
    echo "where: -u = ummute application (default action is to mute)"
    exit $1
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }

adjust_muteness() {
    local index=$(get_index "$1")
    if [[ -z "$index" ]]; then
        echo "error: no PulseAudio sink named $1 was found" >&2
    else
        [[ "$index" ]] && pacmd set-sink-input-mute "$index" $2 >/dev/null
    fi
}

get_index() {
#    local pid=$(pidof "$1")
#    if [[ -z "$pid" ]]; then
#        echo "error: no running processes for: $1" >&2
#    else
        pacmd list-sink-inputs |
        awk -v name=$1 '
            $1 == "index:" {idx = $2}
            $1 == "application.name" && $3 == "\"" name "\"" {print idx; exit}
        '
#    fi
}

main "$@"

6

질문이 스크립트를 요구하고 있지만 여기에 남겨두고 싶었습니다.

우분투 에서이 작업을 수행하는 C 응용 프로그램을 작성했습니다. 더 좋은 점은 표시기를 트레이에 놓고 (을 사용하여 libappindicator) Spotify가 재생중인 것을 짧은 간격으로 확인합니다. 광고를 재생하는 경우 (블랙리스트를 확인) Spotify를 음소거합니다. 새 광고가 재생되는 경우 표시기 메뉴에서 음소거를 클릭하면 블랙리스트에 추가됩니다.

그것이하는 일은 X 윈도우를 찾고,이 윈도우 XFetchName는를 반환합니다 Spotify - Linux Preview. 그런 다음 해당 창의 속성 XGetWindowProperty을 쿼리 _NET_WM_ICON_NAME하여 "Spotify – <Artist> – <Song>"형식으로 문자열을 반환 합니다. 광고를 재생하면 다음과 같은 결과가 반환됩니다.

"Spotify – Spotify – Premium Free Trial Cancel Any Time"

현재 제목이 목록에 있는지 효과적으로 확인하기 위해 광고 목록의 3 차 검색 트리 를 유지 관리 합니다.

또한 PulseAudio Asynchronous API 를 사용하여 sink-inputsand 를 쿼리합니다 set-mute.

pa_context_get_sink_input_info_list()
pa_context_set_sink_input_mute()

단순한 C 코드이므로 가볍습니다. indicator-muteads.deb 에서 소스 코드와 Ubuntu 패키지를 확인하십시오 . 아마 쉘 스크립트를 2-3 배나 능가했을 것입니다.


1.0.11 버전에서 작동하지 않습니다
Janus Troelsen

4

우선 spotify와 같은 애플리케이션의 PID를 찾는 "보다 정확한"방법은 다음을 사용하는 것입니다.

pidof spotify

나는 일을하는 스크립트를 만들었습니다. 그것이 최선의 방법인지는 모르겠지만 완벽하게 작동합니다.

#!/bin/bash
# Script to mute an application using PulseAudio, depending solely on
# process name, constructed as answer on askubuntu.com: 
# http://askubuntu.com/questions/180612/script-to-mute-an-application

#It works as: mute_application.sh vlc mute OR mute_application.sh vlc unmute

if [ -z "$1" ]; then
   echo "Please provide me with an application name"
   exit 1
fi

if [ -z "$2" ]; then
   echo "Please provide me with an action mute/unmute after the application name"
   exit 1
fi

if ! [[ "$2" == "mute" || "$2" == "unmute" ]]; then
   echo "The 2nd argument must be mute/unmute"
   exit 1
fi

process_id=$(pidof "$1")

if [ $? -ne 0 ]; then
   echo "There is no such process as "$1""
   exit 1
fi

temp=$(mktemp)

pacmd list-sink-inputs > $temp

inputs_found=0;
current_index=-1;

while read line; do
   if [ $inputs_found -eq 0 ]; then
      inputs=$(echo -ne "$line" | awk '{print $2}')
      if [[ "$inputs" == "to" ]]; then
         continue
      fi
      inputs_found=1
   else
      if [[ "${line:0:6}" == "index:" ]]; then
         current_index="${line:7}"
      elif [[ "${line:0:25}" == "application.process.id = " ]]; then
         if [[ "${line:25}" == "\"$process_id\"" ]]; then
            #index found...
            break;
         fi
      fi
   fi
done < $temp

rm -f $temp

if [ $current_index -eq -1 ]; then
   echo "Could not find "$1" in the processes that output sound."
   exit 1
fi

#muting...
if [[ "$2" == "mute" ]]; then
   pacmd set-sink-input-mute "$current_index" 1 > /dev/null 2>&1
else
   pacmd set-sink-input-mute "$current_index" 0 > /dev/null 2>&1
fi

exit 0

다음과 같이 작업 할 수 있습니다.

./mute_application.sh spotify mute

또는

./mute_application.sh spotify unmute

Audacious와 Vlc를 실행하고 그중 하나만 뮤팅 / 뮤트 해제하여 테스트했습니다.


완벽한 대본, 예상대로 작동
era878

1

나는 실제로 스크립트를 할 수 없지만 hakermania의 스크립트를 수정하여 다른 스크립트를 만들었습니다.

이것은 5 % 단위로 특정 응용 프로그램의 볼륨을 늘리거나 줄입니다.

편집 : 실제로, 항상 마지막으로 열린 앱을 변경하는 중입니다. 아이디어?

#!/bin/bash
# Script to increase or decrease an individual application's volume using PulseAudio, depending solely on
# process name, based on another script by hakermania, constructed as answer on askubuntu.com: 
# http://askubuntu.com/questions/180612/script-to-mute-an-application

# It works as: change_app_volume.sh vlc increase OR change_app_volume.sh vlc decrease
# Set desired increments in lines #66 and #68

if [ -z "$1" ]; then
   echo "Please provide me with an application name"
   exit 1
fi

if [ -z "$2" ]; then
   echo "Please provide me with an action increase/decrease after the application name"
   exit 1
fi

if ! [[ "$2" == "increase" || "$2" == "decrease" ]]; then
   echo "The 2nd argument must be increase/decrease"
   exit 1
fi

process_id=$(pidof "$1")

if [ $? -ne 0 ]; then
   echo "There is no such process as "$1""
   exit 1
fi

temp=$(mktemp)

pacmd list-sink-inputs > $temp

inputs_found=0;
current_index=-1;

while read line; do
   if [ $inputs_found -eq 0 ]; then
      inputs=$(echo -ne "$line" | awk '{print $2}')
      if [[ "$inputs" == "to" ]]; then
         continue
      fi
      inputs_found=1
   else
      if [[ "${line:0:6}" == "index:" ]]; then
         current_index="${line:7}"
      elif [[ "${line:0:25}" == "application.process.id = " ]]; then
         if [[ "${line:25}" == "\"$process_id\"" ]]; then
            #index found...
            break;
         fi
      fi
   fi
done < $temp

rm -f $temp

if [ $current_index -eq -1 ]; then
   echo "Could not find "$1" in the processes that output sound."
   exit 1
fi

#increase/decrease...
if [[ "$2" == "increase" ]]; then
   pactl set-sink-input-volume "$current_index" +5% > /dev/null 2>&1
else
   pactl set-sink-input-volume "$current_index" -5% > /dev/null 2>&1
fi

exit 0

0

앱의 모든 입력 (다중 프로세스)을 음소거하고 기본값을 토글하도록 편집 된 스크립트 :

#!/bin/bash

main() {
    local action=toggle
    while getopts :hu option; do
        case "$option" in
            h) usage 0 ;;
            m) action=mute ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))

    if [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify an application name"
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "usage: $0 [-h] [-u] appname"
    echo "where: -u = ummute , -m = mute (default action is to toggle)"
    exit $1
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }
toggle() { adjust_muteness "$1" toggle; }

adjust_muteness() {
    clients=$(pactl list clients short | awk '/[0-9]+.*'$1'.*/{print $1}')
    inputs=$(pactl list sink-inputs short)
    for c in $clients; do
        for i in $(printf '%s' "$inputs" | awk '/[0-9]+\s[0-9]+\s'$c'/{print $1}'); do
            pactl set-sink-input-mute $i $2 &
        done
    done
}

main "$@"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.