私が知っている限り、を使用してオーディオデバイスのプロファイルを選択できますpactl set-card-profile CARD PROFILE
。ただし、これはpactl
PulseAudioユーティリティなので、不要なレイヤーを追加することを意味し、もちろんある程度PulseAudioをインストールする必要があります。
PipeWireを使用してオーディオデバイスプロファイルを設定する基本的な方法が何であるかを知りたいです。
pw-cli
私に必要なようなPipeWireというユーティリティを見つけましたが、それを正しく使用する方法はわかりません。 pw-cli [オプション] [コマンド] -h、--helpこのヘルプを表示--versionバージョンを表示-d、--daemonデーモンで始まる(デフォルトはfalse)-r、--remoteリモートデーモン名
Available commands:
help Show this help
load-module Load a module. <module-name> [<module-arguments>]
unload-module Unload a module. <module-var>
connect Connect to a remote. [<remote-name>]
disconnect Disconnect from a remote. [<remote-var>]
list-remotes List connected remotes.
switch-remote Switch between current remotes. [<remote-var>]
list-objects List objects or current remote. [<interface>]
info Get info about an object. <object-id>|all
create-device Create a device from a factory. <factory-name> [<properties>]
create-node Create a node from a factory. <factory-name> [<properties>]
destroy Destroy a global object. <object-id>
create-link Create a link between nodes. <node-id> <port-id> <node-id> <port-id> [<properties>]
export-node Export a local node to the current remote. <node-id> [remote-var]
enum-params Enumerate params of an object <object-id> <param-id>
set-param Set param of an object <object-id> <param-id> <param-json>
permissions Set permissions for a client <client-id> <object> <permission>
get-permissions Get permissions of a client <client-id>
send-command Send a command <object-id>
dump Dump objects in ways that are cleaner for humans to understand [short|deep|resolve|notype] [-sdrt] [all|Core|Module|Device|Node|Port|Factory|Client|Link|Session|Endpoint|EndpointStream|<id>]
quit Quit
エラーなしenum-params
で呼び出すことができますが、出力は非常に秘密であり、解析するのは困難です。ただし、どちらのコマンドも予想される出力を提供します。Route
Profile
pw-cli enum-params 45 Route
pw-cli enum-params 45 Profile
pw-dump
出力形式がJSONなので、データを取得する方が意味があるようです。必要なデータを渡すために必要get_audio_devices
なすべての情報を取得するために、いくつかのヘルパー関数(、、、、、、、、、、、)を作成し、それをより合理的にする2つの関数(および)をさらに作成しました。使用。get_object_by_id
get_device_by_name
get_active_profiles
get_active_routes
get_all_profiles
get_all_routes
get_nodes_for_device_by_id
pw-cli set-param
set_profile
set_route
pw-cli
私の機能は次のとおりです。
#!/bin/bash
# Function: get_audio_devices
# Description: Get all audio devices as a JSON array of objects
function get_audio_devices() {
pw-dump | jq -r '.[] | select(.type == "PipeWire:Interface:Device" and .info.props."media.class" == "Audio/Device")'
}
# Function: get_object_by_id
# Description: Get a object by object ID as a JSON object
# Parameters:
# - $1: The object ID of the device, node, module, factory, port, link or whatever
function get_object_by_id() {
local object_id="$1"
pw-dump | jq -r ".[] | select(.id == $object_id)"
}
# Function: get_device_by_name
# Description: Get a device by name as a JSON object
# Parameters:
# - $1: The name of the device
function get_device_by_name() {
local device_name="$1"
pw-dump | jq -r ".[] | select(.info.props.\"device.name\" == \"$device_name\")"
}
# Function: get_active_profiles
# Description: Get active profiles for a device by object ID as a JSON array of objects
# Parameters:
# - $1: The object ID of the device
function get_active_profiles() {
local device_object_id="$1"
pw-dump | jq -r ".[] | select(.id == $device_object_id) | .info.params.Profile"
}
# Function: get_active_routes
# Description: Get active routes for all nodes of the active profile(s) of a device by object ID as a JSON array of objects
# Parameters:
# - $1: The object ID of the device
function get_active_routes() {
local device_object_id="$1"
pw-dump | jq -r ".[] | select(.id == $device_object_id) | .info.params.Route"
}
# Function: get_all_profiles
# Description: Get all profiles for a device by object ID as a JSON array of objects
# Parameters:
# - $1: The object ID of the device
function get_all_profiles() {
local device_object_id="$1"
pw-dump | jq -r ".[] | select(.id == $device_object_id) | .info.params.EnumProfile"
}
# Function: get_all_routes
# Description: Get all routes for all nodes of a device by object ID as a JSON array of objects
# Parameters:
# - $1: The object ID of the device
function get_all_routes() {
local device_object_id="$1"
pw-dump | jq -r ".[] | select(.id == $device_object_id) | .info.params.EnumRoute"
}
# Function: get_nodes_for_device_by_id
# Description: Get all nodes for a device by object ID as a JSON array of objects
# Parameters:
# - $1: The object ID of the device
function get_nodes_for_device_by_id() {
local device_object_id="$1"
pw-dump | jq -r --argjson dev_id "$device_object_id" '[.[] | select(.type == "PipeWire:Interface:Node" and .info.props."device.id" == $dev_id)]'
#pw-dump | jq -r '[.[] | select(.type == "PipeWire:Interface:Node" and .info.props."device.id" == "'"$device_object_id"'")]'
}
# Function: set_route
# Description: Set route for a given node by object ID
# Parameters:
# - $1: The object ID of the node
# - $2: The index of the new route
function set_route() {
local node_object_id="$1"
local route_index="$2"
local node=$(get_object_by_id $node_object_id)
local device_object_id=$(echo "$node" | jq -r '.info.props["device.id"]')
# Get available route "templates" for the device's active profile(s)
routes=$(get_all_routes $device_object_id)
# Get the first route template (edit: using the fourth because the first 3 are for input rather than output)
first_route_template=$(echo "$routes" | jq '.[3]')
# Create a Routes entry for the given node based on the given template and save as new JSON object
route_to_set=$(echo "$first_route_template")
# Set the "route.hw-mute" property to false beacuase I have no clu how to find out the right value
route_to_set=$(echo "$route_to_set" | jq '.info += ["route.hw-mute", "true"]')
# Set the "route.hw-volume" property to false beacuase I have no clu how to find out the right value
route_to_set=$(echo "$route_to_set" | jq '.info += ["route.hw-volume", "true"]')
# Calculate the length of the "info" array and set the first element accordingly
info_length=$(echo "$route_to_set" | jq '.info | length')
route_to_set=$(echo "$route_to_set" | jq ".info[0] = ($info_length - 1) / 2")
# Get the index of the node of which we want to change the route
node_index=$(echo "$node" | jq -r '.info.props["card.profile.device"]')
# Set device property
route_to_set=$(echo "$route_to_set" | jq ". + { \"device\": $node_index }")
# Gather values for the properties of the "props" section
mute=$(echo "$node" | jq -r '.info.params.Props[0].mute')
channel_volumes=$(echo "$node" | jq -r '.info.params.Props[0].channelVolumes')
volume_base=$(echo "$node" | jq -r '.info.params.Props[0].volume')
volume_step=0.000015 # No clue how to get the correct value
channel_map=$(echo "$node" | jq -r '.info.params.Props[0].channelMap')
soft_volumes=$(echo "$node" | jq -r '.info.params.Props[0].softVolumes')
latency_offset=$(echo "$node" | jq -r '.info.params.Props[1].latencyOffsetNsec')
# Set the properties in the "props" section
route_to_set=$(echo "$route_to_set" | jq "
.props += {
\"mute\": $mute,
\"channelVolumes\": $channel_volumes,
\"volumeBase\": $volume_base,
\"volumeStep\": $volume_step,
\"channelMap\": $channel_map,
\"softVolumes\": $soft_volumes,
\"latencyOffsetNsec\": $latency_offset
}"
)
# Get active profile
profiles=$(get_active_profiles $device_object_id)
first_active_profile=$(echo "$profiles" | jq '.[0]')
# Get profile index
first_active_profile_index=$(echo "$first_active_profile" | jq -r '.index')
route_to_set=$(echo "$route_to_set" | jq ". + { \"profile\": $first_active_profile_index }")
# Add the "save" property to the "route_to_set" object
route_to_set=$(echo "$route_to_set" | jq '. + { "save": false }')
# Get active routes
old_active_routes="$(get_active_routes $device_object_id)"
# Get route index
route_index=$(echo "$old_active_routes" | jq -r "map(.device == $node_index) | index(true)")
# Create a new routes array where the route we want to set replaces the old one
updated_active_routes=$(echo "$old_active_routes" | jq ".[$route_index] = $route_to_set")
# Check diff between old and new routes array
#file1=/tmp/updated_active_routes && file2=/tmp/old_active_routes && echo "$updated_active_routes" > "$file1" && echo "$old_active_routes" > "$file2" && meld "$file1" "$file2" ; rm "$file1" "$file2"
# Set the updated routes
pw-cli set-param $device_object_id Route "$updated_active_routes"
}
# Function: set_profile
# Description: Set route for a given device by object ID
# Parameters:
# - $1: The object ID of the device
# - $2: The index of the new profile
function set_profile() {
local device_object_id="$1"
local profile_index="$2"
# Get available profile "templates" for device with object ID 78
profiles="$(get_all_profiles $device_object_id)"
# Get desired profile template
profile_template=$(echo "$profiles" | jq ".[] | select(.index == $PROFILE_INDEX)")
# Add the "save" property and save as new JSON object
profile_to_set=$(echo "$profile_template" | jq '. + { "save": false }')
# Set the new profile(s)
pw-cli set-param $device_object_id Profile "[ $profile_to_set ]"
}
# Example usage:
# get_audio_devices
# get_object_by_id 78
# get_device_by_name alsa_card.pci-0000_00_1f.3
# get_active_profiles 78
# get_active_routes 78
# get_all_profiles 78
# get_all_routes 78
# get_nodes_for_device_by_id 78
# set_profile 78 1
# set_route 45 0
しかし、少し推測が含まれています。私はset_profileとset_routesを使用しようとしましたが、成功しませんでした。
プロフィールを変更しようとしています。
#####################################################
# Attempt to change the profile #
#####################################################
# My audio device
DEVICE_NAME="alsa_card.pci-0000_00_1f.3"
# Index of the profile I want to use
PROFILE_INDEX=1
# Get device id
device_object_id=$(get_device_by_name "$DEVICE_NAME" | jq -r '.id')
# Set the desired profile
set_profile $device_object_id $PROFILE_INDEX
# Doesn't work and results in:
# Array: child.size 2, child.type Spa:String
# String "{"
# String ""
# String ""
# String ""
# String ""
# String ""
# String ""
# String ""
#####################################################
ルートを変更してみました。
#####################################################
# Attempt to change the route #
#####################################################
# My audio device
DEVICE_NAME="alsa_card.pci-0000_00_1f.3"
# Get device id
device_object_id=$(get_device_by_name "$DEVICE_NAME" | jq -r '.id')
# Get all nodes for the device
nodes=$(get_nodes_for_device_by_id $device_object_id)
# Error if there are no nodes
if [ "$nodes" == "[]" ]; then
echo "There are no nodes for the active profile(s) of device with name '$DEVICE_NAME' (object ID: $device_object_id)" >&2
exit 1
fi
# Get first sink node of current profile
first_sink_node_object_id=$(echo "$nodes" | jq '.[] | select(.info.props."media.class" == "Audio/Sink")'| jq -r '.id')
# Get first route output route (it's not guaranteed that this route is available for that specific node and profile)
first_output_route_index=$(get_all_routes $device_object_id | jq '.[] | select(.direction == "Output") | .index' | head -n 1)
# Set the route
set_route $first_sink_node_object_id $first_output_route_index
# Doesn't work and results in:
# Array: child.size 2, child.type Spa:String
# String "{"
# String ""
# String ""
# String ""
# String ""
# String ""
# String ""
# String ""
#####################################################
それでは、私が何をしようとしたのかを詳しく説明します。署名はset-params
次のとおりです。
pw-cli set-param <object-id> <param-id> <param-json>
それぞれobject-id
デバイスIDと。param-id
Route
Profile
最初の問題は、設定したいパス/プロファイルのIDまたはインデックスのみを渡すことができず、JSONオブジェクト全体を渡す必要があり、どのように見えるかはわかりません。
すべてのデバイスに可能なプロファイルとパスのセットがあるようです。これらはそれぞれ.info.params.EnumProfile
およびからオブジェクトの配列として見つけることができます.info.params.EnumRoute
。私が知っている限り、これらのオブジェクトはテンプレートです。アクティブなプロファイルとパスはそれぞれ.info.params.Profile
に保存されます.info.params.Route
。一見するとテンプレートと同じですが、追加のプロパティがあります。の各オブジェクトには、.info.params.Profile
trueまたはfalseのいずれかを指定できるブール値「save」という追加の属性があります。.info.params.Route
配列の悪い点は、追加の属性device、profile、およびsaveがあり、名前付き属性の1つに属性、、、、およびオブジェクトを持つ属性である新しい属性info
「props」があることです。これらのプロパティの正しい値をどこで取得できるかわかりません。これらのプロパティのいくつかは、接続された(シンク/ソース)ノード(たとえば、および)から取得できるように見えますが、場合によってはどの値を設定する必要があるかを推測しています。mute
channelVolumes
volumeBase
volumeStep
channelMap
softVolumes
latencyOffsetNsec
channelMap
softVolumes
.info.params.Profile
もう一つの問題は、なぜ配列なのか理解できないことです。これは複数のアクティブプロファイルがある可能性があることを意味しますが、デバイスがそれをサポートしているかどうかはどうすればわかりますか?
どんな助けでも大変感謝します。
答え1
問題は「PipeWireを使用してオーディオデバイスプロファイルを設定する基本的な方法は何ですか?」
pw-cli
そうだあまりにも関与する私の好みに適しています(IMHOオーディオリスニングは脳の流出にならないでください)。取り扱いが簡単な別のツールがあります。まさにwpctl
(ワイヤ配管工制御)ということです。明らかにそれはDebianの一部ですが(Debian)に含まれているので、私の本ではwireplumber
「デフォルト」と見なされます。wireplumber
apt install
pipewire
私のシステムはとても簡単です。 Raspberry PiとBluetoothスピーカーです。 CLI でボリュームを簡単に設定できます。
wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+
マニュアルはなく、ヘルプオプションwpctl
しかありませんが、-h
Archのスタッフは1つを作成しました。とても良い参考資料。