Как изменить приемник pulseaudio с помощью "pacmd set-default-sink" во время воспроизведения?
Мне нужно переключить сервер pulseaudio воспроизводимого в данный момент аудиопотока.
Pulseaudio сервер настроен с IP 192.168.1.105
$>cat /etc/pulse/default.pa
...
load-module module-esound-protocol-tcp auth-anonymous=1
load-module module-native-protocol-tcp auth-anonymous=1
load-module module-zeroconf-publish
...
На стороне источника медиаплеер VLC воспроизводит песню.
Я создал новый туннельный приемник с pulseaudio на стороне источника.
pacmd load-module module-tunnel-sink server=192.168.1.105
pacmd set-default-sink 1
Но во время воспроизведения аудио сервер не мог быть немедленно изменен. Только после остановки плеера и воспроизведения все нормально.
От "gnome-volume-control" или "gnome-control-center sound" переключение выходных устройств применяется немедленно.
Как можно применить переключение выходного приемника сразу из командной строки во время воспроизведения звукового файла?
14 ответов
PulseAudio pacmd
не способен переключать приемники по умолчанию, пока на вход приемника идет активно воспроизводимый поток. Однако есть способ все еще достичь этого.
Изменение приемника по умолчанию из командной строки
Сначала нам нужно определить порядковый номер приемников, которые мы хотим переключить. Это можно сделать, позвонив по телефону:
pacmd list-sinks
В зависимости от нашей системы это даст вам более или менее длинный список приемников и свойств, которые в настоящее время доступны:
>>> 2 sink(s) available.
* index: 0
name: <alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1>
driver: <module-alsa-card.c>
:
:
index: 1
name: <alsa_output.pci-0000_00_14.2.analog-stereo>
driver: <module-alsa-card.c>
index
или name
здесь дано то, что нам нужно для адресации раковины из командной строки. Текущий приемник по умолчанию помечен звездочкой (здесь 0
).
Чтобы иметь возможность переключать приемники по умолчанию из командной строки, нам может потребоваться отключить восстановление целевого устройства потока, отредактировав соответствующую строку в /etc/pulse/default.pa
чтобы:
load-module module-stream-restore restore_device=false
Чтобы изменить выходной приемник по умолчанию на приемник 1
мы тогда бежим
pacmd set-default-sink 1
Успех можно визуализировать, открыв меню " Настройки звука".
Перемещение потока в другую раковину
Изменение приемника по умолчанию, пока у нас есть активный входной поток, воспроизводимый для данного приемника , не имеет никакого эффекта. Это должно быть сделано путем перемещения этого входа в другой приемник.
pacmd list-sink-inputs
скажет нам индекс входного потока
>>> 1 sink input(s) available.
index: 5
driver: <protocol-native.c>
Теперь мы знаем, что мы хотим переместить поток ввода 5
тонуть 1
позвонив
pacmd move-sink-input 5 1
или обратно в раковину 0
если нам нравится Это будет сделано немедленно без необходимости остановки воспроизведения.
Изменение раковины по умолчанию во время игры
Конечно, мы можем объединить эти две команды для немедленного переключения приемников по умолчанию во время воспроизведения, например, с помощью
pacmd set-default-sink 1 & pacmd move-sink-input 5 1
Недостаток этого метода заключается в том, что индекс входного потока изменяется каждый раз, когда мы останавливаем и перезапускаем музыкальный проигрыватель. Поэтому нам всегда нужно выяснить текущий индекс потока, прежде чем мы сможем переключиться с помощью строки комманд.
Я написал простой сценарий для автоматического перемещения всех входов приемника.
Использование: ./movesinks.sh <sink number>
#!/bin/bash
echo "Setting default sink to: $1";
pacmd set-default-sink $1
pacmd list-sink-inputs | grep index | while read line
do
echo "Moving input: ";
echo $line | cut -f2 -d' ';
echo "to sink: $1";
pacmd move-sink-input `echo $line | cut -f2 -d' '` $1
done
Улучшенная версия скрипта Gaco
#!/usr/bin/env bash
case "${1:-}" in
(""|list)
pacmd list-sinks |
grep -E 'index:|name:'
;;
([0-9]*)
echo switching default
pacmd set-default-sink $1 ||
echo failed
echo switching applications
pacmd list-sink-inputs |
awk '/index:/{print $2}' |
xargs -r -I{} pacmd move-sink-input {} $1 ||
echo failed
;;
(*)
echo "Usage: $0 [|list|<sink name to switch to>]"
;;
esac
моя копия во время выполнения находится на github и включает в себя также автоматическое переключение главного канала для kmix
Основываясь на ответе Гако, я немного переписал его для личного использования. Может быть, кто-то найдет это полезным. Это для переключения моих USB-колонок и игровой гарнитуры USB.
#!/bin/bash
# get list of sinks/cards (for settings CARD1/CARD2)
# pacmd list-sinks | awk '/name:/ {print $0};' | awk '{ print $2}' | sed 's/<//g; s/>//g'
CARD1="alsa_output.usb-C-Media_INC._C-Media_USB_Audio-00"
CARD2="alsa_output.usb-Kingston_HyperX_Virtual_Surround_Sound_00000000-00"
CURRENT_SINK=$(pacmd stat | awk -F": " '/^Default sink name: /{print $2}' | awk 'BEGIN{FS=OFS="."} NF--' | sed 's/alsa_output/alsa_output/g')
function setCard() {
if [ "$CURRENT_SINK" == "$1" ]
then
echo "Already using this Sink"
exit 1
fi
NEW_SINK=$(pacmd list-sinks | awk '/index:/ {print $1 $2 $3} /name:/ {print $0};' | grep -m1 -B1 $1 | grep index | awk '{print $1}' | cut -d ":" -f2)
SINK=$(pacmd set-default-sink $NEW_SINK)
INPUT=$(pacmd list-sink-inputs | grep index | awk '{print $2}')
pacmd move-sink-input $INPUT $NEW_SINK
echo "Moving input: $INPUT to sink: $NEW_SINK";
echo "Setting default sink to: $NEW_SINK";
notify-send --urgency=low "Audio Switching" "SINK: $NEW_SINK"
}
function toggleSinks() {
if [ "$CURRENT_SINK" == "$CARD1" ]
then
setCard $CARD2
else
setCard $CARD1
fi
}
function showHelp() {
echo "------------------------------------"
echo "AUDIO SINK SWITCHER"
echo " "
echo "$0 [options]"
echo " "
echo "options:"
echo "-h --help What you are looking at.."
echo "-g, --gaming Sets Gaming headset as output device"
echo "-s, --speakers Sets Speakers as output device"
echo "-t, --toggle Toggles the different output devices"
echo " "
echo "------------------------------------"
}
# check args length
if [ $# -eq 0 ]
then
echo "Toggling output devices (Speakers/Headset)"
toggleSinks
fi
# arg options
while test $# -gt 0; do
case "$1" in
-h|--help)
showHelp
exit 1
;;
-g|--gaming)
setCard $CARD2
exit 1
;;
-s|--speakers)
setCard $CARD1
exit 1
;;
-t|--toggle)
toggleSinks
echo "Toggling output devices (Speakers/Headset)"
exit 1
;;
*)
showHelp
exit 1
;;
esac
done
КРУЖИНЫ КРУЖЕВНЫЕ.
Скрипт Gaco с еще одной линией для переключения по кругу через доступные приемники.
#!/bin/bash
new_sink=$(pacmd list-sinks | grep index | tee /dev/stdout | grep -m1 -A1 "* index" | tail -1 | cut -c12-)
echo "Setting default sink to: $new_sink";
pacmd set-default-sink $new_sink
pacmd list-sink-inputs | grep index | while read line
do
echo "Moving input: ";
echo $line | cut -f2 -d' ';
echo "to sink: $new_sink";
pacmd move-sink-input `echo $line | cut -f2 -d' '` $new_sink
done
Теперь в Ubuntu 22.04 все намного проще. Нет необходимости изменять конфигурацию PulseAudio или перемещать активные потоки.
Вот как раз то, что мне нужно, чтобы переключиться с динамиков и микрофона моего компьютера на динамики моей гарнитуры:
#!/bin/bash
if [ "$(pactl get-default-sink)" = "alsa_output...<first-device-speaker>" ]; then
pactl set-default-sink "alsa_output...<second-device-speaker>"
pactl set-default-source "alsa_input...<second-device-microphone>"
else
pactl set-default-sink "alsa_output...<first-device-speaker>"
pactl set-default-source "alsa_input...<first-device-microphone>"
fi
Я назначил этому скрипту сочетание клавиш, и я могу легко переключаться с одного устройства на другое. Активные потоки переключаются мгновенно.
Чтобы получить список доступных приемников и источников:
-
pactl list short sinks
-
pactl list short sources
Я собрал воедино материал из нескольких разных мест и придумал этот сценарий для настройки Bluetooth JAMBOX после его сопряжения. Ваш MAC будет другим, конечно. Поскольку я контролирую громкость JAMBOX с помощью приложения Clementine, 130% громкости лучше всего работает в моем случае. Возможно, вы захотите изменить это, наряду с rhythmbox.png (это была единственная картинка динамика, которую я смог найти на своем компьютере). Проверка ошибок является элементарной, но работает довольно надежно. Я поместил это в настольный лаунчер для удобства на нетбуке ASUS.
#!/bin/bash
# setjambox connection setup
# Find the particulars of your environment with 'pactl list sinks'
# This script uses the sink name instead of the index number
# You also need libnotify-bin to run this script
# Enter the bluetooth MAC address of your device here
MAC=00:21:3C:9F:19:AD
# Make ready
# Convert device address per pulseaudio standards
DEV=$(echo $MAC|tr ':' '_')
TITLE="JAMBOX $MAC"
CONNECTED="Audio connection updated."
PROBLEM="Unable to update settings."
JBLOGO=/usr/share/icons/hicolor/48x48/apps/rhythmbox.png
# And go
pactl list short sink-inputs | while read stream; do
streamId=$(echo $stream | cut '-d ' -f1)
pactl move-sink-input "$streamId" bluez_sink.$DEV
done
pactl set-default-sink bluez_sink.$DEV
pactl set-card-profile bluez_card.$DEV a2dp
pactl set-sink-volume bluez_sink.$DEV 130%
if [ $? -eq 0 ]
then
notify-send -i $JBLOGO -t 3000 "$TITLE" "$CONNECTED"
else
notify-send -i gtk-dialog-warning -t 3000 "$TITLE" "$PROBLEM"
fi
Существует скрипт ruby (который я изначально разветвлял и переписывал), который позволяет вам изменять приемники по умолчанию, громкость и состояние отключения звука из командной строки.
Я адаптировал @mpapis к простому "переключателю sink0 или sink1" при запуске:
#!/bin/bash
SINK_INDEX1=0
SINK_INDEX2=1
ACTIVE_SINK=$(pacmd list-sinks | grep '* index:' | grep -o '[0-9]*')
if [ "$ACTIVE_SINK" = $SINK_INDEX1 ] ; then
pacmd set-default-sink $SINK_INDEX2
pacmd list-sink-inputs | awk '/index:/{print $2}' | xargs -r -I{} pacmd move-sink-input {} $SINK_INDEX2
else
pacmd set-default-sink $SINK_INDEX1
pacmd list-sink-inputs | awk '/index:/{print $2}' | xargs -r -I{} pacmd move-sink-input {} $SINK_INDEX1
fi
И вот скрипт, который будет переключаться между приемниками:
http://marginalhacks.com/index.0.html
Вот скрипт ниже:
#!/usr/bin/ruby
# Filename: pulse-switch-out
# Author: David Ljung Madison <DaveSource.com>
# See License: http://MarginalHacks.com/License/
# Description: Switch pulse audio output (sink) using pacmd
PACMD = %w(pacmd)
##################################################
# Usage
##################################################
def fatal(*msg)
msg.each { |m| $stderr.puts "[#{$0.sub(/.*\//,'')}] ERROR: #{m}" }
exit(-1);
end
def usage(*msg)
msg.each { |m| $stderr.puts "ERROR: #{m}" }
$stderr.puts <<-USAGE
Usage: #{$0.sub(/.*\//,'')} [sink]
Switch sound playback device for ALSA/pulseaudio
[sink] Specify sink number to use (see 'pacmd list-sinks')
USAGE
exit -1;
end
def parseArgs
opt = Hash.new
loop {
if (arg=ARGV.shift)==nil then break
elsif arg == '-h' then usage
elsif arg == '-?' then usage
#elsif arg == '-arg' then opt[:arg] = true
elsif arg =~ /^(\d)$/ then opt[:sink] = arg.to_i
else
usage("Unknown arg [#{arg}]")
end
}
opt
end
# Unfortunately you can't return or break from the yield without leaving
# the pipe open, maybe use some sort of ensure and figure out how to close?
def pipe(cmd)
# This is leaving files open
#IO.popen(cmd.join(' ')).each { |l|
a = `#{cmd.join(' ')}`
ret = $?
a.split("\n").each { |l|
yield l
}
$?
end
def getSinks(ins=false)
cmd = PACMD.dup
cmd.push(ins ? 'list-sink-inputs' : 'list-sinks')
curr = nil
sinks = Array.new
pipe(cmd) { |l|
next unless l=~/\s*(\*)?\s*index:\s+(\d+)/
i = $2.to_i
sinks.push(i)
curr = i if $1
}
return sinks,curr
end
##################################################
# Main code
##################################################
def main
opt = parseArgs
sinks,curr = getSinks
usage("No sinks found?") if sinks.empty?
usage("Only one sink found") if sinks.size==1
if opt[:sink]
usage("Unknown sink [#{opt[:sink]}] (out of #{sinks.join(' ')})") unless sinks.index(opt[:sink])
else
# Find next sink after curr
opt[:sink] = sinks[0]
sinks.each { |s|
next unless s>curr
opt[:sink] = s
break
}
end
# Set default sink
## For some reason this doesn't change the behavior of new apps.
puts "Set sink: #{opt[:sink]}"
system("#{PACMD} set-default-sink #{opt[:sink]} > /dev/null")
usage("Couldn't set default sink [#{opt[:sink]}]") unless $?==0
# And move all sink-inputs to the new sink
ins,ignore = getSinks(true)
ins.each { |i|
puts "Move playback #{i} to sink #{opt[:sink]}"
system("#{PACMD} move-sink-input #{i} #{opt[:sink]} > /dev/null")
usage("Couldn't move playback #{i} to sink [#{opt[:sink]}]") unless $?==0
}
end
main
Мой способ изменить устройство вывода звука по умолчанию состоял в том, чтобы написать скрипт Python:
import os
os.system(f"pacmd set-default-sink {next(a for a in list(os.popen('pacmd list-sinks | grep index').readlines()) if '*' not in a).split(': ')[1][0]}")
Я думаю, вы можете изменить его для ваших нужд.
Я думаю, что есть еще один вариант, о котором стоит упомянуть, и он доступен на официальной странице часто задаваемых вопросов о PulseAudio по адресу https://freedesktop.org/. Под следующим заголовком:
Как переключить звуковую карту по умолчанию, перемещая все приложения?
Для этого они предоставляют следующий скрипт:
#/bin/bash
# paswitch 2011-02-02 by Ng Oon-Ee <ngoonee@gmail.com>
# I can't remember where I found this script, can't locate the original author.
# Please inform me if you know, so that I can give proper attribution.
# CHANGES: Added auto-move all inputs to new default sound card.
# WAS: Pulse Audio Sound Card Switcher v1.0 2010-01-13
# Switches between soundcards when run. All streams are moved to the new default sound-card.
# $totalsc: Number of sound cards available
totalsc=$(pacmd "list-sinks" | grep card: | wc -l) # total of sound cards: $totalsc
if [ $totalsc -le 1 ]; then # Check whether there are actually multiple cards available
notify-send -u critical -t 5000 "Nothing to switch, system only has one sound card."
exit
fi
# $scindex: The Pulseaudio index of the current default sound card
scindex=$(pacmd list-sinks | awk '$1 == "*" && $2 == "index:" {print $3}')
# $cards: A list of card Pulseaudio indexes
cards=$(pacmd list-sinks | sed 's|*||' | awk '$1 == "index:" {print $2}')
PICKNEXTCARD=1 # Is true when the previous card is default
count=0 # count of number of iterations
for CARD in $cards; do
if [ $PICKNEXTCARD == 1 ]; then
# $nextsc: The pulseaudio index of the next sound card (to be switched to)
nextsc=$CARD
PICKNEXTCARD=0
# $nextind: The numerical index (1 to totalsc) of the next card
nextind=$count
fi
if [ $CARD == $scindex ]; then # Choose the next card as default
PICKNEXTCARD=1
fi
count=$((count+1))
done
pacmd "set-default-sink $nextsc" # switch default sound card to next
# $inputs: A list of currently playing inputs
inputs=$(pacmd list-sink-inputs | awk '$1 == "index:" {print $2}')
for INPUT in $inputs; do # Move all current inputs to the new default sound card
pacmd move-sink-input $INPUT $nextsc
done
# $nextscdec: The device.description of the new default sound card
# NOTE: This is the most likely thing to break in future, the awk lines may need playing around with
nextscdesc=$(pacmd list-sinks | awk '$1 == "device.description" {print substr($0,5+length($1 $2))}' \
| sed 's|"||g' | awk -F"," 'NR==v1{print$0}' v1=$((nextind+1)))
notify-send "Default sound-card changed to $nextscdesc"
exit
# Below text was from original author and remains unaltered
# CC BY - creative commons
# Thanks God for help :) and guys lhunath, geirha, Tramp and others from irc #bash on freenode.net
Я добавлю к этому миксу мой маленький сценарий рыбьей раковины.
#!/usr/bin/env fish
set sinks (pacmd list-sinks | pcregrep -Mo '(?<=index: )\d|(?<=state: )\w+' | sed 'N;s/\n/:/')
set switchtnow false
while true
for sink in $sinks
if $switchtnow
set i (echo "$sink" | grep -Po '\d')
pacmd set-default-sink "$i"
return
end
set match (echo $sink | grep -o RUNNING)
if [ "$match" = "RUNNING" ]
set switchtnow true
end
end
end
Вот скрипт, созданный с помощью ChatGPT для переключения аудиоустройства по умолчанию с помощью rofi...
#!/usr/bin/env python
import subprocess
import pulsectl
def get_devices_with_input_and_output():
with pulsectl.Pulse('set-default-sink') as pulse:
sinks = pulse.sink_list()
with pulsectl.Pulse('set-default-source') as pulse:
sources = pulse.source_list()
devices_with_input_and_output = []
for sink in sinks:
for source in sources:
if sink.description == source.description:
devices_with_input_and_output.append((sink, source))
return devices_with_input_and_output
def switch_default_audio_device(device_index):
devices = get_devices_with_input_and_output()
if device_index >= 0 and device_index < len(devices):
input_device = devices[device_index][1]
with pulsectl.Pulse('set-default-source') as pulse:
pulse.source_default_set(input_device)
output_device = devices[device_index][0]
with pulsectl.Pulse('set-default-sink') as pulse:
pulse.sink_default_set(output_device)
def select_device(devices):
device_list = [f"{device[0].description}" for device in devices]
rofi_cmd = ['rofi', '-dmenu', '-p', 'Select audio device', '-i', '-format', 'i']
p = subprocess.Popen(rofi_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf-8')
stdout, _ = p.communicate('\n'.join(device_list))
if p.returncode == 0:
try:
device_index = int(stdout.strip())
return device_index
except ValueError:
pass
return None
if __name__ == '__main__':
devices = get_devices_with_input_and_output()
if not devices:
print('No devices found with both audio input and output.')
else:
print('Devices with audio input and output:')
for i, (sink, source) in enumerate(devices):
print(f'{i + 1}. {sink.description}')
device_index = select_device(devices)
# output_device_index = select_device(devices)
switch_default_audio_device(device_index)
print('Default audio devices switched successfully.')