From 5fb25536c716da153ba07598a0862d7ce00ed901 Mon Sep 17 00:00:00 2001 From: j1nx Date: Sat, 3 Feb 2024 19:27:41 +0000 Subject: [PATCH] [All] Revert certain pipewire tweaks (let's start over) --- .../etc/pipewire/client-rt.conf | 7 +- .../rootfs-overlay/etc/pipewire/client.conf | 6 +- .../etc/pipewire/filter-chain.conf | 63 - .../etc/pipewire/filter-chain/demonic.conf | 64 -- .../filter-chain/sink-dolby-surround.conf | 47 - .../etc/pipewire/filter-chain/sink-eq6.conf | 70 -- .../pipewire/filter-chain/sink-make-LFE.conf | 56 - .../filter-chain/sink-matrix-spatialiser.conf | 42 - .../pipewire/filter-chain/sink-mix-FL-FR.conf | 40 - .../sink-virtual-surround-5.1-kemar.conf | 180 --- .../sink-virtual-surround-7.1-hesuvi.conf | 104 -- .../filter-chain/source-duplicate-FL.conf | 52 - .../pipewire/filter-chain/source-rnnoise.conf | 44 - .../rootfs-overlay/etc/pipewire/jack.conf | 133 --- .../rootfs-overlay/etc/pipewire/minimal.conf | 363 ------ .../etc/pipewire/pipewire-aes67.conf | 129 --- .../etc/pipewire/pipewire-avb.conf | 73 -- .../etc/pipewire/pipewire-pulse.conf | 19 +- .../rootfs-overlay/etc/pipewire/pipewire.conf | 76 +- .../pipewire.conf.d/90-input-denoising.conf | 3 +- .../etc/wireplumber/bluetooth.conf | 91 -- .../bluetooth.lua.d/00-functions.lua | 36 - .../bluetooth.lua.d/30-bluez-midi-monitor.lua | 18 - .../bluetooth.lua.d/30-bluez-monitor.lua | 18 - .../bluetooth.lua.d/90-enable-all.lua | 2 - .../etc/wireplumber/common/00-functions.lua | 36 - .../rootfs-overlay/etc/wireplumber/main.conf | 74 -- .../wireplumber/main.lua.d/00-functions.lua | 36 - .../main.lua.d/20-default-access.lua | 19 - .../main.lua.d/30-alsa-monitor.lua | 29 - .../main.lua.d/30-libcamera-monitor.lua | 14 - .../main.lua.d/30-v4l2-monitor.lua | 14 - .../main.lua.d/40-device-defaults.lua | 85 -- .../main.lua.d/40-stream-defaults.lua | 42 - .../wireplumber/main.lua.d/50-alsa-config.lua | 4 +- .../main.lua.d/50-default-access-config.lua | 36 - .../main.lua.d/50-libcamera-config.lua | 38 - .../wireplumber/main.lua.d/50-v4l2-config.lua | 38 - .../wireplumber/main.lua.d/90-enable-all.lua | 26 - .../etc/wireplumber/policy.conf | 73 -- .../wireplumber/policy.lua.d/00-functions.lua | 36 - .../policy.lua.d/10-default-policy.lua | 98 -- .../policy.lua.d/50-endpoints-config.lua | 95 -- .../policy.lua.d/90-enable-all.lua | 1 - .../scripts/access/access-default.lua | 53 - .../scripts/access/access-portal.lua | 141 --- .../etc/wireplumber/scripts/create-item.lua | 130 --- .../etc/wireplumber/scripts/fallback-sink.lua | 93 -- .../wireplumber/scripts/intended-roles.lua | 74 -- .../scripts/monitors/alsa-midi.lua | 68 -- .../etc/wireplumber/scripts/monitors/alsa.lua | 427 ------- .../scripts/monitors/bluez-midi.lua | 187 --- .../wireplumber/scripts/monitors/bluez.lua | 428 ------- .../scripts/monitors/libcamera.lua | 174 --- .../etc/wireplumber/scripts/monitors/v4l2.lua | 165 --- .../wireplumber/scripts/policy-bluetooth.lua | 398 ------- .../scripts/policy-device-profile.lua | 246 ---- .../scripts/policy-device-routes.lua | 487 -------- .../etc/wireplumber/scripts/policy-dsp.lua | 86 -- .../scripts/policy-endpoint-client-links.lua | 218 ---- .../scripts/policy-endpoint-client.lua | 261 ----- .../scripts/policy-endpoint-device.lua | 285 ----- .../etc/wireplumber/scripts/policy-node.lua | 1012 ----------------- .../wireplumber/scripts/restore-stream.lua | 499 -------- .../etc/wireplumber/scripts/sm-objects.lua | 103 -- .../wireplumber/scripts/static-endpoints.lua | 36 - .../etc/wireplumber/scripts/suspend-node.lua | 59 - 67 files changed, 66 insertions(+), 8094 deletions(-) delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/demonic.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-dolby-surround.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-eq6.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-make-LFE.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-matrix-spatialiser.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-mix-FL-FR.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-virtual-surround-5.1-kemar.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-virtual-surround-7.1-hesuvi.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/source-duplicate-FL.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/source-rnnoise.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/jack.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/minimal.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/pipewire-aes67.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/pipewire/pipewire-avb.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/00-functions.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/30-bluez-midi-monitor.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/30-bluez-monitor.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/90-enable-all.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/common/00-functions.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/00-functions.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/20-default-access.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-alsa-monitor.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-libcamera-monitor.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-v4l2-monitor.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/40-device-defaults.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/40-stream-defaults.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-default-access-config.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-libcamera-config.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-v4l2-config.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/90-enable-all.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/policy.conf delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/00-functions.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/10-default-policy.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/50-endpoints-config.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/90-enable-all.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/access/access-default.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/access/access-portal.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/create-item.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/fallback-sink.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/intended-roles.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/alsa-midi.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/alsa.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/bluez-midi.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/bluez.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/libcamera.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/v4l2.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-bluetooth.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-device-profile.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-device-routes.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-dsp.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-client-links.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-client.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-device.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-node.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/restore-stream.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/sm-objects.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/static-endpoints.lua delete mode 100644 buildroot-external/rootfs-overlay/etc/wireplumber/scripts/suspend-node.lua diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/client-rt.conf b/buildroot-external/rootfs-overlay/etc/pipewire/client-rt.conf index 40ae8994..f327178e 100644 --- a/buildroot-external/rootfs-overlay/etc/pipewire/client-rt.conf +++ b/buildroot-external/rootfs-overlay/etc/pipewire/client-rt.conf @@ -1,4 +1,4 @@ -# Real-time Client config file for PipeWire version "0.3.81" # +# Real-time Client config file for PipeWire version "1.0.0" # # # Copy and edit this file in /etc/pipewire for system-wide changes # or in ~/.config/pipewire for local changes. @@ -75,11 +75,11 @@ context.modules = [ ] filter.properties = { - node.latency = 256/48000 + #node.latency = 1024/48000 } stream.properties = { - node.latency = 256/48000 + #node.latency = 1024/48000 #node.autoconnect = true #resample.quality = 4 #channelmix.normalize = false @@ -111,6 +111,7 @@ stream.rules = [ ] alsa.properties = { + #alsa.deny = false # ALSA params take a single value, an array [] of values # or a range { min=.. max=... } #alsa.access = [ MMAP_INTERLEAVED MMAP_NONINTERLEAVED RW_INTERLEAVED RW_NONINTERLEAVED ] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/client.conf b/buildroot-external/rootfs-overlay/etc/pipewire/client.conf index 1625a39e..c9d273d6 100644 --- a/buildroot-external/rootfs-overlay/etc/pipewire/client.conf +++ b/buildroot-external/rootfs-overlay/etc/pipewire/client.conf @@ -1,4 +1,4 @@ -# Client config file for PipeWire version "0.3.78" # +# Client config file for PipeWire version "1.0.0" # # # Copy and edit this file in /etc/pipewire for system-wide changes # or in ~/.config/pipewire for local changes. @@ -66,11 +66,11 @@ context.modules = [ ] filter.properties = { - node.latency = 256/48000 + #node.latency = 1024/48000 } stream.properties = { - node.latency = 256/48000 + #node.latency = 1024/48000 #node.autoconnect = true #resample.quality = 4 #channelmix.normalize = false diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain.conf deleted file mode 100644 index 5681a237..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain.conf +++ /dev/null @@ -1,63 +0,0 @@ -# Filter-chain config file for PipeWire version "0.3.81" # -# -# This is a base config file for running filters. -# -# Place filter fragments in /etc/pipewire/filter-chain.conf.d/ -# for system-wide changes or in ~/.config/pipewire/filter-chain.conf.d/ -# for local changes. -# -# Run the filters with pipewire -c filter-chain.conf -# - -context.properties = { - ## Configure properties in the system. - #mem.warn-mlock = false - #mem.allow-mlock = true - #mem.mlock-all = false - log.level = 0 -} - -context.spa-libs = { - # = - # - # Used to find spa factory names. It maps an spa factory name - # regular expression to a library name that should contain - # that factory. - # - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - -context.modules = [ - #{ name = - # ( args = { = ... } ) - # ( flags = [ ( ifexists ) ( nofail ) ] ) - # ( condition = [ { = ... } ... ] ) - #} - # - # Loads a module with the given parameters. - # If ifexists is given, the module is ignored when it is not found. - # If nofail is given, module initialization failures are ignored. - # - # Uses realtime scheduling to boost the audio thread priorities - { name = libpipewire-module-rt - args = { - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = [ ifexists nofail ] - } - - # The native communication protocol. - { name = libpipewire-module-protocol-native } - - # Allows creating nodes that run in the context of the - # client. Is used by all clients that want to provide - # data to PipeWire. - { name = libpipewire-module-client-node } - - # Makes a factory for wrapping nodes in an adapter with a - # converter and resampler. - { name = libpipewire-module-adapter } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/demonic.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/demonic.conf deleted file mode 100644 index 328085f2..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/demonic.conf +++ /dev/null @@ -1,64 +0,0 @@ -# filter-chain example config file for PipeWire version "0.3.81" # -# -# Copy this file into a conf.d/ directory such as -# ~/.config/pipewire/filter-chain.conf.d/ -# -context.modules = [ - { name = libpipewire-module-filter-chain - flags = [ nofail ] - args = { - #audio.format = F32 - #audio.rate = 48000 - audio.channels = 2 - audio.position = [ FL FR ] - node.description = "Demonic example" - media.name = "Demonic example" - filter.graph = { - nodes = [ - { - name = rev - type = ladspa - plugin = revdelay_1605 - label = revdelay - control = { - "Delay Time (s)" = 2.0 - } - } - { - name = pitch - type = ladspa - plugin = am_pitchshift_1433 - label = amPitchshift - control = { - "Pitch shift" = 0.6 - } - } - { - name = rev2 - type = ladspa - plugin = g2reverb - label = G2reverb - control = { - "Reverb tail" = 0.5 - "Damping" = 0.9 - } - } - ] - links = [ - { output = "rev:Output" input = "pitch:Input" } - { output = "pitch:Output" input = "rev2:In L" } - ] - inputs = [ "rev:Input" ] - outputs = [ "rev2:Out L" ] - } - capture.props = { - node.name = "effect_input.filter-chain-demonic" - #media.class = Audio/Sink - } - playback.props = { - node.name = "effect_output.filter-chain-demonic" - #media.class = Audio/Source - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-dolby-surround.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-dolby-surround.conf deleted file mode 100644 index f5139580..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-dolby-surround.conf +++ /dev/null @@ -1,47 +0,0 @@ -# Dolby Surround encoder sink -# -# Copy this file into a conf.d/ directory such as -# ~/.config/pipewire/filter-chain.conf.d/ -# -context.modules = [ - { name = libpipewire-module-filter-chain - flags = [ nofail ] - args = { - node.description = "Dolby Surround Sink" - media.name = "Dolby Surround Sink" - filter.graph = { - nodes = [ - { - type = builtin - name = mixer - label = mixer - control = { "Gain 1" = 0.5 "Gain 2" = 0.5 } - } - { - type = ladspa - name = enc - plugin = surround_encoder_1401 - label = surroundEncoder - } - ] - links = [ - { output = "mixer:Out" input = "enc:S" } - ] - inputs = [ "enc:L" "enc:R" "enc:C" null "mixer:In 1" "mixer:In 2" ] - outputs = [ "enc:Lt" "enc:Rt" ] - } - capture.props = { - node.name = "effect_input.dolby_surround" - media.class = Audio/Sink - audio.channels = 6 - audio.position = [ FL FR FC LFE SL SR ] - } - playback.props = { - node.name = "effect_output.dolby_surround" - node.passive = true - audio.channels = 2 - audio.position = [ FL FR ] - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-eq6.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-eq6.conf deleted file mode 100644 index 4cdf21b8..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-eq6.conf +++ /dev/null @@ -1,70 +0,0 @@ -# 6 band sink equalizer -# -# Copy this file into a conf.d/ directory such as -# ~/.config/pipewire/filter-chain.conf.d/ -# -context.modules = [ - { name = libpipewire-module-filter-chain - args = { - node.description = "Equalizer Sink" - media.name = "Equalizer Sink" - filter.graph = { - nodes = [ - { - type = builtin - name = eq_band_1 - label = bq_lowshelf - control = { "Freq" = 100.0 "Q" = 1.0 "Gain" = 0.0 } - } - { - type = builtin - name = eq_band_2 - label = bq_peaking - control = { "Freq" = 100.0 "Q" = 1.0 "Gain" = 0.0 } - } - { - type = builtin - name = eq_band_3 - label = bq_peaking - control = { "Freq" = 500.0 "Q" = 1.0 "Gain" = 0.0 } - } - { - type = builtin - name = eq_band_4 - label = bq_peaking - control = { "Freq" = 2000.0 "Q" = 1.0 "Gain" = 0.0 } - } - { - type = builtin - name = eq_band_5 - label = bq_peaking - control = { "Freq" = 5000.0 "Q" = 1.0 "Gain" = 0.0 } - } - { - type = builtin - name = eq_band_6 - label = bq_highshelf - control = { "Freq" = 5000.0 "Q" = 1.0 "Gain" = 0.0 } - } - ] - links = [ - { output = "eq_band_1:Out" input = "eq_band_2:In" } - { output = "eq_band_2:Out" input = "eq_band_3:In" } - { output = "eq_band_3:Out" input = "eq_band_4:In" } - { output = "eq_band_4:Out" input = "eq_band_5:In" } - { output = "eq_band_5:Out" input = "eq_band_6:In" } - ] - } - audio.channels = 2 - audio.position = [ FL FR ] - capture.props = { - node.name = "effect_input.eq6" - media.class = Audio/Sink - } - playback.props = { - node.name = "effect_output.eq6" - node.passive = true - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-make-LFE.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-make-LFE.conf deleted file mode 100644 index 4ab770e3..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-make-LFE.conf +++ /dev/null @@ -1,56 +0,0 @@ -# An example filter chain that makes a stereo sink that mixes -# the FL and FR channels to FL, FR, LFE -# -# Copy this file into a conf.d/ directory -# -context.modules = [ - { name = libpipewire-module-filter-chain - args = { - node.description = "LFE example" - media.name = "LFE example" - filter.graph = { - nodes = [ - { name = copyIL type = builtin label = copy } - { name = copyOL type = builtin label = copy } - { name = copyIR type = builtin label = copy } - { name = copyOR type = builtin label = copy } - { - name = mix - type = builtin - label = mixer - control = { - "Gain 1" = 0.5 - "Gain 2" = 0.5 - } - } - { - type = builtin - name = lpLFE - label = bq_lowpass - control = { "Freq" = 150.0 } - } - ] - links = [ - { output = "copyIL:Out" input = "copyOL:In" } - { output = "copyIR:Out" input = "copyOR:In" } - { output = "copyIL:Out" input = "mix:In 1" } - { output = "copyIR:Out" input = "mix:In 2" } - { output = "mix:Out" input = "lpLFE:In" } - ] - inputs = [ "copyIL:In" "copyIR:In" ] - outputs = [ "copyOL:Out" "copyOR:Out" "lpLFE:Out"] - } - capture.props = { - node.name = "input_lfe" - audio.position = [ FL FR ] - media.class = "Audio/Sink" - } - playback.props = { - node.name = "output_lfe" - audio.position = [ FL FR LFE ] - stream.dont-remix = true - node.passive = true - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-matrix-spatialiser.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-matrix-spatialiser.conf deleted file mode 100644 index b2b66eb7..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-matrix-spatialiser.conf +++ /dev/null @@ -1,42 +0,0 @@ -# Matrix Spatialiser sink -# -# Copy this file into a conf.d/ directory such as -# ~/.config/pipewire/filter-chain.conf.d/ -# -# ( Jean-Philippe Guillemin ) -# - -context.modules = [ - { name = libpipewire-module-filter-chain - flags = [ nofail ] - args = { - node.description = "Matrix Spatialiser" - media.name = "Matrix Spatialiser" - filter.graph = { - nodes = [ - { - type = ladspa - name = matrix - plugin = matrix_spatialiser_1422 - label = matrixSpatialiser - control = { - "Width" = 80 - } - } - ] - inputs = [ "matrix:Input L" "matrix:Input R" ] - outputs = [ "matrix:Output L" "matrix:Output R" ] - } - audio.channels = 2 - audio.position = [ FL FR ] - capture.props = { - node.name = "effect_input.matrix_spatialiser" - media.class = Audio/Sink - } - playback.props = { - node.name = "effect_output.matrix_spatialiser" - node.passive = true - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-mix-FL-FR.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-mix-FL-FR.conf deleted file mode 100644 index 8288fad5..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-mix-FL-FR.conf +++ /dev/null @@ -1,40 +0,0 @@ -# An example filter chain that makes a stereo sink that mixes -# the FL and FR channels to a single FL channel -# -# Copy this file into a conf.d/ directory such as -# ~/.config/pipewire/filter-chain.conf.d/ -# -context.modules = [ - { name = libpipewire-module-filter-chain - args = { - node.description = "Mix example" - media.name = "Mix example" - filter.graph = { - nodes = [ - { - name = mix - type = builtin - label = mixer - control = { - "Gain 1" = 0.5 - "Gain 2" = 0.5 - } - } - ] - inputs = [ "mix:In 1" "mix:In 2" ] - outputs = [ "mix:Out" ] - } - capture.props = { - node.name = "mix_input.mix-FL-FR-to-FL" - audio.position = [ FL FR ] - media.class = "Audio/Sink" - } - playback.props = { - node.name = "mix_output.mix-FL-FR-to-FL" - audio.position = [ FL ] - stream.dont-remix = true - node.passive = true - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-virtual-surround-5.1-kemar.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-virtual-surround-5.1-kemar.conf deleted file mode 100644 index ee3333d4..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-virtual-surround-5.1-kemar.conf +++ /dev/null @@ -1,180 +0,0 @@ -# Convolver sink -# -# Copy this file into a conf.d/ directory such as -# ~/.config/pipewire/filter-chain.conf.d/ -# -# Adjust the paths to the convolver files to match your system -# -context.modules = [ - { name = libpipewire-module-filter-chain - flags = [ nofail ] - args = { - node.description = "Virtual Surround Sink" - media.name = "Virtual Surround Sink" - filter.graph = { - nodes = [ - { - type = builtin - label = convolver - name = convFL_L - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 0 - } - } - { - type = builtin - label = convolver - name = convFL_R - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 1 - } - } - { - type = builtin - label = convolver - name = convFR_L - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 1 - } - } - { - type = builtin - label = convolver - name = convFR_R - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 0 - } - } - { - type = builtin - label = convolver - name = convFC - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 2 - } - } - { - type = builtin - label = convolver - name = convLFE - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 3 - } - } - { - type = builtin - label = convolver - name = convSL_L - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 4 - } - } - { - type = builtin - label = convolver - name = convSL_R - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 5 - } - } - { - type = builtin - label = convolver - name = convSR_L - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 5 - } - } - { - type = builtin - label = convolver - name = convSR_R - config = { - filename = "hrir_kemar/hrir-kemar.wav" - channel = 4 - } - } - { - type = builtin - label = mixer - name = mixL - } - { - type = builtin - label = mixer - name = mixR - } - { - type = builtin - label = copy - name = copyFL - } - { - type = builtin - label = copy - name = copyFR - } - { - type = builtin - label = copy - name = copySL - } - { - type = builtin - label = copy - name = copySR - } - ] - links = [ - { output = "copyFL:Out" input = "convFL_L:In" } - { output = "copyFL:Out" input = "convFL_R:In" } - { output = "copyFR:Out" input = "convFR_R:In" } - { output = "copyFR:Out" input = "convFR_L:In" } - - { output = "copySL:Out" input = "convSL_L:In" } - { output = "copySL:Out" input = "convSL_R:In" } - { output = "copySR:Out" input = "convSR_R:In" } - { output = "copySR:Out" input = "convSR_L:In" } - - { output = "convFL_L:Out" input = "mixL:In 1" } - { output = "convFR_L:Out" input = "mixL:In 2" } - { output = "convFC:Out" input = "mixL:In 3" } - { output = "convLFE:Out" input = "mixL:In 4" } - { output = "convSL_L:Out" input = "mixL:In 5" } - { output = "convSR_L:Out" input = "mixL:In 6" } - - { output = "convFL_R:Out" input = "mixR:In 1" } - { output = "convFR_R:Out" input = "mixR:In 2" } - { output = "convFC:Out" input = "mixR:In 3" } - { output = "convLFE:Out" input = "mixR:In 4" } - { output = "convSL_R:Out" input = "mixR:In 5" } - { output = "convSR_R:Out" input = "mixR:In 6" } - ] - inputs = [ "copyFL:In" "copyFR:In" "convFC:In" "convLFE:In" "copySL:In" "copySR:In" ] - outputs = [ "mixL:Out" "mixR:Out" ] - - } - capture.props = { - node.name = "effect_input.virtual-surround-5.1-kemar" - media.class = Audio/Sink - audio.channels = 6 - audio.position = [ FL FR FC LFE SL SR] - } - playback.props = { - node.name = "effect_output.virtual-surround-5.1-kemar" - node.passive = true - audio.channels = 2 - audio.position = [ FL FR ] - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-virtual-surround-7.1-hesuvi.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-virtual-surround-7.1-hesuvi.conf deleted file mode 100644 index 3630c23a..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/sink-virtual-surround-7.1-hesuvi.conf +++ /dev/null @@ -1,104 +0,0 @@ -# Convolver sink -# -# Copy this file into a conf.d/ directory such as -# ~/.config/pipewire/filter-chain.conf.d/ -# -# Adjust the paths to the convolver files to match your system -# -context.modules = [ - { name = libpipewire-module-filter-chain - flags = [ nofail ] - args = { - node.description = "Virtual Surround Sink" - media.name = "Virtual Surround Sink" - filter.graph = { - nodes = [ - # duplicate inputs - { type = builtin label = copy name = copyFL } - { type = builtin label = copy name = copyFR } - { type = builtin label = copy name = copyFC } - { type = builtin label = copy name = copyRL } - { type = builtin label = copy name = copyRR } - { type = builtin label = copy name = copySL } - { type = builtin label = copy name = copySR } - { type = builtin label = copy name = copyLFE } - - # apply hrir - HeSuVi 14-channel WAV (not the *-.wav variants) (note: */44/* in HeSuVi are the same, but resampled to 44100) - { type = builtin label = convolver name = convFL_L config = { filename = "hrir_hesuvi/hrir.wav" channel = 0 } } - { type = builtin label = convolver name = convFL_R config = { filename = "hrir_hesuvi/hrir.wav" channel = 1 } } - { type = builtin label = convolver name = convSL_L config = { filename = "hrir_hesuvi/hrir.wav" channel = 2 } } - { type = builtin label = convolver name = convSL_R config = { filename = "hrir_hesuvi/hrir.wav" channel = 3 } } - { type = builtin label = convolver name = convRL_L config = { filename = "hrir_hesuvi/hrir.wav" channel = 4 } } - { type = builtin label = convolver name = convRL_R config = { filename = "hrir_hesuvi/hrir.wav" channel = 5 } } - { type = builtin label = convolver name = convFC_L config = { filename = "hrir_hesuvi/hrir.wav" channel = 6 } } - { type = builtin label = convolver name = convFR_R config = { filename = "hrir_hesuvi/hrir.wav" channel = 7 } } - { type = builtin label = convolver name = convFR_L config = { filename = "hrir_hesuvi/hrir.wav" channel = 8 } } - { type = builtin label = convolver name = convSR_R config = { filename = "hrir_hesuvi/hrir.wav" channel = 9 } } - { type = builtin label = convolver name = convSR_L config = { filename = "hrir_hesuvi/hrir.wav" channel = 10 } } - { type = builtin label = convolver name = convRR_R config = { filename = "hrir_hesuvi/hrir.wav" channel = 11 } } - { type = builtin label = convolver name = convRR_L config = { filename = "hrir_hesuvi/hrir.wav" channel = 12 } } - { type = builtin label = convolver name = convFC_R config = { filename = "hrir_hesuvi/hrir.wav" channel = 13 } } - - # treat LFE as FC - { type = builtin label = convolver name = convLFE_L config = { filename = "hrir_hesuvi/hrir.wav" channel = 6 } } - { type = builtin label = convolver name = convLFE_R config = { filename = "hrir_hesuvi/hrir.wav" channel = 13 } } - - # stereo output - { type = builtin label = mixer name = mixL } - { type = builtin label = mixer name = mixR } - ] - links = [ - # input - { output = "copyFL:Out" input="convFL_L:In" } - { output = "copyFL:Out" input="convFL_R:In" } - { output = "copySL:Out" input="convSL_L:In" } - { output = "copySL:Out" input="convSL_R:In" } - { output = "copyRL:Out" input="convRL_L:In" } - { output = "copyRL:Out" input="convRL_R:In" } - { output = "copyFC:Out" input="convFC_L:In" } - { output = "copyFR:Out" input="convFR_R:In" } - { output = "copyFR:Out" input="convFR_L:In" } - { output = "copySR:Out" input="convSR_R:In" } - { output = "copySR:Out" input="convSR_L:In" } - { output = "copyRR:Out" input="convRR_R:In" } - { output = "copyRR:Out" input="convRR_L:In" } - { output = "copyFC:Out" input="convFC_R:In" } - { output = "copyLFE:Out" input="convLFE_L:In" } - { output = "copyLFE:Out" input="convLFE_R:In" } - - # output - { output = "convFL_L:Out" input="mixL:In 1" } - { output = "convFL_R:Out" input="mixR:In 1" } - { output = "convSL_L:Out" input="mixL:In 2" } - { output = "convSL_R:Out" input="mixR:In 2" } - { output = "convRL_L:Out" input="mixL:In 3" } - { output = "convRL_R:Out" input="mixR:In 3" } - { output = "convFC_L:Out" input="mixL:In 4" } - { output = "convFC_R:Out" input="mixR:In 4" } - { output = "convFR_R:Out" input="mixR:In 5" } - { output = "convFR_L:Out" input="mixL:In 5" } - { output = "convSR_R:Out" input="mixR:In 6" } - { output = "convSR_L:Out" input="mixL:In 6" } - { output = "convRR_R:Out" input="mixR:In 7" } - { output = "convRR_L:Out" input="mixL:In 7" } - { output = "convLFE_R:Out" input="mixR:In 8" } - { output = "convLFE_L:Out" input="mixL:In 8" } - ] - inputs = [ "copyFL:In" "copyFR:In" "copyFC:In" "copyLFE:In" "copyRL:In" "copyRR:In", "copySL:In", "copySR:In" ] - outputs = [ "mixL:Out" "mixR:Out" ] - } - capture.props = { - node.name = "effect_input.virtual-surround-7.1-hesuvi" - media.class = Audio/Sink - audio.channels = 8 - audio.position = [ FL FR FC LFE RL RR SL SR ] - } - playback.props = { - node.name = "effect_output.virtual-surround-7.1-hesuvi" - node.passive = true - audio.channels = 2 - audio.position = [ FL FR ] - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/source-duplicate-FL.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/source-duplicate-FL.conf deleted file mode 100644 index 7e0158f1..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/source-duplicate-FL.conf +++ /dev/null @@ -1,52 +0,0 @@ -# An example filter chain that makes a source that duplicates the FL channel -# to FL and FR. -# -# Copy this file into a conf.d/ directory such as -# ~/.config/pipewire/filter-chain.conf.d/ -# -context.modules = [ - { name = libpipewire-module-filter-chain - args = { - node.description = "Remap example" - media.name = "Remap example" - filter.graph = { - nodes = [ - { - name = copyIL - type = builtin - label = copy - } - { - name = copyOL - type = builtin - label = copy - } - { - name = copyOR - type = builtin - label = copy - } - ] - links = [ - # we can only tee from nodes, not inputs so we need - # to copy the inputs and then tee. - { output = "copyIL:Out" input = "copyOL:In" } - { output = "copyIL:Out" input = "copyOR:In" } - ] - inputs = [ "copyIL:In" ] - outputs = [ "copyOL:Out" "copyOR:Out" ] - } - capture.props = { - node.name = "remap_input.remap-FL-to-FL-FR" - audio.position = [ FL ] - stream.dont-remix = true - node.passive = true - } - playback.props = { - node.name = "remap_output.remap-FL-to-FL-FR" - audio.position = [ FL FR ] - media.class = "Audio/Source" - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/source-rnnoise.conf b/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/source-rnnoise.conf deleted file mode 100644 index f3c2c71b..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/filter-chain/source-rnnoise.conf +++ /dev/null @@ -1,44 +0,0 @@ -# Noise canceling source -# -# Copy this file into a conf.d/ directory such as -# ~/.config/pipewire/filter-chain.conf.d/ -# -# Adjust the paths to the rnnoise plugin to match your system -# -context.modules = [ - { name = libpipewire-module-filter-chain - flags = [ nofail ] - args = { - node.description = "Noise Canceling source" - media.name = "Noise Canceling source" - filter.graph = { - nodes = [ - { - type = ladspa - name = rnnoise - # The path to the plugin. The suffix .so is appended to - # this string and then the file is then located in the directories - # listed in the environment variable LADSPA_PATH or - # /usr/lib64/ladspa, /usr/lib/ladspa or the system library directory - # as a fallback. - # You might want to use an absolute path here to avoid problems. - plugin = "librnnoise_ladspa" - label = noise_suppressor_stereo - control = { - "VAD Threshold (%)" 50.0 - } - } - ] - } - audio.position = [ FL FR ] - capture.props = { - node.name = "effect_input.rnnoise" - node.passive = true - } - playback.props = { - node.name = "effect_output.rnnoise" - media.class = Audio/Source - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/jack.conf b/buildroot-external/rootfs-overlay/etc/pipewire/jack.conf deleted file mode 100644 index 9eb839f4..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/jack.conf +++ /dev/null @@ -1,133 +0,0 @@ -# JACK client config file for PipeWire version "0.3.81" # -# -# Copy and edit this file in /etc/pipewire for system-wide changes -# or in ~/.config/pipewire for local changes. -# -# It is also possible to place a file with an updated section in -# /etc/pipewire/jack.conf.d/ for system-wide changes or in -# ~/.config/pipewire/jack.conf.d/ for local changes. -# - -context.properties = { - ## Configure properties in the system. - #mem.warn-mlock = false - #mem.allow-mlock = true - #mem.mlock-all = false - log.level = 0 - - #default.clock.quantum-limit = 8192 -} - -context.spa-libs = { - # = - # - # Used to find spa factory names. It maps an spa factory name - # regular expression to a library name that should contain - # that factory. - # - support.* = support/libspa-support -} - -context.modules = [ - #{ name = - # ( args = { = ... } ) - # ( flags = [ ( ifexists ) ( nofail ) ] ) - # ( condition = [ { = ... } ... ] ) - #} - # - # Loads a module with the given parameters. - # If ifexists is given, the module is ignored when it is not found. - # If nofail is given, module initialization failures are ignored. - # - # - # Boost the data thread priority. - { name = libpipewire-module-rt - args = { - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = [ ifexists nofail ] - } - - # The native communication protocol. - { name = libpipewire-module-protocol-native } - - # Allows creating nodes that run in the context of the - # client. Is used by all clients that want to provide - # data to PipeWire. - { name = libpipewire-module-client-node } - - # Allows applications to create metadata objects. It creates - # a factory for Metadata objects. - { name = libpipewire-module-metadata } -] - -# global properties for all jack clients -jack.properties = { - #node.latency = 1024/48000 - #node.rate = 1/48000 - #node.quantum = 1024/48000 - #node.lock-quantum = true - #node.force-quantum = 0 - #jack.show-monitor = true - #jack.merge-monitor = true - #jack.show-midi = true - #jack.short-name = false - #jack.filter-name = false - #jack.filter-char = " " - # - # allow: Don't restrict self connect requests - # fail-external: Fail self connect requests to external ports only - # ignore-external: Ignore self connect requests to external ports only - # fail-all: Fail all self connect requests - # ignore-all: Ignore all self connect requests - #jack.self-connect-mode = allow - #jack.locked-process = true - #jack.default-as-system = false - #jack.fix-midi-events = true - #jack.global-buffer-size = false - #jack.max-client-ports = 768 - #jack.fill-aliases = false -} - -# client specific properties -jack.rules = [ - { matches = [ - { - # all keys must match the value. ! negates. ~ starts regex. - #client.name = "Carla" - #application.process.binary = "jack_simple_client" - #application.name = "~jack_simple_client.*" - } - ] - actions = { - update-props = { - #node.latency = 512/48000 - } - } - } - { matches = [ { application.process.binary = "jack_bufsize" } ] - actions = { - update-props = { - jack.global-buffer-size = true # quantum set globally using metadata - } - } - } - { matches = [ { application.process.binary = "qsynth" } ] - actions = { - update-props = { - node.always-process = false # makes qsynth idle - node.pause-on-idle = false # makes audio fade out when idle - node.passive = out # makes the sink and qsynth suspend - } - } - } - { matches = [ { client.name = "Mixxx" } ] - actions = { - update-props = { - jack.merge-monitor = false - } - } - } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/minimal.conf b/buildroot-external/rootfs-overlay/etc/pipewire/minimal.conf deleted file mode 100644 index 35bcd027..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/minimal.conf +++ /dev/null @@ -1,363 +0,0 @@ -# Simple daemon config file for PipeWire version "0.3.81" # -# -# Copy and edit this file in /etc/pipewire for system-wide changes -# or in ~/.config/pipewire for local changes. -# -# It is also possible to place a file with an updated section in -# /etc/pipewire/minimal.conf.d/ for system-wide changes or in -# ~/.config/pipewire/minimal.conf.d/ for local changes. -# - -context.properties = { - ## Configure properties in the system. - #library.name.system = support/libspa-support - #context.data-loop.library.name.system = support/libspa-support - #support.dbus = true - #link.max-buffers = 64 - link.max-buffers = 16 # version < 3 clients can't handle more - #mem.warn-mlock = false - #mem.allow-mlock = true - #mem.mlock-all = false - #clock.power-of-two-quantum = true - #log.level = 2 - #cpu.zero.denormals = false - - core.daemon = true # listening for socket connections - core.name = pipewire-0 # core name and socket name - - ## Properties for the DSP configuration. - default.clock.rate = 48000 - default.clock.allowed-rates = [ 16000 22050 44100 48000 88200 96000 ] - #default.clock.quantum = 1024 - #default.clock.min-quantum = 32 - #default.clock.max-quantum = 2048 - #default.clock.quantum-limit = 8192 - #default.video.width = 640 - #default.video.height = 480 - #default.video.rate.num = 25 - #default.video.rate.denom = 1 - # - settings.check-quantum = true - settings.check-rate = true - # - # These overrides are only applied when running in a vm. - vm.overrides = { - default.clock.min-quantum = 1024 - } -} - -context.spa-libs = { - # = - # - # Used to find spa factory names. It maps an spa factory name - # regular expression to a library name that should contain - # that factory. - # - audio.convert.* = audioconvert/libspa-audioconvert - api.alsa.* = alsa/libspa-alsa - support.* = support/libspa-support -} - -context.modules = [ - #{ name = - # ( args = { = ... } ) - # ( flags = [ ( ifexists ) ( nofail ) ] ) - # ( condition = [ { = ... } ... ] ) - #} - # - # Loads a module with the given parameters. - # If ifexists is given, the module is ignored when it is not found. - # If nofail is given, module initialization failures are ignored. - # - - # Uses realtime scheduling to boost the audio thread priorities. This uses - # RTKit if the user doesn't have permission to use regular realtime - # scheduling. - { name = libpipewire-module-rt - args = { - nice.level = -11 - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = [ ifexists nofail ] - } - - # The native communication protocol. - { name = libpipewire-module-protocol-native } - - # The profile module. Allows application to access profiler - # and performance data. It provides an interface that is used - # by pw-top and pw-profiler. - { name = libpipewire-module-profiler } - - # Allows applications to create metadata objects. It creates - # a factory for Metadata objects. - { name = libpipewire-module-metadata } - - # Creates a factory for making nodes that run in the - # context of the PipeWire server. - { name = libpipewire-module-spa-node-factory } - - # Allows creating nodes that run in the context of the - # client. Is used by all clients that want to provide - # data to PipeWire. - { name = libpipewire-module-client-node } - - # The access module can perform access checks and block - # new clients. - { name = libpipewire-module-access - args = { - # access.allowed to list an array of paths of allowed - # apps. - #access.allowed = [ - # /usr/bin/pipewire-media-session - #] - - # An array of rejected paths. - #access.rejected = [ ] - - # An array of paths with restricted access. - #access.restricted = [ ] - - # Anything not in the above lists gets assigned the - # access.force permission. - #access.force = flatpak - } - } - - # Makes a factory for wrapping nodes in an adapter with a - # converter and resampler. - { name = libpipewire-module-adapter } - - # Makes a factory for creating links between ports. - { name = libpipewire-module-link-factory } -] - -context.objects = [ - #{ factory = - # ( args = { = ... } ) - # ( flags = [ ( nofail ) ] ) - # ( condition = [ { = ... } ... ] ) - #} - # - # Creates an object from a PipeWire factory with the given parameters. - # If nofail is given, errors are ignored (and no object is created). - # - #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc node.description = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } } - #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags = [ nofail ] } - #{ factory = spa-device-factory args = { factory.name = api.alsa.enum.udev } } - #{ factory = spa-node-factory args = { factory.name = api.alsa.seq.bridge node.name = Internal-MIDI-Bridge } } - #{ factory = adapter args = { factory.name = audiotestsrc node.name = my-test node.description = audiotestsrc } } - #{ factory = spa-node-factory args = { factory.name = api.vulkan.compute.source node.name = my-compute-source } } - - # Make a default metadata store - { factory = metadata - args = { - metadata.name = default - # metadata.values = [ - # { key = default.audio.sink value = { name = somesink } } - # { key = default.audio.source value = { name = somesource } } - # ] - } - } - - # A default dummy driver. This handles nodes marked with the "node.always-driver" - # property when no other driver is currently active. JACK clients need this. - { factory = spa-node-factory - args = { - factory.name = support.node.driver - node.name = Dummy-Driver - node.group = pipewire.dummy - priority.driver = 20000 - } - } - { factory = spa-node-factory - args = { - factory.name = support.node.driver - node.name = Freewheel-Driver - priority.driver = 19000 - node.group = pipewire.freewheel - node.freewheel = true - } - } - - # This creates a single PCM source device for the given - # alsa device path hw:0. You can change source to sink - # to make a sink in the same way. - { factory = adapter - args = { - factory.name = api.alsa.pcm.source - node.name = "system" - node.description = "system" - media.class = "Audio/Source" - api.alsa.path = "hw:0" - #api.alsa.period-size = 0 - #api.alsa.period-num = 0 - #api.alsa.headroom = 0 - #api.alsa.start-delay = 0 - #api.alsa.disable-mmap = false - #api.alsa.disable-batch = false - #api.alsa.use-chmap = false - #api.alsa.multirate = true - #latency.internal.rate = 0 - #latency.internal.ns = 0 - #clock.name = api.alsa.0 - node.suspend-on-idle = true - #audio.format = "S32" - #audio.rate = 48000 - #audio.allowed-rates = [ ] - #audio.channels = 4 - #audio.position = [ FL FR RL RR ] - #resample.quality = 4 - resample.disable = true - #monitor.channel-volumes = false - #channelmix.normalize = false - #channelmix.mix-lfe = true - #channelmix.upmix = true - #channelmix.upmix-method = psd # none, simple - #channelmix.lfe-cutoff = 150 - #channelmix.fc-cutoff = 12000 - #channelmix.rear-delay = 12.0 - #channelmix.stereo-widen = 0.0 - #channelmix.hilbert-taps = 0 - channelmix.disable = true - #dither.noise = 0 - #node.param.Props = { - # params = [ - # audio.channels 6 - # ] - #} - adapter.auto-port-config = { - mode = dsp - monitor = false - control = false - position = unknown # aux, preserve - } - #node.param.PortConfig = { - # direction = Output - # mode = dsp - # format = { - # mediaType = audio - # mediaSubtype = raw - # format = F32 - # rate = 48000 - # channels = 4 - # position = [ FL FR RL RR ] - # } - #} - } - } - { factory = adapter - args = { - factory.name = api.alsa.pcm.sink - node.name = "system" - node.description = "system" - media.class = "Audio/Sink" - api.alsa.path = "hw:0" - #api.alsa.period-size = 0 - #api.alsa.period-num = 0 - #api.alsa.headroom = 0 - #api.alsa.start-delay = 0 - #api.alsa.disable-mmap = false - #api.alsa.disable-batch = false - #api.alsa.use-chmap = false - #api.alsa.multirate = true - #latency.internal.rate = 0 - #latency.internal.ns = 0 - #clock.name = api.alsa.0 - node.suspend-on-idle = true - #audio.format = "S32" - #audio.rate = 48000 - #audio.allowed-rates = [ ] - #audio.channels = 2 - #audio.position = "FL,FR" - #resample.quality = 4 - resample.disable = true - #channelmix.normalize = false - #channelmix.mix-lfe = true - #channelmix.upmix = true - #channelmix.upmix-method = psd # none, simple - #channelmix.lfe-cutoff = 150 - #channelmix.fc-cutoff = 12000 - #channelmix.rear-delay = 12.0 - #channelmix.stereo-widen = 0.0 - #channelmix.hilbert-taps = 0 - channelmix.disable = true - #dither.noise = 0 - #node.param.Props = { - # params = [ - # audio.format S16 - # ] - #} - adapter.auto-port-config = { - mode = dsp - monitor = false - control = false - position = unknown # aux, preserve - } - #node.param.PortConfig = { - # direction = Input - # mode = dsp - # monitor = true - # format = { - # mediaType = audio - # mediaSubtype = raw - # format = F32 - # rate = 48000 - # channels = 4 - # } - #} - } - } - # This creates a new Source node. It will have input ports - # that you can link, to provide audio for this source. - #{ factory = adapter - # args = { - # factory.name = support.null-audio-sink - # node.name = "my-mic" - # node.description = "Microphone" - # media.class = "Audio/Source/Virtual" - # audio.position = "FL,FR" - # adapter.auto-port-config = { - # mode = dsp - # monitor = true - # position = preserve # unknown, aux, preserve - # } - # } - #} - # This creates a new link between the source and the virtual - # source ports. - #{ factory = link-factory - # args = { - # link.output.node = system - # link.output.port = capture_1 - # link.input.node = my-mic - # link.input.port = input_FL - # } - #} - #{ factory = link-factory - # args = { - # link.output.node = system - # link.output.port = capture_2 - # link.input.node = my-mic - # link.input.port = input_FR - # } - #} -] - -context.exec = [ - #{ path = - # ( args = "" ) - # ( condition = [ { = ... } ... ] ) - #} - # - # Execute the given program with arguments. - # - # You can optionally start the pulseaudio-server here as well - # but it is better to start it as a systemd service. - # It can be interesting to start another daemon here that listens - # on another address with the -a option (eg. -a tcp:4713). - # - ##{ path = "/usr/bin/pipewire" args = "-c pipewire-pulse.conf" } -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-aes67.conf b/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-aes67.conf deleted file mode 100644 index cdb18b95..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-aes67.conf +++ /dev/null @@ -1,129 +0,0 @@ -# AES67 config file for PipeWire version "0.3.81" # -# -# Copy and edit this file in /etc/pipewire for system-wide changes -# or in ~/.config/pipewire for local changes. -# -# It is also possible to place a file with an updated section in -# /etc/pipewire/pipewire-aes67.conf.d/ for system-wide changes or in -# ~/.config/pipewire/pipewire-aes67.conf.d/ for local changes. -# - -context.properties = { - ## Configure properties in the system. - #mem.warn-mlock = false - #mem.allow-mlock = true - #mem.mlock-all = false - #log.level = 2 - - #default.clock.quantum-limit = 8192 -} - -context.spa-libs = { - support.* = support/libspa-support -} - -context.objects = [ - # An example clock reading from /dev/ptp0. Another option is to sync the - # ptp clock to CLOCK_TAI and then set clock.id = tai. - # If both device and ID are given and available, device takes precedence - { factory = spa-node-factory - args = { - factory.name = support.node.driver - node.name = PTP0-Driver - node.group = pipewire.ptp0 - # This driver should only be used for network nodes marked with group - priority.driver = 0 - clock.name = "clock.system.ptp0" - clock.device = "/dev/ptp0" - clock.id = tai - object.export = true - } - } -] - -context.modules = [ - { name = libpipewire-module-rt - args = { - nice.level = -11 - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = [ ifexists nofail ] - } - { name = libpipewire-module-protocol-native } - { name = libpipewire-module-client-node } - { name = libpipewire-module-spa-node-factory } - { name = libpipewire-module-adapter } - { name = libpipewire-module-rtp-sap - args = { - local.ifname = eth0 - sap.ip = 239.255.255.255 - sap.port = 9875 - net.ttl = 32 - net.loop = true - - stream.rules = [ - { - matches = [ - { - rtp.session = "~.*" - } - ] - actions = { - create-stream = { - node.virtual = false - media.class = "Audio/Source" - device.api = aes67 - sess.latency.msec = 10 - node.group = pipewire.ptp0 - } - } - }, - { - matches = [ - { - sess.sap.announce = true - } - ] - actions = { - announce-stream = {} - } - } - ] - } - }, - { name = libpipewire-module-rtp-sink - args = { - local.ifname = eth0 - destination.ip = 239.69.150.243 - destination.port = 5004 - net.mtu = 1280 - net.ttl = 32 - net.loop = true - sess.min-ptime = 1 - sess.max-ptime = 1 - sess.name = "PipeWire RTP stream" - sess.media = "audio" - sess.ts-refclk = "ptp=traceable" - sess.ts-offset = 0 - sess.ptime = 1 - sess.latency.msec = 1 - sess.announce = true - audio.format = "S24BE" - audio.rate = 48000 - audio.channels = 2 - audio.position = [ FL FR ] - - stream.props = { - node.name = "rtp-sink" - media.class = "Audio/Sink" - node.virtual = false - device.api = aes67 - sess.sap.announce = true - node.always-process = true - node.group = pipewire.ptp0 - } - } - }, -] diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-avb.conf b/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-avb.conf deleted file mode 100644 index 0f172ac2..00000000 --- a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-avb.conf +++ /dev/null @@ -1,73 +0,0 @@ -# PulseAudio config file for PipeWire version "0.3.81" # -# -# Copy and edit this file in /etc/pipewire for system-wide changes -# or in ~/.config/pipewire for local changes. -# -# It is also possible to place a file with an updated section in -# /etc/pipewire/pipewire-pulse.conf.d/ for system-wide changes or in -# ~/.config/pipewire/pipewire-pulse.conf.d/ for local changes. -# - -context.properties = { - ## Configure properties in the system. - #mem.warn-mlock = false - #mem.allow-mlock = true - #mem.mlock-all = false - #log.level = 2 - - #default.clock.quantum-limit = 8192 -} - -context.spa-libs = { - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - -context.modules = [ - { name = libpipewire-module-rt - args = { - nice.level = -11 - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = [ ifexists nofail ] - } - { name = libpipewire-module-protocol-native } - { name = libpipewire-module-client-node } - { name = libpipewire-module-adapter } - { name = libpipewire-module-avb - args = { - # contents of avb.properties can also be placed here - # to have config per server. - } - } -] - -# Extra modules can be loaded here. Setup in default.pa can be moved here -context.exec = [ - #{ path = "pactl" args = "load-module module-always-sink" } -] - -stream.properties = { - #node.latency = 1024/48000 - #node.autoconnect = true - #resample.quality = 4 - #channelmix.normalize = false - #channelmix.mix-lfe = true - #channelmix.upmix = true - #channelmix.lfe-cutoff = 120 - #channelmix.fc-cutoff = 6000 - #channelmix.rear-delay = 12.0 - #channelmix.stereo-widen = 0.1 - #channelmix.hilbert-taps = 0 -} - -avb.properties = { - # the addresses this server listens on - #ifname = "eth0.2" - ifname = "enp3s0" - # These overrides are only applied when running in a vm. - vm.overrides = { - } -} diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-pulse.conf b/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-pulse.conf index e4a505ee..3ba92451 100644 --- a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-pulse.conf +++ b/buildroot-external/rootfs-overlay/etc/pipewire/pipewire-pulse.conf @@ -1,4 +1,4 @@ -# PulseAudio config file for PipeWire version "0.3.78" # +# PulseAudio config file for PipeWire version "1.0.0" # # # Copy and edit this file in /etc/pipewire for system-wide changes # or in ~/.config/pipewire for local changes. @@ -27,9 +27,12 @@ context.modules = [ { name = libpipewire-module-rt args = { nice.level = -19 + #rt.prio = 88 rt.prio = 95 #rt.time.soft = -1 #rt.time.hard = -1 + #uclamp.min = 0 + #uclamp.max = 1024 } flags = [ ifexists nofail ] } @@ -42,16 +45,6 @@ context.modules = [ args = { # contents of pulse.properties can also be placed here # to have config per server. - pulse.min.req = "256/48000" - pulse.default.req = "256/48000" - pulse.max.req = "256/48000" - pulse.min.quantum = "256/48000" - pulse.max.quantum = "256/48000" - pulse.min.req = "256/48000" - pulse.default.req = "256/48000" - pulse.max.req = "256/48000" - pulse.min.quantum = "256/48000" - pulse.max.quantum = "256/48000" } } ] @@ -75,7 +68,7 @@ pulse.cmd = [ ] stream.properties = { - node.latency = 256/48000 + #node.latency = 1024/48000 #node.autoconnect = true #resample.quality = 4 #channelmix.normalize = false @@ -126,7 +119,7 @@ pulse.rules = [ { matches = [ { - # all keys must match the value. ~ starts regex. + # all keys must match the value. ! negates. ~ starts regex. #client.name = "Firefox" #application.process.binary = "teams" #application.name = "~speech-dispatcher.*" diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire.conf b/buildroot-external/rootfs-overlay/etc/pipewire/pipewire.conf index 5dbaec21..a50062fc 100644 --- a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire.conf +++ b/buildroot-external/rootfs-overlay/etc/pipewire/pipewire.conf @@ -1,4 +1,4 @@ -# Daemon config file for PipeWire version "0.3.78" # +# Daemon config file for PipeWire version "1.0.0" # # # Copy and edit this file in /etc/pipewire for system-wide changes # or in ~/.config/pipewire for local changes. @@ -26,11 +26,11 @@ context.properties = { core.name = pipewire-0 # core name and socket name ## Properties for the DSP configuration. - default.clock.rate = 48000 - default.clock.allowed-rates = [ 48000 ] - default.clock.quantum = 256 - default.clock.min-quantum = 64 - default.clock.max-quantum = 512 + #default.clock.rate = 48000 + #default.clock.allowed-rates = [ 48000 ] + #default.clock.quantum = 1024 + #default.clock.min-quantum = 32 + #default.clock.max-quantum = 2048 #default.clock.quantum-limit = 8192 #default.video.width = 640 #default.video.height = 480 @@ -50,6 +50,8 @@ context.properties = { # enables autoloading of access module, when disabled an alternative # access module needs to be loaded. module.access = true + # enables autoloading of module-jackdbus-detect + module.jackdbus-detect = true } context.spa-libs = { @@ -88,19 +90,27 @@ context.modules = [ # Uses realtime scheduling to boost the audio thread priorities. This uses # RTKit if the user doesn't have permission to use regular realtime - # scheduling. + # scheduling. You can also clamp utilisation values to improve scheduling + # on embedded and heterogeneous systems, e.g. Arm big.LITTLE devices. { name = libpipewire-module-rt args = { - nice.level = -19 - rt.prio = 95 + nice.level = -19 + rt.prio = 95 #rt.time.soft = -1 #rt.time.hard = -1 + #uclamp.min = 0 + #uclamp.max = 1024 } flags = [ ifexists nofail ] } # The native communication protocol. - { name = libpipewire-module-protocol-native } + { name = libpipewire-module-protocol-native + args = { + # List of server Unix sockets, and optionally permissions + #sockets = [ { name = "pipewire-0" }, { name = "pipewire-0-manager" } ] + } + } # The profile module. Allows application to access profiler # and performance data. It provides an interface that is used @@ -139,21 +149,12 @@ context.modules = [ # new clients. { name = libpipewire-module-access args = { - # access.allowed to list an array of paths of allowed - # apps. - #access.allowed = [ - # /usr/bin/pipewire-media-session - #] + # Socket-specific access permissions + #access.socket = { pipewire-0 = "default", pipewire-0-manager = "unrestricted" } - # An array of rejected paths. - #access.rejected = [ ] - - # An array of paths with restricted access. - #access.restricted = [ ] - - # Anything not in the above lists gets assigned the - # access.force permission. - #access.force = flatpak + # Deprecated legacy mode (not socket-based), + # for now enabled by default if access.socket is not specified + #access.legacy = true } condition = [ { module.access = true } ] } @@ -179,6 +180,29 @@ context.modules = [ flags = [ ifexists nofail ] condition = [ { module.x11.bell = true } ] } + { name = libpipewire-module-jackdbus-detect + args = { + #jack.library = libjack.so.0 + #jack.server = null + #jack.client-name = PipeWire + #jack.connect = true + #tunnel.mode = duplex # source|sink|duplex + source.props = { + #audio.channels = 2 + #midi.ports = 1 + #audio.position = [ FL FR ] + # extra sink properties + } + sink.props = { + #audio.channels = 2 + #midi.ports = 1 + #audio.position = [ FL FR ] + # extra sink properties + } + } + flags = [ ifexists nofail ] + condition = [ { module.jackdbus-detect = true } ] + } ] context.objects = [ @@ -193,11 +217,11 @@ context.objects = [ # If condition is given, the object is created only when the context properties # all match the match rules. # - #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } } + #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc node.description = videotestsrc "Spa:Pod:Object:Param:Props:patternType" = 1 } } #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags = [ nofail ] } #{ factory = spa-device-factory args = { factory.name = api.alsa.enum.udev } } #{ factory = spa-node-factory args = { factory.name = api.alsa.seq.bridge node.name = Internal-MIDI-Bridge } } - #{ factory = adapter args = { factory.name = audiotestsrc node.name = my-test } } + #{ factory = adapter args = { factory.name = audiotestsrc node.name = my-test node.description = audiotestsrc } } #{ factory = spa-node-factory args = { factory.name = api.vulkan.compute.source node.name = my-compute-source } } # A default dummy driver. This handles nodes marked with the "node.always-driver" diff --git a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire.conf.d/90-input-denoising.conf b/buildroot-external/rootfs-overlay/etc/pipewire/pipewire.conf.d/90-input-denoising.conf index 3d2270c8..f8dcceee 100644 --- a/buildroot-external/rootfs-overlay/etc/pipewire/pipewire.conf.d/90-input-denoising.conf +++ b/buildroot-external/rootfs-overlay/etc/pipewire/pipewire.conf.d/90-input-denoising.conf @@ -10,7 +10,7 @@ context.modules = [ type = ladspa name = rnnoise plugin = /usr/lib/ladspa/librnnoise_ladspa.so - label = noise_suppressor_mono + label = noise_suppressor_stereo control = { "VAD Threshold (%)" 85.0 "VAD Grace Period (ms)" = 200 @@ -27,7 +27,6 @@ context.modules = [ playback.props = { node.name = "openvoiceos_denoised_mic" media.class = Audio/Source - audio.rate = 48000 } } } diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.conf b/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.conf deleted file mode 100644 index e4d666d1..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.conf +++ /dev/null @@ -1,91 +0,0 @@ -# WirePlumber daemon context configuration # - -context.properties = { - ## Properties to configure the PipeWire context and some modules - - application.name = "WirePlumber Bluetooth" - log.level = 2 - wireplumber.script-engine = lua-scripting - wireplumber.export-core = true - - #mem.mlock-all = false - #support.dbus = true -} - -context.spa-libs = { - # = - # - # Used to find spa factory names. It maps an spa factory name - # regular expression to a library name that should contain - # that factory. - # - api.bluez5.* = bluez5/libspa-bluez5 - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - -context.modules = [ - #{ name = - # [ args = { = ... } ] - # [ flags = [ [ ifexists ] [ nofail ] ] - #} - # - # PipeWire modules to load. - # If ifexists is given, the module is ignored when it is not found. - # If nofail is given, module initialization failures are ignored. - # - - # Uses RTKit to boost the data thread priority. Also allows clamping - # of utilisation when using the Completely Fair Scheduler on Linux. - { name = libpipewire-module-rt - args = { - nice.level = -11 - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - #uclamp.min = 0 - #uclamp.max = 1024 - } - flags = [ ifexists nofail ] - } - - # The native communication protocol. - { name = libpipewire-module-protocol-native } - - # Allows creating nodes that run in the context of the - # client. Is used by all clients that want to provide - # data to PipeWire. - { name = libpipewire-module-client-node } - - # Allows creating devices that run in the context of the - # client. Is used by the session manager. - { name = libpipewire-module-client-device } - - # Makes a factory for wrapping nodes in an adapter with a - # converter and resampler. - { name = libpipewire-module-adapter } - - # Allows applications to create metadata objects. It creates - # a factory for Metadata objects. - { name = libpipewire-module-metadata } - - # Provides factories to make session manager objects. - { name = libpipewire-module-session-manager } - - # Provides factories to make SPA node objects. - { name = libpipewire-module-spa-node-factory } -] - -wireplumber.components = [ - #{ name = , type = } - # - # WirePlumber components to load - # - - # The lua scripting engine - { name = libwireplumber-module-lua-scripting, type = module } - - # The lua configuration file - # Other components are loaded from there - { name = bluetooth.lua, type = config/lua } -] diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/00-functions.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/00-functions.lua deleted file mode 100644 index 451ad4f5..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/00-functions.lua +++ /dev/null @@ -1,36 +0,0 @@ -components = {} - -function load_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libwireplumber-module-" .. m, type = "module", args = a } - end -end - -function load_optional_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libwireplumber-module-" .. m, type = "module", args = a, optional = true } - end -end - -function load_pw_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libpipewire-module-" .. m, type = "pw_module", args = a } - end -end - -function load_script(s, a) - if not components[s] then - components[s] = { s, type = "script/lua", args = a } - end -end - -function load_monitor(s, a) - load_script("monitors/" .. s .. ".lua", a) -end - -function load_access(s, a) - load_script("access/access-" .. s .. ".lua", a) -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/30-bluez-midi-monitor.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/30-bluez-midi-monitor.lua deleted file mode 100644 index 839ec108..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/30-bluez-midi-monitor.lua +++ /dev/null @@ -1,18 +0,0 @@ -bluez_midi_monitor = {} -bluez_midi_monitor.properties = {} -bluez_midi_monitor.rules = {} - -function bluez_midi_monitor.enable() - if bluez_midi_monitor.enabled == false then - return - end - - load_monitor("bluez-midi", { - properties = bluez_midi_monitor.properties, - rules = bluez_midi_monitor.rules, - }) - - if bluez_midi_monitor.properties["with-logind"] then - load_optional_module("logind") - end -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/30-bluez-monitor.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/30-bluez-monitor.lua deleted file mode 100644 index a870aa5d..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/30-bluez-monitor.lua +++ /dev/null @@ -1,18 +0,0 @@ -bluez_monitor = {} -bluez_monitor.properties = {} -bluez_monitor.rules = {} - -function bluez_monitor.enable() - if bluez_monitor.enabled == false then - return - end - - load_monitor("bluez", { - properties = bluez_monitor.properties, - rules = bluez_monitor.rules, - }) - - if bluez_monitor.properties["with-logind"] then - load_optional_module("logind") - end -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/90-enable-all.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/90-enable-all.lua deleted file mode 100644 index efa6bf54..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/bluetooth.lua.d/90-enable-all.lua +++ /dev/null @@ -1,2 +0,0 @@ -bluez_monitor.enable() -bluez_midi_monitor.enable() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/common/00-functions.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/common/00-functions.lua deleted file mode 100644 index 451ad4f5..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/common/00-functions.lua +++ /dev/null @@ -1,36 +0,0 @@ -components = {} - -function load_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libwireplumber-module-" .. m, type = "module", args = a } - end -end - -function load_optional_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libwireplumber-module-" .. m, type = "module", args = a, optional = true } - end -end - -function load_pw_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libpipewire-module-" .. m, type = "pw_module", args = a } - end -end - -function load_script(s, a) - if not components[s] then - components[s] = { s, type = "script/lua", args = a } - end -end - -function load_monitor(s, a) - load_script("monitors/" .. s .. ".lua", a) -end - -function load_access(s, a) - load_script("access/access-" .. s .. ".lua", a) -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.conf b/buildroot-external/rootfs-overlay/etc/wireplumber/main.conf deleted file mode 100644 index e822f020..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.conf +++ /dev/null @@ -1,74 +0,0 @@ -# WirePlumber daemon context configuration # - -context.properties = { - ## Properties to configure the PipeWire context and some modules - - #application.name = WirePlumber - log.level = 2 - wireplumber.script-engine = lua-scripting - - #mem.mlock-all = false - #support.dbus = true -} - -context.spa-libs = { - # = - # - # Used to find spa factory names. It maps an spa factory name - # regular expression to a library name that should contain - # that factory. - # - api.alsa.* = alsa/libspa-alsa - api.v4l2.* = v4l2/libspa-v4l2 - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - -context.modules = [ - #{ name = - # [ args = { = ... } ] - # [ flags = [ [ ifexists ] [ nofail ] ] - #} - # - # PipeWire modules to load. - # If ifexists is given, the module is ignored when it is not found. - # If nofail is given, module initialization failures are ignored. - # - - # The native communication protocol. - { name = libpipewire-module-protocol-native } - - # Allows creating nodes that run in the context of the - # client. Is used by all clients that want to provide - # data to PipeWire. - { name = libpipewire-module-client-node } - - # Allows creating devices that run in the context of the - # client. Is used by the session manager. - { name = libpipewire-module-client-device } - - # Makes a factory for wrapping nodes in an adapter with a - # converter and resampler. - { name = libpipewire-module-adapter } - - # Allows applications to create metadata objects. It creates - # a factory for Metadata objects. - { name = libpipewire-module-metadata } - - # Provides factories to make session manager objects. - { name = libpipewire-module-session-manager } -] - -wireplumber.components = [ - #{ name = , type = } - # - # WirePlumber components to load - # - - # The lua scripting engine - { name = libwireplumber-module-lua-scripting, type = module } - - # The lua configuration file - # Other components are loaded from there - { name = main.lua, type = config/lua } -] diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/00-functions.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/00-functions.lua deleted file mode 100644 index 451ad4f5..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/00-functions.lua +++ /dev/null @@ -1,36 +0,0 @@ -components = {} - -function load_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libwireplumber-module-" .. m, type = "module", args = a } - end -end - -function load_optional_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libwireplumber-module-" .. m, type = "module", args = a, optional = true } - end -end - -function load_pw_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libpipewire-module-" .. m, type = "pw_module", args = a } - end -end - -function load_script(s, a) - if not components[s] then - components[s] = { s, type = "script/lua", args = a } - end -end - -function load_monitor(s, a) - load_script("monitors/" .. s .. ".lua", a) -end - -function load_access(s, a) - load_script("access/access-" .. s .. ".lua", a) -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/20-default-access.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/20-default-access.lua deleted file mode 100644 index 0a7eb955..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/20-default-access.lua +++ /dev/null @@ -1,19 +0,0 @@ -default_access = {} -default_access.properties = {} -default_access.rules = {} - -function default_access.enable() - if default_access.enabled == false then - return - end - - load_access("default", { - rules = default_access.rules - }) - - if default_access.properties["enable-flatpak-portal"] then - -- Enables portal permissions via org.freedesktop.impl.portal.PermissionStore - load_module("portal-permissionstore") - load_access("portal") - end -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-alsa-monitor.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-alsa-monitor.lua deleted file mode 100644 index 8e45e434..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-alsa-monitor.lua +++ /dev/null @@ -1,29 +0,0 @@ -alsa_monitor = {} -alsa_monitor.properties = {} -alsa_monitor.rules = {} - -function alsa_monitor.enable() - if alsa_monitor.enabled == false then - return - end - - -- The "reserve-device" module needs to be loaded for reservation to work - if alsa_monitor.properties["alsa.reserve"] then - load_module("reserve-device") - end - - load_monitor("alsa", { - properties = alsa_monitor.properties, - rules = alsa_monitor.rules, - }) - - if alsa_monitor.properties["alsa.midi"] then - load_monitor("alsa-midi", { - properties = alsa_monitor.properties, - }) - -- The "file-monitor-api" module needs to be loaded for MIDI device monitoring - if alsa_monitor.properties["alsa.midi.monitoring"] then - load_module("file-monitor-api") - end - end -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-libcamera-monitor.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-libcamera-monitor.lua deleted file mode 100644 index cd820a83..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-libcamera-monitor.lua +++ /dev/null @@ -1,14 +0,0 @@ -libcamera_monitor = {} -libcamera_monitor.properties = {} -libcamera_monitor.rules = {} - -function libcamera_monitor.enable() - if libcamera_monitor.enabled == false then - return - end - - load_monitor("libcamera", { - properties = libcamera_monitor.properties, - rules = libcamera_monitor.rules, - }) -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-v4l2-monitor.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-v4l2-monitor.lua deleted file mode 100644 index 3fbdc9e7..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/30-v4l2-monitor.lua +++ /dev/null @@ -1,14 +0,0 @@ -v4l2_monitor = {} -v4l2_monitor.properties = {} -v4l2_monitor.rules = {} - -function v4l2_monitor.enable() - if v4l2_monitor.enabled == false then - return - end - - load_monitor("v4l2", { - properties = v4l2_monitor.properties, - rules = v4l2_monitor.rules, - }) -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/40-device-defaults.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/40-device-defaults.lua deleted file mode 100644 index 19202914..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/40-device-defaults.lua +++ /dev/null @@ -1,85 +0,0 @@ -device_defaults = {} -device_defaults.enabled = true - -device_defaults.properties = { - -- store preferences to the file system and restore them at startup; - -- when set to false, default nodes and routes are selected based on - -- their priorities and any runtime changes do not persist after restart - ["use-persistent-storage"] = true, - - -- the default volumes to apply to ACP device nodes, in the linear scale - --["default-volume"] = 0.064, - --["default-input-volume"] = 1.0, - - -- Whether to auto-switch to echo cancel sink and source nodes or not - ["auto-echo-cancel"] = true, - - -- Sets the default echo-cancel-sink node name to automatically switch to - ["echo-cancel-sink-name"] = "echo-cancel-sink", - - -- Sets the default echo-cancel-source node name to automatically switch to - ["echo-cancel-source-name"] = "echo-cancel-source", -} - --- Sets persistent device profiles that should never change when wireplumber is --- running, even if a new profile with higher priority becomes available -device_defaults.persistent_profiles = { - { - matches = { - { - -- Matches all devices - { "device.name", "matches", "*" }, - }, - }, - profile_names = { - "off", - "pro-audio" - } - }, -} - -device_defaults.profile_priorities = { - { - matches = { - { - -- Matches all bluez devices - { "device.name", "matches", "bluez_card.*" }, - }, - }, - -- lower the index higher the priority - priorities = { - -- "a2dp-sink-sbc", - -- "a2dp-sink-aptx_ll", - -- "a2dp-sink-aptx", - -- "a2dp-sink-aptx_hd", - -- "a2dp-sink-ldac", - -- "a2dp-sink-aac", - -- "a2dp-sink-sbc_xq", - } - }, -} - -function device_defaults.enable() - if device_defaults.enabled == false then - return - end - - -- Selects appropriate default nodes and enables saving and restoring them - load_module("default-nodes", device_defaults.properties) - - -- Selects appropriate profile for devices - load_script("policy-device-profile.lua", { - persistent = device_defaults.persistent_profiles, - priorities = device_defaults.profile_priorities - }) - - -- Selects appropriate device routes ("ports" in pulseaudio terminology) - -- and enables saving and restoring them together with - -- their properties (per-route/port volume levels, channel maps, etc) - load_script("policy-device-routes.lua", device_defaults.properties) - - if device_defaults.properties["use-persistent-storage"] then - -- Enables functionality to save and restore default device profiles - load_module("default-profile") - end -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/40-stream-defaults.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/40-stream-defaults.lua deleted file mode 100644 index b869099b..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/40-stream-defaults.lua +++ /dev/null @@ -1,42 +0,0 @@ -stream_defaults = {} -stream_defaults.enabled = true - -stream_defaults.properties = { - -- whether to restore the last stream properties or not - ["restore-props"] = true, - - -- whether to restore the last stream target or not - ["restore-target"] = true, - - -- the default channel volume for new streams whose props were never saved - -- previously. This is only used if "restore-props" is set to true. - ["default-channel-volume"] = 1.0, -} - -stream_defaults.rules = { - -- Rules to override settings per node - -- { - -- matches = { - -- { - -- { "application.name", "matches", "pw-play" }, - -- }, - -- }, - -- apply_properties = { - -- ["state.restore-props"] = false, - -- ["state.restore-target"] = false, - -- ["state.default-channel-volume"] = 0.5, - -- }, - -- }, -} - -function stream_defaults.enable() - if stream_defaults.enabled == false then - return - end - - -- Save and restore stream-specific properties - load_script("restore-stream.lua", { - properties = stream_defaults.properties, - rules = stream_defaults.rules, - }) -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-alsa-config.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-alsa-config.lua index 315cd1bf..f37949d9 100644 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-alsa-config.lua +++ b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-alsa-config.lua @@ -140,8 +140,8 @@ alsa_monitor.rules = { --["audio.rate"] = 44100, --["audio.allowed-rates"] = "32000,96000", --["audio.position"] = "FL,FR", - ["api.alsa.period-size"] = 256, - ["api.alsa.period-num"] = 3, + --["api.alsa.period-size"] = 256, + --["api.alsa.period-num"] = 3, --["api.alsa.headroom"] = 0, --["api.alsa.start-delay"] = 0, --["api.alsa.disable-mmap"] = false, diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-default-access-config.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-default-access-config.lua deleted file mode 100644 index 4ad3d57a..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-default-access-config.lua +++ /dev/null @@ -1,36 +0,0 @@ -default_access.enabled = true - -default_access.properties = { - -- Enable the use of the flatpak portal integration. - -- Disable if you are running a system-wide instance, which - -- doesn't have access to the D-Bus user session - ["enable-flatpak-portal"] = true, -} - -default_access.rules = { - { - matches = { - { - { "pipewire.access", "=", "flatpak" }, - { "media.category", "=", "Manager" }, - }, - }, - default_permissions = "all", - }, - { - matches = { - { - { "pipewire.access", "=", "flatpak" }, - }, - }, - default_permissions = "rx", - }, - { - matches = { - { - { "pipewire.access", "=", "restricted" }, - }, - }, - default_permissions = "rx", - }, -} diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-libcamera-config.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-libcamera-config.lua deleted file mode 100644 index d63fed11..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-libcamera-config.lua +++ /dev/null @@ -1,38 +0,0 @@ -libcamera_monitor.enabled = true - -libcamera_monitor.rules = { - -- An array of matches/actions to evaluate. - { - -- Rules for matching a device or node. It is an array of - -- properties that all need to match the regexp. If any of the - -- matches work, the actions are executed for the object. - matches = { - { - -- This matches all cards. - { "device.name", "matches", "libcamera_device.*" }, - }, - }, - -- Apply properties on the matched object. - apply_properties = { - -- ["device.nick"] = "My Device", - }, - }, - { - matches = { - { - -- Matches all sources. - { "node.name", "matches", "libcamera_input.*" }, - }, - { - -- Matches all sinks. - { "node.name", "matches", "libcamera_output.*" }, - }, - }, - apply_properties = { - --["node.nick"] = "My Node", - --["priority.driver"] = 100, - --["priority.session"] = 100, - --["node.pause-on-idle"] = false, - }, - }, -} diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-v4l2-config.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-v4l2-config.lua deleted file mode 100644 index 36e9f423..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/50-v4l2-config.lua +++ /dev/null @@ -1,38 +0,0 @@ -v4l2_monitor.enabled = true - -v4l2_monitor.rules = { - -- An array of matches/actions to evaluate. - { - -- Rules for matching a device or node. It is an array of - -- properties that all need to match the regexp. If any of the - -- matches work, the actions are executed for the object. - matches = { - { - -- This matches all cards. - { "device.name", "matches", "v4l2_device.*" }, - }, - }, - -- Apply properties on the matched object. - apply_properties = { - -- ["device.nick"] = "My Device", - }, - }, - { - matches = { - { - -- Matches all sources. - { "node.name", "matches", "v4l2_input.*" }, - }, - { - -- Matches all sinks. - { "node.name", "matches", "v4l2_output.*" }, - }, - }, - apply_properties = { - --["node.nick"] = "My Node", - --["priority.driver"] = 100, - --["priority.session"] = 100, - --["node.pause-on-idle"] = false, - }, - }, -} diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/90-enable-all.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/90-enable-all.lua deleted file mode 100644 index 37d790ea..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/main.lua.d/90-enable-all.lua +++ /dev/null @@ -1,26 +0,0 @@ --- Provide the "default" pw_metadata, which stores --- dynamic properties of pipewire objects in RAM -load_module("metadata") - --- Default client access policy -default_access.enable() - --- Load devices -alsa_monitor.enable() -v4l2_monitor.enable() -libcamera_monitor.enable() - --- Track/store/restore user choices about devices -device_defaults.enable() - --- Track/store/restore user choices about streams -stream_defaults.enable() - --- Link nodes by stream role and device intended role -load_script("intended-roles.lua") - --- Automatically suspends idle nodes after 3 seconds -load_script("suspend-node.lua") - --- Allows loading objects on demand via metadata -load_script("sm-objects.lua") diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.conf b/buildroot-external/rootfs-overlay/etc/wireplumber/policy.conf deleted file mode 100644 index 42f71484..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.conf +++ /dev/null @@ -1,73 +0,0 @@ -# WirePlumber daemon context configuration # - -context.properties = { - ## Properties to configure the PipeWire context and some modules - - application.name = "WirePlumber Policy" - log.level = 2 - wireplumber.script-engine = lua-scripting - wireplumber.export-core = false - - #mem.mlock-all = false - #support.dbus = true -} - -context.spa-libs = { - # = - # - # Used to find spa factory names. It maps an spa factory name - # regular expression to a library name that should contain - # that factory. - # - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - -context.modules = [ - #{ name = - # [ args = { = ... } ] - # [ flags = [ [ ifexists ] [ nofail ] ] - #} - # - # PipeWire modules to load. - # If ifexists is given, the module is ignored when it is not found. - # If nofail is given, module initialization failures are ignored. - # - - # The native communication protocol. - { name = libpipewire-module-protocol-native } - - # Allows creating nodes that run in the context of the - # client. Is used by all clients that want to provide - # data to PipeWire. - { name = libpipewire-module-client-node } - - # Allows creating devices that run in the context of the - # client. Is used by the session manager. - { name = libpipewire-module-client-device } - - # Makes a factory for wrapping nodes in an adapter with a - # converter and resampler. - { name = libpipewire-module-adapter } - - # Allows applications to create metadata objects. It creates - # a factory for Metadata objects. - { name = libpipewire-module-metadata } - - # Provides factories to make session manager objects. - { name = libpipewire-module-session-manager } -] - -wireplumber.components = [ - #{ name = , type = } - # - # WirePlumber components to load - # - - # The lua scripting engine - { name = libwireplumber-module-lua-scripting, type = module } - - # The lua configuration file - # Other components are loaded from there - { name = policy.lua, type = config/lua } -] diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/00-functions.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/00-functions.lua deleted file mode 100644 index 451ad4f5..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/00-functions.lua +++ /dev/null @@ -1,36 +0,0 @@ -components = {} - -function load_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libwireplumber-module-" .. m, type = "module", args = a } - end -end - -function load_optional_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libwireplumber-module-" .. m, type = "module", args = a, optional = true } - end -end - -function load_pw_module(m, a) - assert(type(m) == "string", "module name is mandatory, bail out"); - if not components[m] then - components[m] = { "libpipewire-module-" .. m, type = "pw_module", args = a } - end -end - -function load_script(s, a) - if not components[s] then - components[s] = { s, type = "script/lua", args = a } - end -end - -function load_monitor(s, a) - load_script("monitors/" .. s .. ".lua", a) -end - -function load_access(s, a) - load_script("access/access-" .. s .. ".lua", a) -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/10-default-policy.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/10-default-policy.lua deleted file mode 100644 index 83d0a3b2..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/10-default-policy.lua +++ /dev/null @@ -1,98 +0,0 @@ -default_policy = {} -default_policy.enabled = true -default_policy.properties = {} -default_policy.endpoints = {} - -default_policy.policy = { - ["move"] = true, -- moves session items when metadata target.node changes - ["follow"] = true, -- moves session items to the default device when it has changed - - -- Whether to forward the ports format of filter stream nodes to their - -- associated filter device nodes. This is needed for application to stream - -- surround audio if echo-cancel is enabled. - ["filter.forward-format"] = false, - - -- Set to 'true' to disable channel splitting & merging on nodes and enable - -- passthrough of audio in the same format as the format of the device. - -- Note that this breaks JACK support; it is generally not recommended - ["audio.no-dsp"] = false, - - -- how much to lower the volume of lower priority streams when ducking - -- note that this is a linear volume modifier (not cubic as in pulseaudio) - ["duck.level"] = 0.3, -} - -bluetooth_policy = {} - -bluetooth_policy.policy = { - -- Whether to store state on the filesystem. - ["use-persistent-storage"] = true, - - -- Whether to use headset profile in the presence of an input stream. - ["media-role.use-headset-profile"] = true, - - -- Application names correspond to application.name in stream properties. - -- Applications which do not set media.role but which should be considered - -- for role based profile switching can be specified here. - ["media-role.applications"] = { - "Firefox", "Chromium input", "Google Chrome input", "Brave input", - "Microsoft Edge input", "Vivaldi input", "ZOOM VoiceEngine", - "Telegram Desktop", "telegram-desktop", "linphone", "Mumble", - "WEBRTC VoiceEngine", "Skype", "Firefox Developer Edition", - }, -} - -dsp_policy = {} - -dsp_policy.policy = {} - -dsp_policy.policy.properties = {} - --- An array of matches/filters to apply. --- `matches` are rules for matching a sink node. It is an array of --- properties that all need to match the regexp. If any of the --- matches in an array work, the filters are executed for the sink. --- `filter_chain` is a JSON string of parameters to filter-chain module --- `properties` table only has `pro_audio` boolean, which enables Pro Audio mode on the sink when applying DSP -dsp_policy.policy.rules = {} - -function default_policy.enable() - if default_policy.enabled == false then - return - end - - -- Session item factories, building blocks for the session management graph - -- Do not disable these unless you really know what you are doing - load_module("si-node") - load_module("si-audio-adapter") - load_module("si-standard-link") - load_module("si-audio-endpoint") - - -- API to access default nodes from scripts - load_module("default-nodes-api") - - -- API to access mixer controls, needed for volume ducking - load_module("mixer-api") - - -- Create endpoints statically at startup - load_script("static-endpoints.lua", default_policy.endpoints) - - -- Create items for nodes that appear in the graph - load_script("create-item.lua", default_policy.policy) - - -- Link nodes to each other to make media flow in the graph - load_script("policy-node.lua", default_policy.policy) - - -- Link client nodes with endpoints to make media flow in the graph - load_script("policy-endpoint-client.lua", default_policy.policy) - load_script("policy-endpoint-client-links.lua", default_policy.policy) - - -- Link endpoints with device nodes to make media flow in the graph - load_script("policy-endpoint-device.lua", default_policy.policy) - - -- Switch bluetooth profile based on media.role - load_script("policy-bluetooth.lua", bluetooth_policy.policy) - - -- Load filter chains for hardware requiring DSP - load_script("policy-dsp.lua", dsp_policy.policy) -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/50-endpoints-config.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/50-endpoints-config.lua deleted file mode 100644 index 28656949..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/50-endpoints-config.lua +++ /dev/null @@ -1,95 +0,0 @@ --- uncomment to enable role-based endpoints --- this is not yet ready for desktop use --- ---[[ - -default_policy.policy.roles = { - ["Capture"] = { - ["alias"] = { "Multimedia", "Music", "Voice", "Capture" }, - ["priority"] = 25, - ["action.default"] = "cork", - ["action.capture"] = "mix", - ["media.class"] = "Audio/Source", - }, - ["Multimedia"] = { - ["alias"] = { "Movie", "Music", "Game" }, - ["priority"] = 25, - ["action.default"] = "cork", - }, - ["Speech-Low"] = { - ["priority"] = 30, - ["action.default"] = "cork", - ["action.Speech-Low"] = "mix", - }, - ["Custom-Low"] = { - ["priority"] = 35, - ["action.default"] = "cork", - ["action.Custom-Low"] = "mix", - }, - ["Navigation"] = { - ["priority"] = 50, - ["action.default"] = "duck", - ["action.Navigation"] = "mix", - }, - ["Speech-High"] = { - ["priority"] = 60, - ["action.default"] = "cork", - ["action.Speech-High"] = "mix", - }, - ["Custom-High"] = { - ["priority"] = 65, - ["action.default"] = "cork", - ["action.Custom-High"] = "mix", - }, - ["Communication"] = { - ["priority"] = 75, - ["action.default"] = "cork", - ["action.Communication"] = "mix", - }, - ["Emergency"] = { - ["alias"] = { "Alert" }, - ["priority"] = 99, - ["action.default"] = "cork", - ["action.Emergency"] = "mix", - }, -} - -default_policy.endpoints = { - ["endpoint.capture"] = { - ["media.class"] = "Audio/Source", - ["role"] = "Capture", - }, - ["endpoint.multimedia"] = { - ["media.class"] = "Audio/Sink", - ["role"] = "Multimedia", - }, - ["endpoint.speech_low"] = { - ["media.class"] = "Audio/Sink", - ["role"] = "Speech-Low", - }, - ["endpoint.custom_low"] = { - ["media.class"] = "Audio/Sink", - ["role"] = "Custom-Low", - }, - ["endpoint.navigation"] = { - ["media.class"] = "Audio/Sink", - ["role"] = "Navigation", - }, - ["endpoint.speech_high"] = { - ["media.class"] = "Audio/Sink", - ["role"] = "Speech-High", - }, - ["endpoint.custom_high"] = { - ["media.class"] = "Audio/Sink", - ["role"] = "Custom-High", - }, - ["endpoint.communication"] = { - ["media.class"] = "Audio/Sink", - ["role"] = "Communication", - }, - ["endpoint.emergency"] = { - ["media.class"] = "Audio/Sink", - ["role"] = "Emergency", - }, -} -]]-- diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/90-enable-all.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/90-enable-all.lua deleted file mode 100644 index c20e23ab..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/policy.lua.d/90-enable-all.lua +++ /dev/null @@ -1 +0,0 @@ -default_policy.enable() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/access/access-default.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/access/access-default.lua deleted file mode 100644 index 0fac87b7..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/access/access-default.lua +++ /dev/null @@ -1,53 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author George Kiagiadakis --- --- SPDX-License-Identifier: MIT - -local config = ... or {} - --- preprocess rules and create Interest objects -for _, r in ipairs(config.rules or {}) do - r.interests = {} - for _, i in ipairs(r.matches) do - local interest_desc = { type = "properties" } - for _, c in ipairs(i) do - c.type = "pw" - table.insert(interest_desc, Constraint(c)) - end - local interest = Interest(interest_desc) - table.insert(r.interests, interest) - end - r.matches = nil -end - -function rulesGetDefaultPermissions(properties) - for _, r in ipairs(config.rules or {}) do - if r.default_permissions then - for _, interest in ipairs(r.interests) do - if interest:matches(properties) then - return r.default_permissions - end - end - end - end -end - -clients_om = ObjectManager { - Interest { type = "client" } -} - -clients_om:connect("object-added", function (om, client) - local id = client["bound-id"] - local properties = client["properties"] - - local perms = rulesGetDefaultPermissions(properties) - - if perms then - Log.info(client, "Granting permissions to client " .. id .. ": " .. perms) - client:update_permissions { ["any"] = perms } - end -end) - -clients_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/access/access-portal.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/access/access-portal.lua deleted file mode 100644 index e87a1572..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/access/access-portal.lua +++ /dev/null @@ -1,141 +0,0 @@ -MEDIA_ROLE_NONE = 0 -MEDIA_ROLE_CAMERA = 1 << 0 - -function hasPermission (permissions, app_id, lookup) - if permissions then - for key, values in pairs(permissions) do - if key == app_id then - for _, v in pairs(values) do - if v == lookup then - return true - end - end - end - end - end - return false -end - -function parseMediaRoles (media_roles_str) - local media_roles = MEDIA_ROLE_NONE - for role in media_roles_str:gmatch('[^,%s]+') do - if role == "Camera" then - media_roles = media_roles | MEDIA_ROLE_CAMERA - end - end - return media_roles -end - -function setPermissions (client, allow_client, allow_nodes) - local client_id = client["bound-id"] - Log.info(client, "Granting ALL access to client " .. client_id) - - -- Update permissions on client - client:update_permissions { [client_id] = allow_client and "all" or "-" } - - -- Update permissions on camera source nodes - for node in nodes_om:iterate() do - local node_id = node["bound-id"] - client:update_permissions { [node_id] = allow_nodes and "all" or "-" } - end -end - -function updateClientPermissions (client, permissions) - local client_id = client["bound-id"] - local str_prop = nil - local app_id = nil - local media_roles = nil - local allowed = false - - -- Make sure the client is not the portal itself - str_prop = client.properties["pipewire.access.portal.is_portal"] - if str_prop == "yes" then - Log.info (client, "client is the portal itself") - return - end - - -- Make sure the client has a portal app Id - str_prop = client.properties["pipewire.access.portal.app_id"] - if str_prop == nil then - Log.info (client, "Portal managed client did not set app_id") - return - end - if str_prop == "" then - Log.info (client, "Ignoring portal check for non-sandboxed client") - setPermissions (client, true, true) - return - end - app_id = str_prop - - -- Make sure the client has portal media roles - str_prop = client.properties["pipewire.access.portal.media_roles"] - if str_prop == nil then - Log.info (client, "Portal managed client did not set media_roles") - return - end - media_roles = parseMediaRoles (str_prop) - if (media_roles & MEDIA_ROLE_CAMERA) == 0 then - Log.info (client, "Ignoring portal check for clients without camera role") - return - end - - -- Update permissions - allowed = hasPermission (permissions, app_id, "yes") - - Log.info (client, "setting permissions: " .. tostring(allowed)) - setPermissions (client, allowed, allowed) -end - --- Create portal clients object manager -clients_om = ObjectManager { - Interest { - type = "client", - Constraint { "pipewire.access", "=", "portal" }, - } -} - --- Set permissions to portal clients from the permission store if loaded -pps_plugin = Plugin.find("portal-permissionstore") -if pps_plugin then - nodes_om = ObjectManager { - Interest { - type = "node", - Constraint { "media.role", "=", "Camera" }, - Constraint { "media.class", "=", "Video/Source" }, - } - } - nodes_om:activate() - - clients_om:connect("object-added", function (om, client) - local new_perms = pps_plugin:call("lookup", "devices", "camera"); - updateClientPermissions (client, new_perms) - end) - - nodes_om:connect("object-added", function (om, node) - local new_perms = pps_plugin:call("lookup", "devices", "camera"); - for client in clients_om:iterate() do - updateClientPermissions (client, new_perms) - end - end) - - pps_plugin:connect("changed", function (p, table, id, deleted, permissions) - if table == "devices" or id == "camera" then - for app_id, _ in pairs(permissions) do - for client in clients_om:iterate { - Constraint { "pipewire.access.portal.app_id", "=", app_id } - } do - updateClientPermissions (client, permissions) - end - end - end - end) -else - -- Otherwise, just set all permissions to all portal clients - clients_om:connect("object-added", function (om, client) - local id = client["bound-id"] - Log.info(client, "Granting ALL access to client " .. id) - client:update_permissions { ["any"] = "all" } - end) -end - -clients_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/create-item.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/create-item.lua deleted file mode 100644 index 6d8056aa..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/create-item.lua +++ /dev/null @@ -1,130 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author Julian Bouzas --- --- SPDX-License-Identifier: MIT - --- Receive script arguments from config.lua -local config = ... or {} - -items = {} - -function configProperties(node) - local np = node.properties - local properties = { - ["item.node"] = node, - ["item.plugged.usec"] = GLib.get_monotonic_time(), - ["item.features.no-dsp"] = config["audio.no-dsp"], - ["item.features.monitor"] = true, - ["item.features.control-port"] = false, - ["node.id"] = node["bound-id"], - ["client.id"] = np["client.id"], - ["object.path"] = np["object.path"], - ["object.serial"] = np["object.serial"], - ["target.object"] = np["target.object"], - ["priority.session"] = np["priority.session"], - ["device.id"] = np["device.id"], - ["card.profile.device"] = np["card.profile.device"], - ["target.endpoint"] = np["target.endpoint"], - } - - for k, v in pairs(np) do - if k:find("^node") or k:find("^stream") or k:find("^media") then - properties[k] = v - end - end - - local media_class = properties["media.class"] or "" - - if not properties["media.type"] then - for _, i in ipairs({ "Audio", "Video", "Midi" }) do - if media_class:find(i) then - properties["media.type"] = i - break - end - end - end - - properties["item.node.type"] = - media_class:find("^Stream/") and "stream" or "device" - - if media_class:find("Sink") or - media_class:find("Input") or - media_class:find("Duplex") then - properties["item.node.direction"] = "input" - elseif media_class:find("Source") or media_class:find("Output") then - properties["item.node.direction"] = "output" - end - return properties -end - -function addItem (node, item_type) - local id = node["bound-id"] - local item - - -- create item - item = SessionItem ( item_type ) - items[id] = item - - -- configure item - if not item:configure(configProperties(node)) then - Log.warning(item, "failed to configure item for node " .. tostring(id)) - return - end - - item:register () - - -- activate item - items[id]:activate (Features.ALL, function (item, e) - if e then - Log.message(item, "failed to activate item: " .. tostring(e)); - if item then - item:remove () - end - else - Log.info(item, "activated item for node " .. tostring(id)) - - -- Trigger object managers to update status - item:remove () - if item["active-features"] ~= 0 then - item:register () - end - end - end) -end - -nodes_om = ObjectManager { - Interest { - type = "node", - Constraint { "media.class", "#", "Stream/*", type = "pw-global" }, - }, - Interest { - type = "node", - Constraint { "media.class", "#", "Video/*", type = "pw-global" }, - }, - Interest { - type = "node", - Constraint { "media.class", "#", "Audio/*", type = "pw-global" }, - Constraint { "wireplumber.is-endpoint", "-", type = "pw" }, - }, -} - -nodes_om:connect("object-added", function (om, node) - local media_class = node.properties['media.class'] - if string.find (media_class, "Audio") then - addItem (node, "si-audio-adapter") - else - addItem (node, "si-node") - end -end) - -nodes_om:connect("object-removed", function (om, node) - local id = node["bound-id"] - if items[id] then - items[id]:remove () - items[id] = nil - end -end) - -nodes_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/fallback-sink.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/fallback-sink.lua deleted file mode 100644 index d7c3cfa2..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/fallback-sink.lua +++ /dev/null @@ -1,93 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author Frédéric Danis --- --- SPDX-License-Identifier: MIT - -local sink_ids = {} -local fallback_node = nil - -node_om = ObjectManager { - Interest { - type = "node", - Constraint { "media.class", "matches", "Audio/Sink", type = "pw-global" }, - -- Do not consider endpoints created by WirePlumber - Constraint { "wireplumber.is-endpoint", "!", true, type = "pw" }, - -- or the fallback sink itself - Constraint { "wireplumber.is-fallback", "!", true, type = "pw" }, - } -} - -function createFallbackSink() - if fallback_node then - return - end - - Log.info("Create fallback sink") - - local properties = {} - - properties["node.name"] = "auto_null" - properties["node.description"] = "Dummy Output" - - properties["audio.rate"] = 48000 - properties["audio.channels"] = 2 - properties["audio.position"] = "FL,FR" - - properties["media.class"] = "Audio/Sink" - properties["factory.name"] = "support.null-audio-sink" - properties["node.virtual"] = "true" - properties["monitor.channel-volumes"] = "true" - - properties["wireplumber.is-fallback"] = "true" - properties["priority.session"] = 500 - - fallback_node = LocalNode("adapter", properties) - fallback_node:activate(Feature.Proxy.BOUND) -end - -function checkSinks() - local sink_ids_items = 0 - for _ in pairs(sink_ids) do sink_ids_items = sink_ids_items + 1 end - - if sink_ids_items > 0 then - if fallback_node then - Log.info("Remove fallback sink") - fallback_node = nil - end - elseif not fallback_node then - createFallbackSink() - end -end - -function checkSinksAfterTimeout() - if timeout_source then - timeout_source:destroy() - end - timeout_source = Core.timeout_add(1000, function () - checkSinks() - timeout_source = nil - end) -end - -node_om:connect("object-added", function (_, node) - Log.debug("object added: " .. node.properties["object.id"] .. " " .. - tostring(node.properties["node.name"])) - - sink_ids[node.properties["object.id"]] = node.properties["node.name"] - - checkSinksAfterTimeout() -end) - -node_om:connect("object-removed", function (_, node) - Log.debug("object removed: " .. node.properties["object.id"] .. " " .. - tostring(node.properties["node.name"])) - - sink_ids[node.properties["object.id"]] = nil - checkSinksAfterTimeout() -end) - -node_om:activate() - -checkSinksAfterTimeout() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/intended-roles.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/intended-roles.lua deleted file mode 100644 index f0d472b9..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/intended-roles.lua +++ /dev/null @@ -1,74 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Asymptotic --- @author Arun Raghavan --- --- SPDX-License-Identifier: MIT --- --- Route streams of a given role (media.role property) to devices that are --- intended for that role (device.intended-roles property) - -metadata_om = ObjectManager { - Interest { - type = "metadata", - Constraint { "metadata.name", "=", "default" }, - } -} - -devices_om = ObjectManager { - Interest { - type = "node", - Constraint { "media.class", "matches", "Audio/*", type = "pw-global" }, - Constraint { "device.intended-roles", "is-present", type = "pw" }, - } -} - -streams_om = ObjectManager { - Interest { - type = "node", - Constraint { "media.class", "matches", "Stream/*/Audio", type = "pw-global" }, - Constraint { "media.role", "is-present", type = "pw-global" } - } -} - -local function routeUsingIntendedRole(stream, dev) - local stream_role = stream.properties["media.role"] - local is_input = stream.properties["media.class"]:find("Input") ~= nil - - local is_source = dev.properties["media.class"]:find("Source") ~= nil - local dev_roles = dev.properties["device.intended-roles"] - - -- Make sure the stream and device direction match - if is_input ~= is_source then - return - end - - for role in dev_roles:gmatch("(%a+)") do - if role == stream_role then - Log.info(stream, - string.format("Routing stream '%s' (%d) with role '%s' to '%s' (%d)", - stream.properties["node.name"], stream["bound-id"], stream_role, - dev.properties["node.name"], dev["bound-id"]) - ) - - local metadata = metadata_om:lookup() - metadata:set(stream["bound-id"], "target.node", "Spa:Id", dev["bound-id"]) - end - end -end - -streams_om:connect("object-added", function (streams_om, stream) - for dev in devices_om:iterate() do - routeUsingIntendedRole(stream, dev) - end -end) - -devices_om:connect("object-added", function (devices_om, dev) - for stream in streams_om:iterate() do - routeUsingIntendedRole(stream, dev) - end -end) - -metadata_om:activate() -devices_om:activate() -streams_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/alsa-midi.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/alsa-midi.lua deleted file mode 100644 index 6fdf34d3..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/alsa-midi.lua +++ /dev/null @@ -1,68 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author Julian Bouzas --- --- SPDX-License-Identifier: MIT - --- Receive script arguments from config.lua -local config = ... or {} - --- ensure config.properties is not nil -config.properties = config.properties or {} - -SND_PATH = "/dev/snd" -SEQ_NAME = "seq" -SND_SEQ_PATH = SND_PATH .. "/" .. SEQ_NAME - -midi_node = nil -fm_plugin = nil - -function CreateMidiNode () - -- Midi properties - local props = {} - if type(config.properties["alsa.midi.node-properties"]) == "table" then - props = config.properties["alsa.midi.node-properties"] - end - props["factory.name"] = "api.alsa.seq.bridge" - props["node.name"] = props["node.name"] or "Midi-Bridge" - - -- create the midi node - local node = Node("spa-node-factory", props) - node:activate(Feature.Proxy.BOUND, function (n) - Log.info ("activated Midi bridge") - end) - - return node; -end - -if GLib.access (SND_SEQ_PATH, "rw") then - midi_node = CreateMidiNode () -elseif config.properties["alsa.midi.monitoring"] then - fm_plugin = Plugin.find("file-monitor-api") -end - --- Only monitor the MIDI device if file does not exist and plugin API is loaded -if midi_node == nil and fm_plugin ~= nil then - -- listen for changed events - fm_plugin:connect ("changed", function (o, file, old, evtype) - -- files attributes changed - if evtype == "attribute-changed" then - if file ~= SND_SEQ_PATH then - return - end - if midi_node == nil and GLib.access (SND_SEQ_PATH, "rw") then - midi_node = CreateMidiNode () - fm_plugin:call ("remove-watch", SND_PATH) - end - end - - -- directory is going to be unmounted - if evtype == "pre-unmount" then - fm_plugin:call ("remove-watch", SND_PATH) - end - end) - - -- add watch - fm_plugin:call ("add-watch", SND_PATH, "m") -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/alsa.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/alsa.lua deleted file mode 100644 index d56c9f04..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/alsa.lua +++ /dev/null @@ -1,427 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author George Kiagiadakis --- --- SPDX-License-Identifier: MIT - --- Receive script arguments from config.lua -local config = ... or {} - --- ensure config.properties is not nil -config.properties = config.properties or {} - --- unique device/node name tables -device_names_table = nil -node_names_table = nil - --- preprocess rules and create Interest objects -for _, r in ipairs(config.rules or {}) do - r.interests = {} - for _, i in ipairs(r.matches) do - local interest_desc = { type = "properties" } - for _, c in ipairs(i) do - c.type = "pw" - table.insert(interest_desc, Constraint(c)) - end - local interest = Interest(interest_desc) - table.insert(r.interests, interest) - end - r.matches = nil -end - --- applies properties from config.rules when asked to -function rulesApplyProperties(properties) - for _, r in ipairs(config.rules or {}) do - if r.apply_properties then - for _, interest in ipairs(r.interests) do - if interest:matches(properties) then - for k, v in pairs(r.apply_properties) do - properties[k] = v - end - end - end - end - end -end - -function nonempty(str) - return str ~= "" and str or nil -end - -function createNode(parent, id, obj_type, factory, properties) - local dev_props = parent.properties - - -- set the device id and spa factory name; REQUIRED, do not change - properties["device.id"] = parent["bound-id"] - properties["factory.name"] = factory - - -- set the default pause-on-idle setting - properties["node.pause-on-idle"] = false - - -- try to negotiate the max ammount of channels - if dev_props["api.alsa.use-acp"] ~= "true" then - properties["audio.channels"] = properties["audio.channels"] or "64" - end - - local dev = properties["api.alsa.pcm.device"] - or properties["alsa.device"] or "0" - local subdev = properties["api.alsa.pcm.subdevice"] - or properties["alsa.subdevice"] or "0" - local stream = properties["api.alsa.pcm.stream"] or "unknown" - local profile = properties["device.profile.name"] - or (stream .. "." .. dev .. "." .. subdev) - local profile_desc = properties["device.profile.description"] - - -- set priority - if not properties["priority.driver"] then - local priority = (dev == "0") and 1000 or 744 - if stream == "capture" then - priority = priority + 1000 - end - - priority = priority - (tonumber(dev) * 16) - tonumber(subdev) - - if profile:find("^pro%-") then - priority = priority + 500 - elseif profile:find("^analog%-") then - priority = priority + 9 - elseif profile:find("^iec958%-") then - priority = priority + 8 - end - - properties["priority.driver"] = priority - properties["priority.session"] = priority - end - - -- ensure the node has a media class - if not properties["media.class"] then - if stream == "capture" then - properties["media.class"] = "Audio/Source" - else - properties["media.class"] = "Audio/Sink" - end - end - - -- ensure the node has a name - if not properties["node.name"] then - local name = - (stream == "capture" and "alsa_input" or "alsa_output") - .. "." .. - (dev_props["device.name"]:gsub("^alsa_card%.(.+)", "%1") or - dev_props["device.name"] or - "unnamed-device") - .. "." .. - profile - - -- sanitize name - name = name:gsub("([^%w_%-%.])", "_") - - properties["node.name"] = name - - -- deduplicate nodes with the same name - for counter = 2, 99, 1 do - if node_names_table[properties["node.name"]] ~= true then - node_names_table[properties["node.name"]] = true - break - end - properties["node.name"] = name .. "." .. counter - end - end - - -- and a nick - local nick = nonempty(properties["node.nick"]) - or nonempty(properties["api.alsa.pcm.name"]) - or nonempty(properties["alsa.name"]) - or nonempty(profile_desc) - or dev_props["device.nick"] - if nick == "USB Audio" then - nick = dev_props["device.nick"] - end - -- also sanitize nick, replace ':' with ' ' - properties["node.nick"] = nick:gsub("(:)", " ") - - -- ensure the node has a description - if not properties["node.description"] then - local desc = nonempty(dev_props["device.description"]) or "unknown" - local name = nonempty(properties["api.alsa.pcm.name"]) or - nonempty(properties["api.alsa.pcm.id"]) or dev - - if profile_desc then - desc = desc .. " " .. profile_desc - elseif subdev ~= "0" then - desc = desc .. " (" .. name .. " " .. subdev .. ")" - elseif dev ~= "0" then - desc = desc .. " (" .. name .. ")" - end - - -- also sanitize description, replace ':' with ' ' - properties["node.description"] = desc:gsub("(:)", " ") - end - - -- add api.alsa.card.* properties for rule matching purposes - for k, v in pairs(dev_props) do - if k:find("^api%.alsa%.card%..*") then - properties[k] = v - end - end - - -- apply VM overrides - local vm_overrides = config.properties["vm.node.defaults"] - if nonempty(Core.get_vm_type()) and type(vm_overrides) == "table" then - for k, v in pairs(vm_overrides) do - properties[k] = v - end - end - - -- apply properties from config.rules - rulesApplyProperties(properties) - if properties["node.disabled"] then - node_names_table [properties ["node.name"]] = nil - return - end - - -- create the node - local node = Node("adapter", properties) - node:activate(Feature.Proxy.BOUND) - parent:store_managed_object(id, node) -end - -function createDevice(parent, id, factory, properties) - local device = SpaDevice(factory, properties) - if device then - device:connect("create-object", createNode) - device:connect("object-removed", function (parent, id) - local node = parent:get_managed_object(id) - if not node then - return - end - - node_names_table[node.properties["node.name"]] = nil - end) - device:activate(Feature.SpaDevice.ENABLED | Feature.Proxy.BOUND) - parent:store_managed_object(id, device) - else - Log.warning ("Failed to create '" .. factory .. "' device") - end -end - -function prepareDevice(parent, id, obj_type, factory, properties) - -- ensure the device has an appropriate name - local name = "alsa_card." .. - (properties["device.name"] or - properties["device.bus-id"] or - properties["device.bus-path"] or - tostring(id)):gsub("([^%w_%-%.])", "_") - - properties["device.name"] = name - - -- deduplicate devices with the same name - for counter = 2, 99, 1 do - if device_names_table[properties["device.name"]] ~= true then - device_names_table[properties["device.name"]] = true - break - end - properties["device.name"] = name .. "." .. counter - end - - -- ensure the device has a description - if not properties["device.description"] then - local d = nil - local f = properties["device.form-factor"] - local c = properties["device.class"] - local n = properties["api.alsa.card.name"] - - if n == "Loopback" then - d = I18n.gettext("Loopback") - elseif f == "internal" then - d = I18n.gettext("Built-in Audio") - elseif c == "modem" then - d = I18n.gettext("Modem") - end - - d = d or properties["device.product.name"] - or properties["api.alsa.card.name"] - or properties["alsa.card_name"] - or "Unknown device" - properties["device.description"] = d - end - - -- ensure the device has a nick - properties["device.nick"] = - properties["device.nick"] or - properties["api.alsa.card.name"] or - properties["alsa.card_name"] - - -- set the icon name - if not properties["device.icon-name"] then - local icon = nil - local icon_map = { - -- form factor -> icon - ["microphone"] = "audio-input-microphone", - ["webcam"] = "camera-web", - ["handset"] = "phone", - ["portable"] = "multimedia-player", - ["tv"] = "video-display", - ["headset"] = "audio-headset", - ["headphone"] = "audio-headphones", - ["speaker"] = "audio-speakers", - ["hands-free"] = "audio-handsfree", - } - local f = properties["device.form-factor"] - local c = properties["device.class"] - local b = properties["device.bus"] - - icon = icon_map[f] or ((c == "modem") and "modem") or "audio-card" - properties["device.icon-name"] = icon .. "-analog" .. (b and ("-" .. b) or "") - end - - -- apply properties from config.rules - rulesApplyProperties(properties) - if properties["device.disabled"] then - device_names_table [properties ["device.name"]] = nil - return - end - - -- override the device factory to use ACP - if properties["api.alsa.use-acp"] then - Log.info("Enabling the use of ACP on " .. properties["device.name"]) - factory = "api.alsa.acp.device" - end - - -- use device reservation, if available - if rd_plugin and properties["api.alsa.card"] then - local rd_name = "Audio" .. properties["api.alsa.card"] - local rd = rd_plugin:call("create-reservation", - rd_name, - config.properties["alsa.reserve.application-name"] or "WirePlumber", - properties["device.name"], - config.properties["alsa.reserve.priority"] or -20); - - properties["api.dbus.ReserveDevice1"] = rd_name - - -- unlike pipewire-media-session, this logic here keeps the device - -- acquired at all times and destroys it if someone else acquires - rd:connect("notify::state", function (rd, pspec) - local state = rd["state"] - - if state == "acquired" then - -- create the device - createDevice(parent, id, factory, properties) - - elseif state == "available" then - -- attempt to acquire again - rd:call("acquire") - - elseif state == "busy" then - -- destroy the device - parent:store_managed_object(id, nil) - end - end) - - rd:connect("release-requested", function (rd) - Log.info("release requested") - parent:store_managed_object(id, nil) - rd:call("release") - end) - - if jack_device then - rd:connect("notify::owner-name-changed", function (rd, pspec) - if rd["state"] == "busy" and - rd["owner-application-name"] == "Jack audio server" then - -- TODO enable the jack device - else - -- TODO disable the jack device - end - end) - end - - rd:call("acquire") - else - -- create the device - createDevice(parent, id, factory, properties) - end -end - -function createMonitor () - local m = SpaDevice("api.alsa.enum.udev", config.properties) - if m == nil then - Log.message("PipeWire's SPA ALSA udev plugin(\"api.alsa.enum.udev\")" - .. "missing or broken. Sound Cards cannot be enumerated") - return nil - end - - -- handle create-object to prepare device - m:connect("create-object", prepareDevice) - - -- handle object-removed to destroy device reservations and recycle device name - m:connect("object-removed", function (parent, id) - local device = parent:get_managed_object(id) - if not device then - return - end - - if rd_plugin then - local rd_name = device.properties["api.dbus.ReserveDevice1"] - if rd_name then - rd_plugin:call("destroy-reservation", rd_name) - end - end - device_names_table[device.properties["device.name"]] = nil - for managed_node in device:iterate_managed_objects() do - node_names_table[managed_node.properties["node.name"]] = nil - end - end) - - -- reset the name tables to make sure names are recycled - device_names_table = {} - node_names_table = {} - - -- activate monitor - Log.info("Activating ALSA monitor") - m:activate(Feature.SpaDevice.ENABLED) - return m -end - --- create the JACK device (for PipeWire to act as client to a JACK server) -if config.properties["alsa.jack-device"] then - jack_device = Device("spa-device-factory", { - ["factory.name"] = "api.jack.device", - ["node.name"] = "JACK-Device", - }) - jack_device:activate(Feature.Proxy.BOUND) -end - --- enable device reservation if requested -if config.properties["alsa.reserve"] then - rd_plugin = Plugin.find("reserve-device") -end - --- if the reserve-device plugin is enabled, at the point of script execution --- it is expected to be connected. if it is not, assume the d-bus connection --- has failed and continue without it -if rd_plugin and rd_plugin:call("get-dbus")["state"] ~= "connected" then - Log.message("reserve-device plugin is not connected to D-Bus, " - .. "disabling device reservation") - rd_plugin = nil -end - --- handle rd_plugin state changes to destroy and re-create the ALSA monitor in --- case D-Bus service is restarted -if rd_plugin then - local dbus = rd_plugin:call("get-dbus") - dbus:connect("notify::state", function (b, pspec) - local state = b["state"] - Log.info ("rd-plugin state changed to " .. state) - if state == "connected" then - Log.info ("Creating ALSA monitor") - monitor = createMonitor() - elseif state == "closed" then - Log.info ("Destroying ALSA monitor") - monitor = nil - end - end) -end - --- create the monitor -monitor = createMonitor() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/bluez-midi.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/bluez-midi.lua deleted file mode 100644 index 32b886ec..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/bluez-midi.lua +++ /dev/null @@ -1,187 +0,0 @@ --- WirePlumber --- --- Copyright © 2022 Pauli Virtanen --- @author Pauli Virtanen --- --- SPDX-License-Identifier: MIT - -local config = ... or {} - --- unique device/node name tables -node_names_table = nil -id_to_name_table = nil - --- preprocess rules and create Interest objects -for _, r in ipairs(config.rules or {}) do - r.interests = {} - for _, i in ipairs(r.matches) do - local interest_desc = { type = "properties" } - for _, c in ipairs(i) do - c.type = "pw" - table.insert(interest_desc, Constraint(c)) - end - local interest = Interest(interest_desc) - table.insert(r.interests, interest) - end - r.matches = nil -end - --- applies properties from config.rules when asked to -function rulesApplyProperties(properties) - for _, r in ipairs(config.rules or {}) do - if r.apply_properties then - for _, interest in ipairs(r.interests) do - if interest:matches(properties) then - for k, v in pairs(r.apply_properties) do - properties[k] = v - end - end - end - end - end -end - -function setLatencyOffset(node, offset_msec) - if not offset_msec then - return - end - - local props = { "Spa:Pod:Object:Param:Props", "Props" } - props.latencyOffsetNsec = tonumber(offset_msec) * 1000000 - - local param = Pod.Object(props) - Log.debug(param, "setting latency offset on " .. tostring(node)) - node:set_param("Props", param) -end - -function createNode(parent, id, type, factory, properties) - properties["factory.name"] = factory - - -- set the node description - local desc = properties["node.description"] - -- sanitize description, replace ':' with ' ' - properties["node.description"] = desc:gsub("(:)", " ") - - -- set the node name - local name = - "bluez_midi." .. properties["api.bluez5.address"] - -- sanitize name - name = name:gsub("([^%w_%-%.])", "_") - -- deduplicate nodes with the same name - properties["node.name"] = name - for counter = 2, 99, 1 do - if node_names_table[properties["node.name"]] ~= true then - node_names_table[properties["node.name"]] = true - break - end - properties["node.name"] = name .. "." .. counter - end - - properties["api.glib.mainloop"] = "true" - - -- apply properties from config.rules - rulesApplyProperties(properties) - - local latency_offset = properties["node.latency-offset-msec"] - properties["node.latency-offset-msec"] = nil - - -- create the node - -- it doesn't necessarily need to be a local node, - -- the other Bluetooth parts run in the local process, - -- so it's consistent to have also this here - local node = LocalNode("spa-node-factory", properties) - node:activate(Feature.Proxy.BOUND) - parent:store_managed_object(id, node) - id_to_name_table[id] = properties["node.name"] - setLatencyOffset(node, latency_offset) -end - -function createMonitor() - local monitor_props = {} - for k, v in pairs(config.properties or {}) do - monitor_props[k] = v - end - monitor_props["server"] = nil - - monitor_props["api.glib.mainloop"] = "true" - - local monitor = SpaDevice("api.bluez5.midi.enum", monitor_props) - if monitor then - monitor:connect("create-object", createNode) - monitor:connect("object-removed", function (parent, id) - node_names_table[id_to_name_table[id]] = nil - id_to_name_table[id] = nil - end) - else - Log.message("PipeWire's BlueZ MIDI SPA missing or broken. Bluetooth not supported.") - return nil - end - - -- reset the name tables to make sure names are recycled - node_names_table = {} - id_to_name_table = {} - - monitor:activate(Feature.SpaDevice.ENABLED) - return monitor -end - -function createServers() - local props = config.properties or {} - - if not props["servers"] then - return nil - end - - local servers = {} - local i = 1 - - for k, v in pairs(props["servers"]) do - local node_props = { - ["node.name"] = v, - ["node.description"] = string.format(I18n.gettext("BLE MIDI %d"), i), - ["api.bluez5.role"] = "server", - ["factory.name"] = "api.bluez5.midi.node", - ["api.glib.mainloop"] = "true", - } - rulesApplyProperties(node_props) - - local latency_offset = node_props["node.latency-offset-msec"] - node_props["node.latency-offset-msec"] = nil - - local node = LocalNode("spa-node-factory", node_props) - if node then - node:activate(Feature.Proxy.BOUND) - table.insert(servers, node) - setLatencyOffset(node, latency_offset) - else - Log.message("Failed to create BLE MIDI server.") - end - i = i + 1 - end - - return servers -end - -logind_plugin = Plugin.find("logind") -if logind_plugin then - -- if logind support is enabled, activate - -- the monitor only when the seat is active - function startStopMonitor(seat_state) - Log.info(logind_plugin, "Seat state changed: " .. seat_state) - - if seat_state == "active" then - monitor = createMonitor() - servers = createServers() - elseif monitor then - monitor:deactivate(Feature.SpaDevice.ENABLED) - monitor = nil - servers = nil - end - end - - logind_plugin:connect("state-changed", function(p, s) startStopMonitor(s) end) - startStopMonitor(logind_plugin:call("get-state")) -else - monitor = createMonitor() - servers = createServers() -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/bluez.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/bluez.lua deleted file mode 100644 index 4924ae00..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/bluez.lua +++ /dev/null @@ -1,428 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author George Kiagiadakis --- --- SPDX-License-Identifier: MIT - -local config = ... or {} -local COMBINE_OFFSET = 64 - -devices_om = ObjectManager { - Interest { - type = "device", - } -} - -nodes_om = ObjectManager { - Interest { - type = "node", - Constraint { "node.name", "#", "*.bluez_*put*"}, - Constraint { "device.id", "+" }, - } -} - --- preprocess rules and create Interest objects -for _, r in ipairs(config.rules or {}) do - r.interests = {} - for _, i in ipairs(r.matches) do - local interest_desc = { type = "properties" } - for _, c in ipairs(i) do - c.type = "pw" - table.insert(interest_desc, Constraint(c)) - end - local interest = Interest(interest_desc) - table.insert(r.interests, interest) - end - r.matches = nil -end - --- applies properties from config.rules when asked to -function rulesApplyProperties(properties) - for _, r in ipairs(config.rules or {}) do - if r.apply_properties then - for _, interest in ipairs(r.interests) do - if interest:matches(properties) then - for k, v in pairs(r.apply_properties) do - properties[k] = v - end - end - end - end - end -end - -function setOffloadActive(device, value) - local pod = Pod.Object { - "Spa:Pod:Object:Param:Props", "Props", bluetoothOffloadActive = value - } - device:set_params("Props", pod) -end - -nodes_om:connect("object-added", function(_, node) - node:connect("state-changed", function(node, old_state, cur_state) - local interest = Interest { - type = "device", - Constraint { "object.id", "=", node.properties["device.id"]} - } - for d in devices_om:iterate (interest) do - if cur_state == "running" then - setOffloadActive(d, true) - else - setOffloadActive(d, false) - end - end - end) -end) - -function createOffloadScoNode(parent, id, type, factory, properties) - local dev_props = parent.properties - - local args = { - ["audio.channels"] = 1, - ["audio.position"] = "[MONO]", - } - - local desc = - dev_props["device.description"] - or dev_props["device.name"] - or dev_props["device.nick"] - or dev_props["device.alias"] - or "bluetooth-device" - -- sanitize description, replace ':' with ' ' - args["node.description"] = desc:gsub("(:)", " ") - - if factory:find("sink") then - local capture_args = { - ["device.id"] = parent["bound-id"], - ["media.class"] = "Audio/Sink", - ["node.pause-on-idle"] = false, - } - for k, v in pairs(properties) do - capture_args[k] = v - end - - local name = "bluez_output" .. "." .. (properties["api.bluez5.address"] or dev_props["device.name"]) .. "." .. tostring(id) - args["node.name"] = name:gsub("([^%w_%-%.])", "_") - args["capture.props"] = Json.Object(capture_args) - args["playback.props"] = Json.Object { - ["node.passive"] = true, - ["node.pause-on-idle"] = false, - } - elseif factory:find("source") then - local playback_args = { - ["device.id"] = parent["bound-id"], - ["media.class"] = "Audio/Source", - ["node.pause-on-idle"] = false, - } - for k, v in pairs(properties) do - playback_args[k] = v - end - - local name = "bluez_input" .. "." .. (properties["api.bluez5.address"] or dev_props["device.name"]) .. "." .. tostring(id) - args["node.name"] = name:gsub("([^%w_%-%.])", "_") - args["capture.props"] = Json.Object { - ["node.passive"] = true, - ["node.pause-on-idle"] = false, - } - args["playback.props"] = Json.Object(playback_args) - else - Log.warning(parent, "Unsupported factory: " .. factory) - return - end - - -- Transform 'args' to a json object here - local args_json = Json.Object(args) - - -- and get the final JSON as a string from the json object - local args_string = args_json:get_data() - - local loopback_properties = {} - - local loopback = LocalModule("libpipewire-module-loopback", args_string, loopback_properties) - parent:store_managed_object(id, loopback) -end - -device_set_nodes_om = ObjectManager { - Interest { - type = "node", - Constraint { "api.bluez5.set.leader", "+", type = "pw" }, - } -} - -device_set_nodes_om:connect ("object-added", function(_, node) - -- Connect ObjectConfig events to the right node - if not monitor then - return - end - - local interest = Interest { - type = "device", - Constraint { "object.id", "=", node.properties["device.id"] } - } - Log.info("Device set node found: " .. tostring (node["bound-id"])) - for device in devices_om:iterate (interest) do - local device_id = device.properties["api.bluez5.id"] - if not device_id then - goto next_device - end - - local spa_device = monitor:get_managed_object (tonumber (device_id)) - if not spa_device then - goto next_device - end - - local id = node.properties["card.profile.device"] - if id ~= nil then - Log.info(".. assign to device: " .. tostring (device["bound-id"]) .. " node " .. tostring (id)) - spa_device:store_managed_object (id, node) - - -- set routes again to update volumes etc. - for route in device:iterate_params ("Route") do - device:set_param ("Route", route) - end - end - - ::next_device:: - end -end) - -function createSetNode(parent, id, type, factory, properties) - local args = {} - local name - local target_class - local stream_class - local rules = {} - local members_json = Json.Raw (properties["api.bluez5.set.members"]) - local channels_json = Json.Raw (properties["api.bluez5.set.channels"]) - local members = members_json:parse () - local channels = channels_json:parse () - - if properties["media.class"] == "Audio/Sink" then - name = "bluez_output" - args["combine.mode"] = "sink" - target_class = "Audio/Sink/Internal" - stream_class = "Stream/Output/Audio/Internal" - else - name = "bluez_input" - args["combine.mode"] = "source" - target_class = "Audio/Source/Internal" - stream_class = "Stream/Input/Audio/Internal" - end - - Log.info("Device set: " .. properties["node.name"]) - - for _, member in pairs(members) do - Log.info("Device set member:" .. member["object.path"]) - table.insert(rules, - Json.Object { - ["matches"] = Json.Array { - Json.Object { - ["object.path"] = member["object.path"], - ["media.class"] = target_class, - }, - }, - ["actions"] = Json.Object { - ["create-stream"] = Json.Object { - ["media.class"] = stream_class, - ["audio.position"] = Json.Array (member["channels"]), - } - }, - } - ) - end - - properties["node.virtual"] = false - properties["device.api"] = "bluez5" - properties["api.bluez5.set.members"] = nil - properties["api.bluez5.set.channels"] = nil - properties["api.bluez5.set.leader"] = true - properties["audio.position"] = Json.Array (channels) - args["combine.props"] = Json.Object (properties) - args["stream.props"] = Json.Object {} - args["stream.rules"] = Json.Array (rules) - - local args_json = Json.Object(args) - local args_string = args_json:get_data() - local combine_properties = {} - Log.info("Device set node: " .. args_string) - return LocalModule("libpipewire-module-combine-stream", args_string, combine_properties) -end - -function createNode(parent, id, type, factory, properties) - local dev_props = parent.properties - - if config.properties["bluez5.hw-offload-sco"] and factory:find("sco") then - createOffloadScoNode(parent, id, type, factory, properties) - return - end - - -- set the device id and spa factory name; REQUIRED, do not change - properties["device.id"] = parent["bound-id"] - properties["factory.name"] = factory - - -- set the default pause-on-idle setting - properties["node.pause-on-idle"] = false - - -- set the node description - local desc = - dev_props["device.description"] - or dev_props["device.name"] - or dev_props["device.nick"] - or dev_props["device.alias"] - or "bluetooth-device" - -- sanitize description, replace ':' with ' ' - properties["node.description"] = desc:gsub("(:)", " ") - - -- set the node name - local name = - ((factory:find("sink") and "bluez_output") or - (factory:find("source") and "bluez_input" or factory)) .. "." .. - (properties["api.bluez5.address"] or dev_props["device.name"]) .. "." .. - tostring(id) - -- sanitize name - properties["node.name"] = name:gsub("([^%w_%-%.])", "_") - - -- set priority - if not properties["priority.driver"] then - local priority = factory:find("source") and 2010 or 1010 - properties["priority.driver"] = priority - properties["priority.session"] = priority - end - - -- autoconnect if it's a stream - if properties["api.bluez5.profile"] == "headset-audio-gateway" or - properties["api.bluez5.profile"] == "bap-sink" or - factory:find("a2dp.source") or factory:find("media.source") then - properties["node.autoconnect"] = true - end - - -- apply properties from config.rules - rulesApplyProperties(properties) - - -- create the node; bluez requires "local" nodes, i.e. ones that run in - -- the same process as the spa device, for several reasons - - if properties["api.bluez5.set.leader"] then - local combine = createSetNode(parent, id, type, factory, properties) - parent:store_managed_object(id + COMBINE_OFFSET, combine) - else - local node = LocalNode("adapter", properties) - node:activate(Feature.Proxy.BOUND) - parent:store_managed_object(id, node) - end -end - -function removeNode(parent, id) - -- Clear also the device set module, if any - parent:store_managed_object(id + COMBINE_OFFSET, nil) -end - -function createDevice(parent, id, type, factory, properties) - local device = parent:get_managed_object(id) - if not device then - -- ensure a proper device name - local name = - (properties["device.name"] or - properties["api.bluez5.address"] or - properties["device.description"] or - tostring(id)):gsub("([^%w_%-%.])", "_") - - if not name:find("^bluez_card%.", 1) then - name = "bluez_card." .. name - end - properties["device.name"] = name - - -- set the icon name - if not properties["device.icon-name"] then - local icon = nil - local icon_map = { - -- form factor -> icon - ["microphone"] = "audio-input-microphone", - ["webcam"] = "camera-web", - ["handset"] = "phone", - ["portable"] = "multimedia-player", - ["tv"] = "video-display", - ["headset"] = "audio-headset", - ["headphone"] = "audio-headphones", - ["speaker"] = "audio-speakers", - ["hands-free"] = "audio-handsfree", - } - local f = properties["device.form-factor"] - local b = properties["device.bus"] - - icon = icon_map[f] or "audio-card" - properties["device.icon-name"] = icon .. (b and ("-" .. b) or "") - end - - -- initial profile is to be set by policy-device-profile.lua, not spa-bluez5 - properties["bluez5.profile"] = "off" - properties["api.bluez5.id"] = id - - -- apply properties from config.rules - rulesApplyProperties(properties) - - -- create the device - device = SpaDevice(factory, properties) - if device then - device:connect("create-object", createNode) - device:connect("object-removed", removeNode) - parent:store_managed_object(id, device) - else - Log.warning ("Failed to create '" .. factory .. "' device") - return - end - end - - Log.info(parent, string.format("%d, %s (%s): %s", - id, properties["device.description"], - properties["api.bluez5.address"], properties["api.bluez5.connection"])) - - -- activate the device after the bluez profiles are connected - if properties["api.bluez5.connection"] == "connected" then - device:activate(Feature.SpaDevice.ENABLED | Feature.Proxy.BOUND) - else - device:deactivate(Features.ALL) - end -end - -function createMonitor() - local monitor_props = config.properties or {} - monitor_props["api.bluez5.connection-info"] = true - - local monitor = SpaDevice("api.bluez5.enum.dbus", monitor_props) - if monitor then - monitor:connect("create-object", createDevice) - else - Log.message("PipeWire's BlueZ SPA missing or broken. Bluetooth not supported.") - return nil - end - monitor:activate(Feature.SpaDevice.ENABLED) - - return monitor -end - -logind_plugin = Plugin.find("logind") -if logind_plugin then - -- if logind support is enabled, activate - -- the monitor only when the seat is active - function startStopMonitor(seat_state) - Log.info(logind_plugin, "Seat state changed: " .. seat_state) - - if seat_state == "active" then - monitor = createMonitor() - elseif monitor then - monitor:deactivate(Feature.SpaDevice.ENABLED) - monitor = nil - end - end - - logind_plugin:connect("state-changed", function(p, s) startStopMonitor(s) end) - startStopMonitor(logind_plugin:call("get-state")) -else - monitor = createMonitor() -end - -nodes_om:activate() -devices_om:activate() -device_set_nodes_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/libcamera.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/libcamera.lua deleted file mode 100644 index 3f411f28..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/libcamera.lua +++ /dev/null @@ -1,174 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author George Kiagiadakis --- --- SPDX-License-Identifier: MIT - -local config = ... or {} - --- preprocess rules and create Interest objects -for _, r in ipairs(config.rules or {}) do - r.interests = {} - for _, i in ipairs(r.matches) do - local interest_desc = { type = "properties" } - for _, c in ipairs(i) do - c.type = "pw" - table.insert(interest_desc, Constraint(c)) - end - local interest = Interest(interest_desc) - table.insert(r.interests, interest) - end - r.matches = nil -end - --- applies properties from config.rules when asked to -function rulesApplyProperties(properties) - for _, r in ipairs(config.rules or {}) do - if r.apply_properties then - for _, interest in ipairs(r.interests) do - if interest:matches(properties) then - for k, v in pairs(r.apply_properties) do - properties[k] = v - end - end - end - end - end -end - -function findDuplicate(parent, id, property, value) - for i = 0, id - 1, 1 do - local obj = parent:get_managed_object(i) - if obj and obj.properties[property] == value then - return true - end - end - return false -end - -function createNode(parent, id, type, factory, properties) - local dev_props = parent.properties - local location = properties["api.libcamera.location"] - - -- set the device id and spa factory name; REQUIRED, do not change - properties["device.id"] = parent["bound-id"] - properties["factory.name"] = factory - - -- set the default pause-on-idle setting - properties["node.pause-on-idle"] = false - - -- set the node name - local name = - (factory:find("sink") and "libcamera_output") or - (factory:find("source") and "libcamera_input" or factory) - .. "." .. - (dev_props["device.name"]:gsub("^libcamera_device%.(.+)", "%1") or - dev_props["device.name"] or - dev_props["device.nick"] or - dev_props["device.alias"] or - "libcamera-device") - -- sanitize name - name = name:gsub("([^%w_%-%.])", "_") - - properties["node.name"] = name - - -- deduplicate nodes with the same name - for counter = 2, 99, 1 do - if findDuplicate(parent, id, "node.name", properties["node.name"]) then - properties["node.name"] = name .. "." .. counter - else - break - end - end - - -- set the node description - local desc = dev_props["device.description"] or "libcamera-device" - if location == "front" then - desc = I18n.gettext("Built-in Front Camera") - elseif location == "back" then - desc = I18n.gettext("Built-in Back Camera") - end - -- sanitize description, replace ':' with ' ' - properties["node.description"] = desc:gsub("(:)", " ") - - -- set the node nick - local nick = properties["node.nick"] or - dev_props["device.product.name"] or - dev_props["device.description"] or - dev_props["device.nick"] - properties["node.nick"] = nick:gsub("(:)", " ") - - -- set priority - if not properties["priority.session"] then - local priority = 700 - if location == "external" then - priority = priority + 150 - elseif location == "front" then - priority = priority + 100 - elseif location == "back" then - priority = priority + 50 - end - properties["priority.session"] = priority - end - - -- apply properties from config.rules - rulesApplyProperties(properties) - if properties ["node.disabled"] then - return - end - - -- create the node - local node = Node("spa-node-factory", properties) - node:activate(Feature.Proxy.BOUND) - parent:store_managed_object(id, node) -end - -function createDevice(parent, id, type, factory, properties) - -- ensure the device has an appropriate name - local name = "libcamera_device." .. - (properties["device.name"] or - properties["device.bus-id"] or - properties["device.bus-path"] or - tostring(id)):gsub("([^%w_%-%.])", "_") - - properties["device.name"] = name - - -- deduplicate devices with the same name - for counter = 2, 99, 1 do - if findDuplicate(parent, id, "device.name", properties["device.name"]) then - properties["device.name"] = name .. "." .. counter - else - break - end - end - - -- ensure the device has a description - properties["device.description"] = - properties["device.description"] - or properties["device.product.name"] - or "Unknown device" - - -- apply properties from config.rules - rulesApplyProperties(properties) - if properties ["device.disabled"] then - return - end - -- create the device - local device = SpaDevice(factory, properties) - if device then - device:connect("create-object", createNode) - device:activate(Feature.SpaDevice.ENABLED | Feature.Proxy.BOUND) - parent:store_managed_object(id, device) - else - Log.warning ("Failed to create '" .. factory .. "' device") - end -end - -monitor = SpaDevice("api.libcamera.enum.manager", config.properties or {}) -if monitor then - monitor:connect("create-object", createDevice) - monitor:activate(Feature.SpaDevice.ENABLED) -else - Log.message("PipeWire's libcamera SPA missing or broken. libcamera not supported.") -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/v4l2.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/v4l2.lua deleted file mode 100644 index 609d7e71..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/monitors/v4l2.lua +++ /dev/null @@ -1,165 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author George Kiagiadakis --- --- SPDX-License-Identifier: MIT - -local config = ... or {} - --- preprocess rules and create Interest objects -for _, r in ipairs(config.rules or {}) do - r.interests = {} - for _, i in ipairs(r.matches) do - local interest_desc = { type = "properties" } - for _, c in ipairs(i) do - c.type = "pw" - table.insert(interest_desc, Constraint(c)) - end - local interest = Interest(interest_desc) - table.insert(r.interests, interest) - end - r.matches = nil -end - --- applies properties from config.rules when asked to -function rulesApplyProperties(properties) - for _, r in ipairs(config.rules or {}) do - if r.apply_properties then - for _, interest in ipairs(r.interests) do - if interest:matches(properties) then - for k, v in pairs(r.apply_properties) do - properties[k] = v - end - end - end - end - end -end - -function findDuplicate(parent, id, property, value) - for i = 0, id - 1, 1 do - local obj = parent:get_managed_object(i) - if obj and obj.properties[property] == value then - return true - end - end - return false -end - -function createNode(parent, id, type, factory, properties) - local dev_props = parent.properties - - -- set the device id and spa factory name; REQUIRED, do not change - properties["device.id"] = parent["bound-id"] - properties["factory.name"] = factory - - -- set the default pause-on-idle setting - properties["node.pause-on-idle"] = false - - -- set the node name - local name = - (factory:find("sink") and "v4l2_output") or - (factory:find("source") and "v4l2_input" or factory) - .. "." .. - (dev_props["device.name"]:gsub("^v4l2_device%.(.+)", "%1") or - dev_props["device.name"] or - dev_props["device.nick"] or - dev_props["device.alias"] or - "v4l2-device") - -- sanitize name - name = name:gsub("([^%w_%-%.])", "_") - - properties["node.name"] = name - - -- deduplicate nodes with the same name - for counter = 2, 99, 1 do - if findDuplicate(parent, id, "node.name", properties["node.name"]) then - properties["node.name"] = name .. "." .. counter - else - break - end - end - - -- set the node description - local desc = dev_props["device.description"] or "v4l2-device" - desc = desc .. " (V4L2)" - -- sanitize description, replace ':' with ' ' - properties["node.description"] = desc:gsub("(:)", " ") - - -- set the node nick - local nick = properties["node.nick"] or - dev_props["device.product.name"] or - dev_props["api.v4l2.cap.card"] or - dev_props["device.description"] or - dev_props["device.nick"] - properties["node.nick"] = nick:gsub("(:)", " ") - - -- set priority - if not properties["priority.session"] then - local path = properties["api.v4l2.path"] or "/dev/video100" - local dev = path:gsub("/dev/video(%d+)", "%1") - properties["priority.session"] = 1000 - (tonumber(dev) * 10) - end - - -- apply properties from config.rules - rulesApplyProperties(properties) - if properties["node.disabled"] then - return - end - - -- create the node - local node = Node("spa-node-factory", properties) - node:activate(Feature.Proxy.BOUND) - parent:store_managed_object(id, node) -end - -function createDevice(parent, id, type, factory, properties) - -- ensure the device has an appropriate name - local name = "v4l2_device." .. - (properties["device.name"] or - properties["device.bus-id"] or - properties["device.bus-path"] or - tostring(id)):gsub("([^%w_%-%.])", "_") - - properties["device.name"] = name - - -- deduplicate devices with the same name - for counter = 2, 99, 1 do - if findDuplicate(parent, id, "device.name", properties["device.name"]) then - properties["device.name"] = name .. "." .. counter - else - break - end - end - - -- ensure the device has a description - properties["device.description"] = - properties["device.description"] - or properties["device.product.name"] - or "Unknown device" - - -- apply properties from config.rules - rulesApplyProperties(properties) - if properties["device.disabled"] then - return - end - - -- create the device - local device = SpaDevice(factory, properties) - if device then - device:connect("create-object", createNode) - device:activate(Feature.SpaDevice.ENABLED | Feature.Proxy.BOUND) - parent:store_managed_object(id, device) - else - Log.warning ("Failed to create '" .. factory .. "' device") - end -end - -monitor = SpaDevice("api.v4l2.enum.udev", config.properties or {}) -if monitor then - monitor:connect("create-object", createDevice) - monitor:activate(Feature.SpaDevice.ENABLED) -else - Log.message("PipeWire's V4L SPA missing or broken. Video4Linux not supported.") -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-bluetooth.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-bluetooth.lua deleted file mode 100644 index f8f69a14..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-bluetooth.lua +++ /dev/null @@ -1,398 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Asymptotic Inc. --- @author Sanchayan Maity --- --- Based on bt-profile-switch.lua in tests/examples --- Copyright © 2021 George Kiagiadakis --- --- Based on bluez-autoswitch in media-session --- Copyright © 2021 Pauli Virtanen --- --- SPDX-License-Identifier: MIT --- --- Checks for the existence of media.role and if present switches the bluetooth --- profile accordingly. Also see bluez-autoswitch in media-session. --- The intended logic of the script is as follows. --- --- When a stream comes in, if it has a Communication or phone role in PulseAudio --- speak in props, we switch to the highest priority profile that has an Input --- route available. The reason for this is that we may have microphone enabled --- non-HFP codecs eg. Faststream. --- We track the incoming streams with Communication role or the applications --- specified which do not set the media.role correctly perhaps. --- When a stream goes away if the list with which we track the streams above --- is empty, then we revert back to the old profile. - -local config = ... -local use_persistent_storage = config["use-persistent-storage"] or false -local applications = {} -local use_headset_profile = config["media-role.use-headset-profile"] or false -local profile_restore_timeout_msec = 2000 - -local INVALID = -1 -local timeout_source = nil -local restore_timeout_source = nil - -local state = use_persistent_storage and State("policy-bluetooth") or nil -local headset_profiles = state and state:load() or {} -local last_profiles = {} - -local active_streams = {} -local previous_streams = {} - -for _, value in ipairs(config["media-role.applications"] or {}) do - applications[value] = true -end - -metadata_om = ObjectManager { - Interest { - type = "metadata", - Constraint { "metadata.name", "=", "default" }, - } -} - -devices_om = ObjectManager { - Interest { - type = "device", - Constraint { "device.api", "=", "bluez5" }, - } -} - -streams_om = ObjectManager { - Interest { - type = "node", - Constraint { "media.class", "matches", "Stream/Input/Audio", type = "pw-global" }, - -- Do not consider monitor streams - Constraint { "stream.monitor", "!", "true" } - } -} - -local function parseParam(param_to_parse, id) - local param = param_to_parse:parse() - if param.pod_type == "Object" and param.object_id == id then - return param.properties - else - return nil - end -end - -local function storeAfterTimeout() - if not use_persistent_storage then - return - end - - if timeout_source then - timeout_source:destroy() - end - timeout_source = Core.timeout_add(1000, function () - local saved, err = state:save(headset_profiles) - if not saved then - Log.warning(err) - end - timeout_source = nil - end) -end - -local function saveHeadsetProfile(device, profile_name) - local key = "saved-headset-profile:" .. device.properties["device.name"] - headset_profiles[key] = profile_name - storeAfterTimeout() -end - -local function getSavedHeadsetProfile(device) - local key = "saved-headset-profile:" .. device.properties["device.name"] - return headset_profiles[key] -end - -local function saveLastProfile(device, profile_name) - last_profiles[device.properties["device.name"]] = profile_name -end - -local function getSavedLastProfile(device) - return last_profiles[device.properties["device.name"]] -end - -local function isSwitched(device) - return getSavedLastProfile(device) ~= nil -end - -local function isBluez5AudioSink(sink_name) - if sink_name and string.find(sink_name, "bluez_output.") ~= nil then - return true - end - return false -end - -local function isBluez5DefaultAudioSink() - local metadata = metadata_om:lookup() - local default_audio_sink = metadata:find(0, "default.audio.sink") - return isBluez5AudioSink(default_audio_sink) -end - -local function findProfile(device, index, name) - for p in device:iterate_params("EnumProfile") do - local profile = parseParam(p, "EnumProfile") - if not profile then - goto skip_enum_profile - end - - Log.debug("Profile name: " .. profile.name .. ", priority: " - .. tostring(profile.priority) .. ", index: " .. tostring(profile.index)) - if (index ~= nil and profile.index == index) or - (name ~= nil and profile.name == name) then - return profile.priority, profile.index, profile.name - end - - ::skip_enum_profile:: - end - - return INVALID, INVALID, nil -end - -local function getCurrentProfile(device) - for p in device:iterate_params("Profile") do - local profile = parseParam(p, "Profile") - if profile then - return profile.name - end - end - - return nil -end - -local function highestPrioProfileWithInputRoute(device) - local profile_priority = INVALID - local profile_index = INVALID - local profile_name = nil - - for p in device:iterate_params("EnumRoute") do - local route = parseParam(p, "EnumRoute") - -- Parse pod - if not route then - goto skip_enum_route - end - - if route.direction ~= "Input" then - goto skip_enum_route - end - - Log.debug("Route with index: " .. tostring(route.index) .. ", direction: " - .. route.direction .. ", name: " .. route.name .. ", description: " - .. route.description .. ", priority: " .. route.priority) - if route.profiles then - for _, v in pairs(route.profiles) do - local priority, index, name = findProfile(device, v) - if priority ~= INVALID then - if profile_priority < priority then - profile_priority = priority - profile_index = index - profile_name = name - end - end - end - end - - ::skip_enum_route:: - end - - return profile_priority, profile_index, profile_name -end - -local function hasProfileInputRoute(device, profile_index) - for p in device:iterate_params("EnumRoute") do - local route = parseParam(p, "EnumRoute") - if route and route.direction == "Input" and route.profiles then - for _, v in pairs(route.profiles) do - if v == profile_index then - return true - end - end - end - end - return false -end - -local function switchProfile() - local index - local name - - if restore_timeout_source then - restore_timeout_source:destroy() - restore_timeout_source = nil - end - - for device in devices_om:iterate() do - if isSwitched(device) then - goto skip_device - end - - local cur_profile_name = getCurrentProfile(device) - saveLastProfile(device, cur_profile_name) - - _, index, name = findProfile(device, nil, cur_profile_name) - if hasProfileInputRoute(device, index) then - Log.info("Current profile has input route, not switching") - goto skip_device - end - - local saved_headset_profile = getSavedHeadsetProfile(device) - index = INVALID - if saved_headset_profile then - _, index, name = findProfile(device, nil, saved_headset_profile) - end - if index == INVALID then - _, index, name = highestPrioProfileWithInputRoute(device) - end - - if index ~= INVALID then - local pod = Pod.Object { - "Spa:Pod:Object:Param:Profile", "Profile", - index = index - } - - Log.info("Setting profile of '" - .. device.properties["device.description"] - .. "' from: " .. cur_profile_name - .. " to: " .. name) - device:set_params("Profile", pod) - else - Log.warning("Got invalid index when switching profile") - end - - ::skip_device:: - end -end - -local function restoreProfile() - for device in devices_om:iterate() do - if isSwitched(device) then - local profile_name = getSavedLastProfile(device) - local cur_profile_name = getCurrentProfile(device) - - saveLastProfile(device, nil) - - if cur_profile_name then - Log.info("Setting saved headset profile to: " .. cur_profile_name) - saveHeadsetProfile(device, cur_profile_name) - end - - if profile_name then - local _, index, name = findProfile(device, nil, profile_name) - - if index ~= INVALID then - local pod = Pod.Object { - "Spa:Pod:Object:Param:Profile", "Profile", - index = index - } - - Log.info("Restoring profile of '" - .. device.properties["device.description"] - .. "' from: " .. cur_profile_name - .. " to: " .. name) - device:set_params("Profile", pod) - else - Log.warning("Failed to restore profile") - end - end - end - end -end - -local function triggerRestoreProfile() - if restore_timeout_source then - return - end - if next(active_streams) ~= nil then - return - end - restore_timeout_source = Core.timeout_add(profile_restore_timeout_msec, function () - restore_timeout_source = nil - restoreProfile() - end) -end - --- We consider a Stream of interest to have role Communication if it has --- media.role set to Communication in props or it is in our list of --- applications as these applications do not set media.role correctly or at --- all. -local function checkStreamStatus(stream) - local app_name = stream.properties["application.name"] - local stream_role = stream.properties["media.role"] - - if not (stream_role == "Communication" or applications[app_name]) then - return false - end - if not isBluez5DefaultAudioSink() then - return false - end - - -- If a stream we previously saw stops running, we consider it - -- inactive, because some applications (Teams) just cork input - -- streams, but don't close them. - if previous_streams[stream["bound-id"]] and stream.state ~= "running" then - return false - end - - return true -end - -local function handleStream(stream) - if not use_headset_profile then - return - end - - if checkStreamStatus(stream) then - active_streams[stream["bound-id"]] = true - previous_streams[stream["bound-id"]] = true - switchProfile() - else - active_streams[stream["bound-id"]] = nil - triggerRestoreProfile() - end -end - -local function handleAllStreams() - for stream in streams_om:iterate { - Constraint { "media.class", "matches", "Stream/Input/Audio", type = "pw-global" }, - Constraint { "stream.monitor", "!", "true" } - } do - handleStream(stream) - end -end - -streams_om:connect("object-added", function (_, stream) - stream:connect("state-changed", function (stream, old_state, cur_state) - handleStream(stream) - end) - stream:connect("params-changed", handleStream) - handleStream(stream) -end) - -streams_om:connect("object-removed", function (_, stream) - active_streams[stream["bound-id"]] = nil - previous_streams[stream["bound-id"]] = nil - triggerRestoreProfile() -end) - -devices_om:connect("object-added", function (_, device) - -- Devices are unswitched initially - if isSwitched(device) then - saveLastProfile(device, nil) - end - handleAllStreams() -end) - -metadata_om:connect("object-added", function (_, metadata) - metadata:connect("changed", function (m, subject, key, t, value) - if (use_headset_profile and subject == 0 and key == "default.audio.sink" - and isBluez5AudioSink(value)) then - -- If bluez sink is set as default, rescan for active input streams - handleAllStreams() - end - end) -end) - -metadata_om:activate() -devices_om:activate() -streams_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-device-profile.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-device-profile.lua deleted file mode 100644 index af10f9b7..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-device-profile.lua +++ /dev/null @@ -1,246 +0,0 @@ --- WirePlumber --- --- Copyright © 2022 Collabora Ltd. --- @author Julian Bouzas --- --- SPDX-License-Identifier: MIT - -local self = {} -self.config = ... or {} -self.config.persistent = self.config.persistent or {} -self.config.priorities = self.config.priorities or {} -self.active_profiles = {} -self.default_profile_plugin = Plugin.find("default-profile") - -function createIntrestObjects(t) - for _, p in ipairs(t or {}) do - p.interests = {} - for _, i in ipairs(p.matches) do - local interest_desc = { type = "properties" } - for _, c in ipairs(i) do - c.type = "pw" - table.insert(interest_desc, Constraint(c)) - end - local interest = Interest(interest_desc) - table.insert(p.interests, interest) - end - p.matches = nil - end -end - --- Preprocess persistent profiles and create Interest objects -createIntrestObjects(self.config.persistent) --- Preprocess profile priorities and create Interest objects -createIntrestObjects(self.config.priorities) - --- Checks whether a device profile is persistent or not -function isProfilePersistent(device_props, profile_name) - for _, p in ipairs(self.config.persistent or {}) do - if p.profile_names then - for _, interest in ipairs(p.interests) do - if interest:matches(device_props) then - for _, pn in ipairs(p.profile_names) do - if pn == profile_name then - return true - end - end - end - end - end - end - return false -end - -function parseParam(param, id) - local parsed = param:parse() - if parsed.pod_type == "Object" and parsed.object_id == id then - return parsed.properties - else - return nil - end -end - -function setDeviceProfile (device, dev_id, dev_name, profile) - if self.active_profiles[dev_id] and - self.active_profiles[dev_id].index == profile.index then - Log.info ("Profile " .. profile.name .. " is already set in " .. dev_name) - return - end - - local param = Pod.Object { - "Spa:Pod:Object:Param:Profile", "Profile", - index = profile.index, - } - Log.info ("Setting profile " .. profile.name .. " on " .. dev_name) - device:set_param("Profile", param) -end - -function findDefaultProfile (device) - local def_name = nil - - if self.default_profile_plugin ~= nil then - def_name = self.default_profile_plugin:call ("get-profile", device) - end - if def_name == nil then - return nil - end - - for p in device:iterate_params("EnumProfile") do - local profile = parseParam(p, "EnumProfile") - if profile.name == def_name then - return profile - end - end - - return nil -end - --- returns the priorities, if defined -function getDevicePriorities(device_props, profile_name) - for _, p in ipairs(self.config.priorities or {}) do - for _, interest in ipairs(p.interests) do - if interest:matches(device_props) then - return p.priorities - end - end - end - - return nil -end - --- find profiles based on user preferences. -function findPreferredProfile(device) - local priority_table = getDevicePriorities(device.properties) - - if not priority_table or #priority_table == 0 then - return nil - else - Log.info("priority table found for device " .. - device.properties["device.name"]) - end - - for _, priority_profile in ipairs(priority_table) do - for p in device:iterate_params("EnumProfile") do - device_profile = parseParam(p, "EnumProfile") - if device_profile.name == priority_profile then - Log.info("Selected user preferred profile " .. - device_profile.name .. " for " .. device.properties["device.name"]) - return device_profile - end - end - end - - return nil -end - --- find profiles based on inbuilt priorities. -function findBestProfile(device) - -- Takes absolute priority if available or unknown - local profile_prop = device.properties["device.profile"] - local off_profile = nil - local best_profile = nil - local unk_profile = nil - local profile = nil - - for p in device:iterate_params("EnumProfile") do - profile = parseParam(p, "EnumProfile") - if profile and profile.name == profile_prop and profile.available ~= "no" then - return profile - elseif profile and profile.name ~= "pro-audio" then - if profile.name == "off" then - off_profile = profile - elseif profile.available == "yes" then - if best_profile == nil or profile.priority > best_profile.priority then - best_profile = profile - end - elseif profile.available ~= "no" then - if unk_profile == nil or profile.priority > unk_profile.priority then - unk_profile = profile - end - end - end - end - - if best_profile ~= nil then - profile = best_profile - elseif unk_profile ~= nil then - profile = unk_profile - elseif off_profile ~= nil then - profile = off_profile - end - - if profile ~= nil then - Log.info("Found best profile " .. profile.name .. " for " .. device.properties["device.name"]) - return profile - else - return nil - end -end - -function handleProfiles (device, new_device) - local dev_id = device["bound-id"] - local dev_name = device.properties["device.name"] - - local def_profile = findDefaultProfile (device) - - -- Do not do anything if active profile is both persistent and default - if not new_device and - self.active_profiles[dev_id] ~= nil and - isProfilePersistent (device.properties, self.active_profiles[dev_id].name) and - def_profile ~= nil and - self.active_profiles[dev_id].name == def_profile.name - then - local active_profile = self.active_profiles[dev_id].name - Log.info ("Device profile " .. active_profile .. " is persistent for " .. dev_name) - return - end - - if def_profile ~= nil then - if def_profile.available == "no" then - Log.info ("Default profile " .. def_profile.name .. " unavailable for " .. dev_name) - else - Log.info ("Found default profile " .. def_profile.name .. " for " .. dev_name) - setDeviceProfile (device, dev_id, dev_name, def_profile) - return - end - else - Log.info ("Default profile not found for " .. dev_name) - end - - local best_profile = findPreferredProfile(device) - - if not best_profile then - best_profile = findBestProfile(device) - end - - if best_profile ~= nil then - setDeviceProfile (device, dev_id, dev_name, best_profile) - else - Log.info ("Best profile not found on " .. dev_name) - end -end - -function onDeviceParamsChanged (device, param_name) - if param_name == "EnumProfile" then - handleProfiles (device, false) - end -end - -self.om = ObjectManager { - Interest { - type = "device", - Constraint { "device.name", "is-present", type = "pw-global" }, - } -} - -self.om:connect("object-added", function (_, device) - device:connect ("params-changed", onDeviceParamsChanged) - handleProfiles (device, true) -end) - -self.om:connect("object-removed", function (_, device) - local dev_id = device["bound-id"] - self.active_profiles[dev_id] = nil -end) - -self.om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-device-routes.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-device-routes.lua deleted file mode 100644 index c21c1cce..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-device-routes.lua +++ /dev/null @@ -1,487 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author George Kiagiadakis --- --- Based on default-routes.c from pipewire-media-session --- Copyright © 2020 Wim Taymans --- --- SPDX-License-Identifier: MIT - -local config = ... or {} - --- whether to store state on the file system -use_persistent_storage = config["use-persistent-storage"] or false - --- the default volume to apply -default_volume = tonumber(config["default-volume"] or 0.4^3) -default_input_volume = tonumber(config["default-input-volume"] or 1.0) - --- table of device info -dev_infos = {} - --- the state storage -state = use_persistent_storage and State("default-routes") or nil -state_table = state and state:load() or {} - --- simple serializer {"foo", "bar"} -> "foo;bar;" -function serializeArray(a) - local str = "" - for _, v in ipairs(a) do - str = str .. tostring(v):gsub(";", "\\;") .. ";" - end - return str -end - --- simple deserializer "foo;bar;" -> {"foo", "bar"} -function parseArray(str, convert_value) - local array = {} - local val = "" - local escaped = false - for i = 1, #str do - local c = str:sub(i,i) - if c == '\\' then - escaped = true - elseif c == ';' and not escaped then - val = convert_value and convert_value(val) or val - table.insert(array, val) - val = "" - else - val = val .. tostring(c) - escaped = false - end - end - return array -end - -function arrayContains(a, value) - for _, v in ipairs(a) do - if v == value then - return true - end - end - return false -end - -function parseParam(param, id) - local route = param:parse() - if route.pod_type == "Object" and route.object_id == id then - return route.properties - else - return nil - end -end - -function storeAfterTimeout() - if timeout_source then - timeout_source:destroy() - end - timeout_source = Core.timeout_add(1000, function () - local saved, err = state:save(state_table) - if not saved then - Log.warning(err) - end - timeout_source = nil - end) -end - -function saveProfile(dev_info, profile_name) - if not use_persistent_storage then - return - end - - local routes = {} - for idx, ri in pairs(dev_info.route_infos) do - if ri.save then - table.insert(routes, ri.name) - end - end - - if #routes > 0 then - local key = dev_info.name .. ":profile:" .. profile_name - state_table[key] = serializeArray(routes) - storeAfterTimeout() - end -end - -function saveRouteProps(dev_info, route) - if not use_persistent_storage or not route.props then - return - end - - local props = route.props.properties - local key_base = dev_info.name .. ":" .. - route.direction:lower() .. ":" .. - route.name .. ":" - - state_table[key_base .. "volume"] = - props.volume and tostring(props.volume) or nil - state_table[key_base .. "mute"] = - props.mute and tostring(props.mute) or nil - state_table[key_base .. "channelVolumes"] = - props.channelVolumes and serializeArray(props.channelVolumes) or nil - state_table[key_base .. "channelMap"] = - props.channelMap and serializeArray(props.channelMap) or nil - state_table[key_base .. "latencyOffsetNsec"] = - props.latencyOffsetNsec and tostring(props.latencyOffsetNsec) or nil - state_table[key_base .. "iec958Codecs"] = - props.iec958Codecs and serializeArray(props.iec958Codecs) or nil - - storeAfterTimeout() -end - -function restoreRoute(device, dev_info, device_id, route) - -- default props - local props = { - "Spa:Pod:Object:Param:Props", "Route", - mute = false, - } - - if route.direction == "Input" then - props.channelVolumes = { default_input_volume } - else - props.channelVolumes = { default_volume } - end - - -- restore props from persistent storage - if use_persistent_storage then - local key_base = dev_info.name .. ":" .. - route.direction:lower() .. ":" .. - route.name .. ":" - - local str = state_table[key_base .. "volume"] - props.volume = str and tonumber(str) or props.volume - - local str = state_table[key_base .. "mute"] - props.mute = str and (str == "true") or false - - local str = state_table[key_base .. "channelVolumes"] - props.channelVolumes = str and parseArray(str, tonumber) or props.channelVolumes - - local str = state_table[key_base .. "channelMap"] - props.channelMap = str and parseArray(str) or props.channelMap - - local str = state_table[key_base .. "latencyOffsetNsec"] - props.latencyOffsetNsec = str and math.tointeger(str) or props.latencyOffsetNsec - - local str = state_table[key_base .. "iec958Codecs"] - props.iec958Codecs = str and parseArray(str) or props.iec958Codecs - end - - -- convert arrays to Spa Pod - if props.channelVolumes then - table.insert(props.channelVolumes, 1, "Spa:Float") - props.channelVolumes = Pod.Array(props.channelVolumes) - end - if props.channelMap then - table.insert(props.channelMap, 1, "Spa:Enum:AudioChannel") - props.channelMap = Pod.Array(props.channelMap) - end - if props.iec958Codecs then - table.insert(props.iec958Codecs, 1, "Spa:Enum:AudioIEC958Codec") - props.iec958Codecs = Pod.Array(props.iec958Codecs) - end - - -- construct Route param - local param = Pod.Object { - "Spa:Pod:Object:Param:Route", "Route", - index = route.index, - device = device_id, - props = Pod.Object(props), - save = route.save, - } - - Log.debug(param, "setting route on " .. tostring(device)) - device:set_param("Route", param) - - route.prev_active = true - route.active = true -end - -function findActiveDeviceIDs(profile) - -- parses the classes from the profile and returns the device IDs - ----- sample structure, should return { 0, 8 } ----- - -- classes: - -- 1: 2 - -- 2: - -- 1: Audio/Source - -- 2: 1 - -- 3: card.profile.devices - -- 4: - -- 1: 0 - -- pod_type: Array - -- value_type: Spa:Int - -- pod_type: Struct - -- 3: - -- 1: Audio/Sink - -- 2: 1 - -- 3: card.profile.devices - -- 4: - -- 1: 8 - -- pod_type: Array - -- value_type: Spa:Int - -- pod_type: Struct - -- pod_type: Struct - local active_ids = {} - if type(profile.classes) == "table" and profile.classes.pod_type == "Struct" then - for _, p in ipairs(profile.classes) do - if type(p) == "table" and p.pod_type == "Struct" then - local i = 1 - while true do - local k, v = p[i], p[i+1] - i = i + 2 - if not k or not v then - break - end - if k == "card.profile.devices" and - type(v) == "table" and v.pod_type == "Array" then - for _, dev_id in ipairs(v) do - table.insert(active_ids, dev_id) - end - end - end - end - end - end - return active_ids -end - --- returns an array of the route names that were previously selected --- for the given device and profile -function getStoredProfileRoutes(dev_name, profile_name) - local key = dev_name .. ":profile:" .. profile_name - local str = state_table[key] - return str and parseArray(str) or {} -end - --- find a route that was previously stored for a device_id --- spr needs to be the array returned from getStoredProfileRoutes() -function findSavedRoute(dev_info, device_id, spr) - for idx, ri in pairs(dev_info.route_infos) do - if arrayContains(ri.devices, device_id) and - (ri.profiles == nil or arrayContains(ri.profiles, dev_info.active_profile)) and - arrayContains(spr, ri.name) then - return ri - end - end - return nil -end - --- find the best route for a given device_id, based on availability and priority -function findBestRoute(dev_info, device_id) - local best_avail = nil - local best_unk = nil - for idx, ri in pairs(dev_info.route_infos) do - if arrayContains(ri.devices, device_id) and - (ri.profiles == nil or arrayContains(ri.profiles, dev_info.active_profile)) then - if ri.available == "yes" or ri.available == "unknown" then - if ri.direction == "Output" and ri.available ~= ri.prev_available then - best_avail = ri - ri.save = true - break - elseif ri.available == "yes" then - if (best_avail == nil or ri.priority > best_avail.priority) then - best_avail = ri - end - elseif best_unk == nil or ri.priority > best_unk.priority then - best_unk = ri - end - end - end - end - return best_avail or best_unk -end - -function restoreProfileRoutes(device, dev_info, profile, profile_changed) - Log.info(device, "restore routes for profile " .. profile.name) - - local active_ids = findActiveDeviceIDs(profile) - local spr = getStoredProfileRoutes(dev_info.name, profile.name) - - for _, device_id in ipairs(active_ids) do - Log.info(device, "restoring device " .. device_id); - - local route = nil - - -- restore routes selection for the newly selected profile - -- don't bother if spr is empty, there is no point - if profile_changed and #spr > 0 then - route = findSavedRoute(dev_info, device_id, spr) - if route then - -- we found a saved route - if route.available == "no" then - Log.info(device, "saved route '" .. route.name .. "' not available") - -- not available, try to find next best - route = nil - else - Log.info(device, "found saved route: " .. route.name) - -- make sure we save it again - route.save = true - end - end - end - - -- we could not find a saved route, try to find a new best - if not route then - route = findBestRoute(dev_info, device_id) - if not route then - Log.info(device, "can't find best route") - else - Log.info(device, "found best route: " .. route.name) - end - end - - -- restore route - if route then - restoreRoute(device, dev_info, device_id, route) - end - end -end - -function findRouteInfo(dev_info, route, return_new) - local ri = dev_info.route_infos[route.index] - if not ri and return_new then - ri = { - index = route.index, - name = route.name, - direction = route.direction, - devices = route.devices or {}, - profiles = route.profiles, - priority = route.priority or 0, - available = route.available or "unknown", - prev_available = route.available or "unknown", - active = false, - prev_active = false, - save = false, - } - end - return ri -end - -function handleDevice(device) - local dev_info = dev_infos[device["bound-id"]] - local new_route_infos = {} - local avail_routes_changed = false - local profile = nil - - -- get current profile - for p in device:iterate_params("Profile") do - profile = parseParam(p, "Profile") - end - - -- look at all the routes and update/reset cached information - for p in device:iterate_params("EnumRoute") do - -- parse pod - local route = parseParam(p, "EnumRoute") - if not route then - goto skip_enum_route - end - - -- find cached route information - local route_info = findRouteInfo(dev_info, route, true) - - -- update properties - route_info.prev_available = route_info.available - if route_info.available ~= route.available then - Log.info(device, "route " .. route.name .. " available changed " .. - route_info.available .. " -> " .. route.available) - route_info.available = route.available - if profile and arrayContains(route.profiles, profile.index) then - avail_routes_changed = true - end - end - route_info.prev_active = route_info.active - route_info.active = false - route_info.save = false - - -- store - new_route_infos[route.index] = route_info - - ::skip_enum_route:: - end - - -- replace old route_infos to lose old routes - -- that no longer exist on the device - dev_info.route_infos = new_route_infos - new_route_infos = nil - - -- check for changes in the active routes - for p in device:iterate_params("Route") do - local route = parseParam(p, "Route") - if not route then - goto skip_route - end - - -- get cached route info and at the same time - -- ensure that the route is also in EnumRoute - local route_info = findRouteInfo(dev_info, route, false) - if not route_info then - goto skip_route - end - - -- update state - route_info.active = true - route_info.save = route.save - - if not route_info.prev_active then - -- a new route is now active, restore the volume and - -- make sure we save this as a preferred route - Log.info(device, "new active route found " .. route.name) - restoreRoute(device, dev_info, route.device, route_info) - elseif route.save then - -- just save route properties - Log.info(device, "storing route props for " .. route.name) - saveRouteProps(dev_info, route) - end - - ::skip_route:: - end - - -- restore routes for profile - if profile then - local profile_changed = (dev_info.active_profile ~= profile.index) - - -- if the profile changed, restore routes for that profile - -- if any of the routes of the current profile changed in availability, - -- then try to select a new "best" route for each device and ignore - -- what was stored - if profile_changed or avail_routes_changed then - dev_info.active_profile = profile.index - restoreProfileRoutes(device, dev_info, profile, profile_changed) - end - - saveProfile(dev_info, profile.name) - end -end - -om = ObjectManager { - Interest { - type = "device", - Constraint { "device.name", "is-present", type = "pw-global" }, - } -} - -om:connect("objects-changed", function (om) - local new_dev_infos = {} - for device in om:iterate() do - local dev_info = dev_infos[device["bound-id"]] - -- new device appeared - if not dev_info then - dev_info = { - name = device.properties["device.name"], - active_profile = -1, - route_infos = {}, - } - dev_infos[device["bound-id"]] = dev_info - - device:connect("params-changed", handleDevice) - handleDevice(device) - end - - new_dev_infos[device["bound-id"]] = dev_info - end - -- replace list to get rid of dev_info for devices that no longer exist - dev_infos = new_dev_infos -end) - -om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-dsp.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-dsp.lua deleted file mode 100644 index ce23a67a..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-dsp.lua +++ /dev/null @@ -1,86 +0,0 @@ --- WirePlumber --- --- Copyright © 2022-2023 The WirePlumber project contributors --- @author Dmitry Sharshakov --- --- SPDX-License-Identifier: MIT - -local config = ... or {} -config.rules = config.rules or {} - -for _, r in ipairs(config.rules) do - r.interests = {} - for _, i in ipairs(r.matches) do - local interest_desc = { type = "properties" } - - for _, c in ipairs(i) do - c.type = "pw" - table.insert(interest_desc, Constraint(c)) - end - - local interest = Interest(interest_desc) - table.insert(r.interests, interest) - end -end - --- TODO: only check for hotplug of nodes with known DSP rules -nodes_om = ObjectManager { - Interest { type = "node" }, -} - -clients_om = ObjectManager { - Interest { type = "client" } -} - -filter_chains = {} -hidden_nodes = {} - -nodes_om:connect("object-added", function (om, node) - for _, r in ipairs(config.rules or {}) do - for _, interest in ipairs(r.interests) do - if interest:matches(node["global-properties"]) then - local id = node["global-properties"]["object.id"] - - if r.filter_chain then - if filter_chains[id] then - Log.warning("Sink " .. id .. " has been plugged now, but has a filter chain loaded. Skipping") - else - filter_chains[id] = LocalModule("libpipewire-module-filter-chain", r.filter_chain, {}, true) - end - end - - if r.hide_parent then - Log.debug("Hiding node " .. node["bound-id"] .. " from clients") - for client in clients_om:iterate { type = "client" } do - if not client["properties"]["wireplumber.daemon"] then - client:update_permissions { [node["bound-id"]] = "-" } - end - end - hidden_nodes[node["bound-id"]] = id - end - - end - end - end -end) - -nodes_om:connect("object-removed", function (om, node) - local id = node["global-properties"]["object.id"] - if filter_chains[id] then - Log.debug("Unloading filter chain associated with sink " .. id) - filter_chains[id] = nil - else - Log.debug("Disconnected sink " .. id .. " does not have any filters to be removed") - end -end) - -clients_om:connect("object-added", function (om, client) - for id, _ in pairs(hidden_nodes) do - if not client["properties"]["wireplumber.daemon"] then - client:update_permissions { [id] = "-" } - end - end -end) - -nodes_om:activate() -clients_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-client-links.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-client-links.lua deleted file mode 100644 index eaa1c088..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-client-links.lua +++ /dev/null @@ -1,218 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author George Kiagiadakis --- --- SPDX-License-Identifier: MIT - -local config = ... or {} -config.roles = config.roles or {} -config["duck.level"] = config["duck.level"] or 0.3 - -function findRole(role) - if role and not config.roles[role] then - for r, p in pairs(config.roles) do - if type(p.alias) == "table" then - for i = 1, #(p.alias), 1 do - if role == p.alias[i] then - return r - end - end - end - end - end - return role -end - -function priorityForRole(role) - local r = role and config.roles[role] or nil - return r and r.priority or 0 -end - -function getAction(dominant_role, other_role) - -- default to "mix" if the role is not configured - if not dominant_role or not config.roles[dominant_role] then - return "mix" - end - - local role_config = config.roles[dominant_role] - return role_config["action." .. other_role] - or role_config["action.default"] - or "mix" -end - -function restoreVolume(role, media_class) - if not mixer_api then return end - - local ep = endpoints_om:lookup { - Constraint { "media.role", "=", role, type = "pw" }, - Constraint { "media.class", "=", media_class, type = "pw" }, - } - - if ep and ep.properties["node.id"] then - Log.debug(ep, "restore role " .. role) - mixer_api:call("set-volume", ep.properties["node.id"], { - monitorVolume = 1.0, - }) - end -end - -function duck(role, media_class) - if not mixer_api then return end - - local ep = endpoints_om:lookup { - Constraint { "media.role", "=", role, type = "pw" }, - Constraint { "media.class", "=", media_class, type = "pw" }, - } - - if ep and ep.properties["node.id"] then - Log.debug(ep, "duck role " .. role) - mixer_api:call("set-volume", ep.properties["node.id"], { - monitorVolume = config["duck.level"], - }) - end -end - -function getSuspendPlaybackMetadata () - local suspend = false - local metadata = metadata_om:lookup() - if metadata then - local value = metadata:find(0, "suspend.playback") - if value then - suspend = value == "1" and true or false - end - end - return suspend -end - -function rescan() - local links = { - ["Audio/Source"] = {}, - ["Audio/Sink"] = {}, - ["Video/Source"] = {}, - } - - Log.info("Rescan endpoint links") - - -- deactivate all links if suspend playback metadata is present - local suspend = getSuspendPlaybackMetadata() - for silink in silinks_om:iterate() do - if suspend then - silink:deactivate(Feature.SessionItem.ACTIVE) - end - end - - -- gather info about links - for silink in silinks_om:iterate() do - local props = silink.properties - local role = props["media.role"] - local target_class = props["target.media.class"] - local plugged = props["item.plugged.usec"] - local active = - ((silink:get_active_features() & Feature.SessionItem.ACTIVE) ~= 0) - if links[target_class] then - table.insert(links[target_class], { - silink = silink, - role = findRole(role), - active = active, - priority = priorityForRole(role), - plugged = plugged and tonumber(plugged) or 0 - }) - end - end - - local function compareLinks(l1, l2) - return (l1.priority > l2.priority) or - ((l1.priority == l2.priority) and (l1.plugged > l2.plugged)) - end - - for media_class, v in pairs(links) do - -- sort on priority and stream creation time - table.sort(v, compareLinks) - - -- apply actions - local first_link = v[1] - if first_link then - for i = 2, #v, 1 do - local action = getAction(first_link.role, v[i].role) - if action == "cork" then - if v[i].active then - v[i].silink:deactivate(Feature.SessionItem.ACTIVE) - end - elseif action == "mix" then - if not v[i].active and not suspend then - v[i].silink:activate(Feature.SessionItem.ACTIVE, pendingOperation()) - end - restoreVolume(v[i].role, media_class) - elseif action == "duck" then - if not v[i].active and not suspend then - v[i].silink:activate(Feature.SessionItem.ACTIVE, pendingOperation()) - end - duck(v[i].role, media_class) - else - Log.warning("Unknown action: " .. action) - end - end - - if not first_link.active and not suspend then - first_link.silink:activate(Feature.SessionItem.ACTIVE, pendingOperation()) - end - restoreVolume(first_link.role, media_class) - end - end -end - -pending_ops = 0 -pending_rescan = false - -function pendingOperation() - pending_ops = pending_ops + 1 - return function() - pending_ops = pending_ops - 1 - if pending_ops == 0 and pending_rescan then - pending_rescan = false - rescan() - end - end -end - -function maybeRescan() - if pending_ops == 0 then - rescan() - else - pending_rescan = true - end -end - -silinks_om = ObjectManager { - Interest { - type = "SiLink", - Constraint { "is.policy.endpoint.client.link", "=", true }, - }, -} -silinks_om:connect("objects-changed", maybeRescan) -silinks_om:activate() - --- enable ducking if mixer-api is loaded -mixer_api = Plugin.find("mixer-api") -if mixer_api then - endpoints_om = ObjectManager { - Interest { type = "endpoint" }, - } - endpoints_om:activate() -end - -metadata_om = ObjectManager { - Interest { - type = "metadata", - Constraint { "metadata.name", "=", "default" }, - } -} -metadata_om:connect("object-added", function (om, metadata) - metadata:connect("changed", function (m, subject, key, t, value) - if key == "suspend.playback" then - maybeRescan() - end - end) -end) -metadata_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-client.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-client.lua deleted file mode 100644 index 0b71b028..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-client.lua +++ /dev/null @@ -1,261 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author Julian Bouzas --- --- SPDX-License-Identifier: MIT - --- Receive script arguments from config.lua -local config = ... or {} -config.roles = config.roles or {} - -local self = {} -self.scanning = false -self.pending_rescan = false - -function rescan () - for si in linkables_om:iterate() do - handleLinkable (si) - end -end - -function scheduleRescan () - if self.scanning then - self.pending_rescan = true - return - end - - self.scanning = true - rescan () - self.scanning = false - - if self.pending_rescan then - self.pending_rescan = false - Core.sync(function () - scheduleRescan () - end) - end -end - -function findRole(role, tmc) - if role and not config.roles[role] then - -- find the role with matching alias - for r, p in pairs(config.roles) do - -- default media class can be overridden in the role config data - mc = p["media.class"] or "Audio/Sink" - if (type(p.alias) == "table" and tmc == mc) then - for i = 1, #(p.alias), 1 do - if role == p.alias[i] then - return r - end - end - end - end - - -- otherwise get the lowest priority role - local lowest_priority_p = nil - local lowest_priority_r = nil - for r, p in pairs(config.roles) do - mc = p["media.class"] or "Audio/Sink" - if tmc == mc and (lowest_priority_p == nil or - p.priority < lowest_priority_p.priority) then - lowest_priority_p = p - lowest_priority_r = r - end - end - return lowest_priority_r - end - return role -end - -function findTargetEndpoint (node, media_class, role) - local target_class_assoc = { - ["Stream/Input/Audio"] = "Audio/Source", - ["Stream/Output/Audio"] = "Audio/Sink", - ["Stream/Input/Video"] = "Video/Source", - } - local media_role = nil - local highest_priority = -1 - local target = nil - - -- get target media class - local target_media_class = target_class_assoc[media_class] - if not target_media_class then - return nil - end - - -- find highest priority endpoint by role - media_role = findRole(role, target_media_class) - for si_target_ep in endpoints_om:iterate { - Constraint { "role", "=", media_role, type = "pw-global" }, - Constraint { "media.class", "=", target_media_class, type = "pw-global" }, - } do - local priority = tonumber(si_target_ep.properties["priority"]) - if priority > highest_priority then - highest_priority = priority - target = si_target_ep - end - end - - return target -end - -function createLink (si, si_target_ep) - local out_item = nil - local in_item = nil - local si_props = si.properties - local target_ep_props = si_target_ep.properties - - if si_props["item.node.direction"] == "output" then - -- playback - out_item = si - in_item = si_target_ep - else - -- capture - out_item = si_target_ep - in_item = si - end - - Log.info (string.format("link %s <-> %s", - tostring(si_props["node.name"]), - tostring(target_ep_props["name"]))) - - -- create and configure link - local si_link = SessionItem ( "si-standard-link" ) - if not si_link:configure { - ["out.item"] = out_item, - ["in.item"] = in_item, - ["out.item.port.context"] = "output", - ["in.item.port.context"] = "input", - ["is.policy.endpoint.client.link"] = true, - ["media.role"] = target_ep_props["role"], - ["target.media.class"] = target_ep_props["media.class"], - ["item.plugged.usec"] = si_props["item.plugged.usec"], - } then - Log.warning (si_link, "failed to configure si-standard-link") - return - end - - -- register - si_link:register() -end - -function checkLinkable (si) - -- only handle session items that has a node associated proxy - local node = si:get_associated_proxy ("node") - if not node or not node.properties then - return false - end - - -- only handle stream session items - local media_class = node.properties["media.class"] - if not media_class or not string.find (media_class, "Stream") then - return false - end - - -- Determine if we can handle item by this policy - if endpoints_om:get_n_objects () == 0 then - Log.debug (si, "item won't be handled by this policy") - return false - end - - return true -end - -function handleLinkable (si) - if not checkLinkable (si) then - return - end - - local node = si:get_associated_proxy ("node") - local media_class = node.properties["media.class"] or "" - local media_role = node.properties["media.role"] or "Default" - Log.info (si, "handling item " .. tostring(node.properties["node.name"]) .. - " with role " .. media_role) - - -- find proper target endpoint - local si_target_ep = findTargetEndpoint (node, media_class, media_role) - if not si_target_ep then - Log.info (si, "... target endpoint not found") - return - end - - -- Check if item is linked to proper target, otherwise re-link - for link in links_om:iterate() do - local out_id = tonumber(link.properties["out.item.id"]) - local in_id = tonumber(link.properties["in.item.id"]) - if out_id == si.id or in_id == si.id then - local is_out = out_id == si.id and true or false - for peer_ep in endpoints_om:iterate() do - if peer_ep.id == (is_out and in_id or out_id) then - - if peer_ep.id == si_target_ep.id then - Log.info (si, "... already linked to proper target endpoint") - return - end - - -- remove old link if active, otherwise schedule rescan - if ((link:get_active_features() & Feature.SessionItem.ACTIVE) ~= 0) then - link:remove () - Log.info (si, "... moving to new target") - else - scheduleRescan () - Log.info (si, "... scheduled rescan") - return - end - - end - end - end - end - - -- create new link - createLink (si, si_target_ep) -end - -function unhandleLinkable (si) - if not checkLinkable (si) then - return - end - - local node = si:get_associated_proxy ("node") - Log.info (si, "unhandling item " .. tostring(node.properties["node.name"])) - - -- remove any links associated with this item - for silink in links_om:iterate() do - local out_id = tonumber (silink.properties["out.item.id"]) - local in_id = tonumber (silink.properties["in.item.id"]) - if out_id == si.id or in_id == si.id then - silink:remove () - Log.info (silink, "... link removed") - end - end -end - -endpoints_om = ObjectManager { Interest { type = "SiEndpoint" }} -linkables_om = ObjectManager { Interest { type = "SiLinkable", - -- only handle si-audio-adapter and si-node - Constraint { - "item.factory.name", "=", "si-audio-adapter", type = "pw-global" }, - Constraint { - "active-features", "!", 0, type = "gobject" }, - Constraint { - "node.link-group", "-" }, - } -} -links_om = ObjectManager { Interest { type = "SiLink", - -- only handle links created by this policy - Constraint { "is.policy.endpoint.client.link", "=", true, type = "pw-global" }, -} } - -linkables_om:connect("objects-changed", function (om) - scheduleRescan () -end) - -linkables_om:connect("object-removed", function (om, si) - unhandleLinkable (si) -end) - -endpoints_om:activate() -linkables_om:activate() -links_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-device.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-device.lua deleted file mode 100644 index 4440c17c..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-endpoint-device.lua +++ /dev/null @@ -1,285 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author Julian Bouzas --- --- SPDX-License-Identifier: MIT - --- Receive script arguments from config.lua -local config = ... or {} - --- ensure config.move and config.follow are not nil -config.move = config.move or false -config.follow = config.follow or false - -local self = {} -self.scanning = false -self.pending_rescan = false - -function rescan () - -- check endpoints and register new links - for si_ep in endpoints_om:iterate() do - handleLinkable(si_ep) - end - - for filter in streams_om:iterate { - Constraint { "node.link-group", "+" }, - } do - handleFilter(filter) - end -end - -function scheduleRescan () - if self.scanning then - self.pending_rescan = true - return - end - - self.scanning = true - rescan () - self.scanning = false - - if self.pending_rescan then - self.pending_rescan = false - Core.sync(function () - scheduleRescan () - end) - end -end - -function findFilterTarget (props) - for si_target in linkables_om:iterate { - -- exclude filter targets - Constraint { "node.link-group", "+" }, - } do - local si_props = si_target.properties - if si_props["target.endpoint"] and si_props["target.endpoint"] == props["name"] then - return si_target - end - end -end - -function findTargetByDefaultNode (target_media_class) - local def_id = default_nodes:call("get-default-node", target_media_class) - if def_id ~= Id.INVALID then - for si_target in linkables_om:iterate { - -- exclude filter targets - Constraint { "node.link-group", "-" }, - } do - local target_node = si_target:get_associated_proxy ("node") - if target_node["bound-id"] == def_id then - return si_target - end - end - end - return nil -end - -function findTargetByFirstAvailable (target_media_class) - for si_target in linkables_om:iterate { - -- exclude filter targets - Constraint { "node.link-group", "-" }, - } do - local target_node = si_target:get_associated_proxy ("node") - if target_node.properties["media.class"] == target_media_class then - return si_target - end - end - return nil -end - -function findUndefinedTarget (si_ep) - local media_class = si_ep.properties["media.class"] - local target_class_assoc = { - ["Audio/Source"] = "Audio/Source", - ["Stream/Output/Audio"] = "Audio/Sink", - ["Audio/Sink"] = "Audio/Sink", - ["Video/Source"] = "Video/Source", - } - local target_media_class = target_class_assoc[media_class] - if not target_media_class then - return nil - end - - local si_target = findFilterTarget (si_ep.properties) - if not si_target then - si_target = findTargetByDefaultNode (target_media_class) - end - if not si_target then - si_target = findTargetByFirstAvailable (target_media_class) - end - return si_target -end - -function createLink (si_ep, si_target) - local out_item = nil - local in_item = nil - local ep_props = si_ep.properties - local target_props = si_target.properties - - if target_props["item.node.direction"] == "input" then - -- playback - out_item = si_ep - in_item = si_target - else - -- capture - in_item = si_ep - out_item = si_target - end - - local link_string = string.format("link %s <-> %s ", - (is_filter and ep_props["node.name"] or ep_props["name"]), - target_props["node.name"]) - - Log.info(si_link, link_string) - - -- create and configure link - local si_link = SessionItem ( "si-standard-link" ) - if not si_link:configure { - ["out.item"] = out_item, - ["in.item"] = in_item, - ["out.item.port.context"] = "output", - ["in.item.port.context"] = "input", - ["is.policy.endpoint.device.link"] = true, - } then - Log.warning (si_link, "failed to configure si-standard-link") - return - end - - -- register - si_link:register () - - Log.info (si_link, " activating link " .. link_string) - - -- activate - si_link:activate (Feature.SessionItem.ACTIVE, function (l, e) - if e then - Log.warning (l, "failed to activate link: " .. link_string .. tostring(e)) - l:remove () - else - Log.info (l, "activated link " .. link_string) - end - end) -end - -function handleFilter(filter) - handleLinkable(filter) -end - -function handleLinkable (si) - local si_props = si.properties - local is_filter = (si_props["node.link-group"] ~= nil) - if is_filter then - Log.info (si, "handling filter " .. si_props["node.name"]) - else - Log.info (si, "handling endpoint " .. si_props["name"]) - end - - -- find proper target item - local si_target = findUndefinedTarget (si) - if not si_target then - Log.info (si, "... target item not found") - return - end - - -- Check if item is linked to proper target, otherwise re-link - for link in links_om:iterate() do - local out_id = tonumber(link.properties["out.item.id"]) - local in_id = tonumber(link.properties["in.item.id"]) - if out_id == si.id or in_id == si.id then - local is_out = out_id == si.id and true or false - for peer in linkables_om:iterate() do - if peer.id == (is_out and in_id or out_id) then - - if peer.id == si_target.id then - Log.info (si, "... already linked to proper target") - return - end - - -- remove old link if active, otherwise schedule rescan - if ((link:get_active_features() & Feature.SessionItem.ACTIVE) ~= 0) then - link:remove () - Log.info (si, "... moving to new target") - else - scheduleRescan () - Log.info (si, "... scheduled rescan") - return - end - - end - end - end - end - - -- create new link - createLink (si, si_target) -end - -function unhandleLinkable (si) - si_props = si.properties - - Log.info (si, string.format("unhandling item: %s (%s)", - tostring(si_props["node.name"]), tostring(si_props["node.id"]))) - - -- remove any links associated with this item - for silink in links_om:iterate() do - local out_id = tonumber (silink.properties["out.item.id"]) - local in_id = tonumber (silink.properties["in.item.id"]) - if out_id == si.id or in_id == si.id then - silink:remove () - Log.info (silink, "... link removed") - end - end -end - -default_nodes = Plugin.find("default-nodes-api") -endpoints_om = ObjectManager { Interest { type = "SiEndpoint" }} -linkables_om = ObjectManager { - Interest { - type = "SiLinkable", - -- only handle device si-audio-adapter items - Constraint { "item.factory.name", "=", "si-audio-adapter", type = "pw-global" }, - Constraint { "item.node.type", "=", "device", type = "pw-global" }, - Constraint { "active-features", "!", 0, type = "gobject" }, - } -} -streams_om = ObjectManager { - Interest { - type = "SiLinkable", - -- only handle stream si-audio-adapter items - Constraint { "item.factory.name", "=", "si-audio-adapter", type = "pw-global" }, - Constraint { "active-features", "!", 0, type = "gobject" }, - Constraint { "media.class", "=", "Stream/Output/Audio" }, - } -} -links_om = ObjectManager { - Interest { - type = "SiLink", - -- only handle links created by this policy - Constraint { "is.policy.endpoint.device.link", "=", true, type = "pw-global" }, - } -} - --- listen for default node changes if config.follow is enabled -if config.follow then - default_nodes:connect("changed", function (p) - scheduleRescan () - end) -end - -linkables_om:connect("objects-changed", function (om) - scheduleRescan () -end) - -endpoints_om:connect("object-added", function (om) - scheduleRescan () -end) - -linkables_om:connect("object-removed", function (om, si) - unhandleLinkable (si) -end) - -endpoints_om:activate() -linkables_om:activate() -links_om:activate() -streams_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-node.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-node.lua deleted file mode 100644 index 99ad8473..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/policy-node.lua +++ /dev/null @@ -1,1012 +0,0 @@ --- WirePlumber --- --- Copyright © 2020 Collabora Ltd. --- @author Julian Bouzas --- --- SPDX-License-Identifier: MIT - --- Receive script arguments from config.lua -local config = ... or {} - --- ensure config.move and config.follow are not nil -config.move = config.move or false -config.follow = config.follow or false -config.filter_forward_format = config["filter.forward-format"] or false - -local self = {} -self.scanning = false -self.pending_rescan = false -self.events_skipped = false -self.pending_error_timer = nil - -function rescan() - for si in linkables_om:iterate() do - handleLinkable (si) - end -end - -function scheduleRescan () - if self.scanning then - self.pending_rescan = true - return - end - - self.scanning = true - rescan () - self.scanning = false - - if self.pending_rescan then - self.pending_rescan = false - Core.sync(function () - scheduleRescan () - end) - end -end - -function parseBool(var) - return var and (var:lower() == "true" or var == "1") -end - -function createLink (si, si_target, passthrough, exclusive) - local out_item = nil - local in_item = nil - local si_props = si.properties - local target_props = si_target.properties - local si_id = si.id - - -- break rescan if tried more than 5 times with same target - if si_flags[si_id].failed_peer_id ~= nil and - si_flags[si_id].failed_peer_id == si_target.id and - si_flags[si_id].failed_count ~= nil and - si_flags[si_id].failed_count > 5 then - Log.warning (si, "tried to link on last rescan, not retrying") - return - end - - if si_props["item.node.direction"] == "output" then - -- playback - out_item = si - in_item = si_target - else - -- capture - in_item = si - out_item = si_target - end - - Log.info ( - string.format("link %s <-> %s passthrough:%s, exclusive:%s", - tostring(si_props["node.name"]), - tostring(target_props["node.name"]), - tostring(passthrough), tostring(exclusive))) - - -- create and configure link - local si_link = SessionItem ( "si-standard-link" ) - if not si_link:configure { - ["out.item"] = out_item, - ["in.item"] = in_item, - ["passthrough"] = passthrough, - ["exclusive"] = exclusive, - ["out.item.port.context"] = "output", - ["in.item.port.context"] = "input", - ["is.policy.item.link"] = true, - } then - Log.warning (si_link, "failed to configure si-standard-link") - return - end - - si_link:connect("link-error", function (_, error_msg) - local ids = {si_id} - if si_flags[si_id] ~= nil then - table.insert (ids, si_flags[si_id].peer_id) - end - - for _, id in ipairs (ids) do - local si = linkables_om:lookup { - Constraint { "id", "=", id, type = "gobject" }, - } - if si then - local node = si:get_associated_proxy ("node") - local client_id = node.properties["client.id"] - if client_id then - local client = clients_om:lookup { - Constraint { "bound-id", "=", client_id, type = "gobject" } - } - if client then - Log.info (node, "sending client error: " .. error_msg) - client:send_error (node["bound-id"], -32, error_msg) - end - end - end - end - end) - - -- register - si_flags[si_id].peer_id = si_target.id - si_flags[si_id].failed_peer_id = si_target.id - if si_flags[si_id].failed_count ~= nil then - si_flags[si_id].failed_count = si_flags[si_id].failed_count + 1 - else - si_flags[si_id].failed_count = 1 - end - si_link:register () - - -- activate - si_link:activate (Feature.SessionItem.ACTIVE, function (l, e) - if e then - Log.info (l, "failed to activate si-standard-link: " .. tostring(e)) - if si_flags[si_id] ~= nil then - si_flags[si_id].peer_id = nil - end - l:remove () - else - if si_flags[si_id] ~= nil then - si_flags[si_id].failed_peer_id = nil - si_flags[si_id].failed_count = 0 - end - Log.info (l, "activated si-standard-link") - end - scheduleRescan() - end) -end - -function isLinked(si_target) - local target_id = si_target.id - local linked = false - local exclusive = false - - for l in links_om:iterate() do - local p = l.properties - local out_id = tonumber(p["out.item.id"]) - local in_id = tonumber(p["in.item.id"]) - linked = (out_id == target_id) or (in_id == target_id) - if linked then - exclusive = parseBool(p["exclusive"]) or parseBool(p["passthrough"]) - break - end - end - return linked, exclusive -end - -function canPassthrough (si, si_target) - -- both nodes must support encoded formats - if not parseBool(si.properties["item.node.supports-encoded-fmts"]) - or not parseBool(si_target.properties["item.node.supports-encoded-fmts"]) then - return false - end - - -- make sure that the nodes have at least one common non-raw format - local n1 = si:get_associated_proxy ("node") - local n2 = si_target:get_associated_proxy ("node") - for p1 in n1:iterate_params("EnumFormat") do - local p1p = p1:parse() - if p1p.properties.mediaSubtype ~= "raw" then - for p2 in n2:iterate_params("EnumFormat") do - if p1:filter(p2) then - return true - end - end - end - end - return false -end - -function canLink (properties, si_target) - local target_properties = si_target.properties - - -- nodes must have the same media type - if properties["media.type"] ~= target_properties["media.type"] then - return false - end - - -- nodes must have opposite direction, or otherwise they must be both input - -- and the target must have a monitor (so the target will be used as a source) - local function isMonitor(properties) - return properties["item.node.direction"] == "input" and - parseBool(properties["item.features.monitor"]) and - not parseBool(properties["item.features.no-dsp"]) and - properties["item.factory.name"] == "si-audio-adapter" - end - - if properties["item.node.direction"] == target_properties["item.node.direction"] - and not isMonitor(target_properties) then - return false - end - - -- check link group - local function canLinkGroupCheck (link_group, si_target, hops) - local target_props = si_target.properties - local target_link_group = target_props["node.link-group"] - - if hops == 8 then - return false - end - - -- allow linking if target has no link-group property - if not target_link_group then - return true - end - - -- do not allow linking if target has the same link-group - if link_group == target_link_group then - return false - end - - -- make sure target is not linked with another node with same link group - -- start by locating other nodes in the target's link-group, in opposite direction - for n in linkables_om:iterate { - Constraint { "id", "!", si_target.id, type = "gobject" }, - Constraint { "item.node.direction", "!", target_props["item.node.direction"] }, - Constraint { "node.link-group", "=", target_link_group }, - } do - -- iterate their peers and return false if one of them cannot link - for silink in links_om:iterate() do - local out_id = tonumber(silink.properties["out.item.id"]) - local in_id = tonumber(silink.properties["in.item.id"]) - if out_id == n.id or in_id == n.id then - local peer_id = (out_id == n.id) and in_id or out_id - local peer = linkables_om:lookup { - Constraint { "id", "=", peer_id, type = "gobject" }, - } - if peer and not canLinkGroupCheck (link_group, peer, hops + 1) then - return false - end - end - end - end - return true - end - - local link_group = properties["node.link-group"] - if link_group then - return canLinkGroupCheck (link_group, si_target, 0) - end - return true -end - -function getTargetDirection(properties) - local target_direction = nil - if properties["item.node.direction"] == "output" or - (properties["item.node.direction"] == "input" and - parseBool(properties["stream.capture.sink"])) then - target_direction = "input" - else - target_direction = "output" - end - return target_direction -end - -function getDefaultNode(properties, target_direction) - local target_media_class = - properties["media.type"] .. - (target_direction == "input" and "/Sink" or "/Source") - return default_nodes:call("get-default-node", target_media_class) -end - --- Try to locate a valid target node that was explicitly requsted by the --- client(node.target) or by the user(target.node) --- Use the target.node metadata, if config.move is enabled, --- then use the node.target property that was set on the node --- `properties` must be the properties dictionary of the session item --- that is currently being handled -function findDefinedTarget (properties) - local metadata = config.move and metadata_om:lookup() - local target_direction = getTargetDirection(properties) - local target_key - local target_value - local node_defined = false - - if properties["target.object"] ~= nil then - target_value = properties["target.object"] - target_key = "object.serial" - node_defined = true - elseif properties["node.target"] ~= nil then - target_value = properties["node.target"] - target_key = "node.id" - node_defined = true - end - - if metadata then - local id = metadata:find(properties["node.id"], "target.object") - if id ~= nil then - target_value = id - target_key = "object.serial" - node_defined = false - else - id = metadata:find(properties["node.id"], "target.node") - if id ~= nil then - target_value = id - target_key = "node.id" - node_defined = false - end - end - end - - if target_value == "-1" then - return nil, false, node_defined - end - - if target_value and tonumber(target_value) then - local si_target = linkables_om:lookup { - Constraint { target_key, "=", target_value }, - } - if si_target and canLink (properties, si_target) then - return si_target, true, node_defined - end - end - - if target_value then - for si_target in linkables_om:iterate() do - local target_props = si_target.properties - if (target_props["node.name"] == target_value or - target_props["object.path"] == target_value) and - target_props["item.node.direction"] == target_direction and - canLink (properties, si_target) then - return si_target, true, node_defined - end - end - end - return nil, (target_value ~= nil), node_defined -end - -function parseParam(param, id) - local route = param:parse() - if route.pod_type == "Object" and route.object_id == id then - return route.properties - else - return nil - end -end - -function arrayContains(a, value) - for _, v in ipairs(a) do - if v == value then - return true - end - end - return false -end - - --- Does the target device have any active/available paths/routes to --- the physical device(spkr/mic/cam)? -function haveAvailableRoutes (si_props) - local card_profile_device = si_props["card.profile.device"] - local device_id = si_props["device.id"] - local device = device_id and devices_om:lookup { - Constraint { "bound-id", "=", device_id, type = "gobject"}, - } - - if not card_profile_device or not device then - return true - end - - local found = 0 - local avail = 0 - - -- First check "SPA_PARAM_Route" if there are any active devices - -- in an active profile. - for p in device:iterate_params("Route") do - local route = parseParam(p, "Route") - if not route then - goto skip_route - end - - if (route.device ~= tonumber(card_profile_device)) then - goto skip_route - end - - if (route.available == "no") then - return false - end - - do return true end - - ::skip_route:: - end - - -- Second check "SPA_PARAM_EnumRoute" if there is any route that - -- is available if not active. - for p in device:iterate_params("EnumRoute") do - local route = parseParam(p, "EnumRoute") - if not route then - goto skip_enum_route - end - - if not arrayContains(route.devices, tonumber(card_profile_device)) then - goto skip_enum_route - end - found = found + 1; - if (route.available ~= "no") then - avail = avail +1 - end - ::skip_enum_route:: - end - - if found == 0 then - return true - end - if avail > 0 then - return true - end - - return false - -end - -function findDefaultLinkable (si) - local si_props = si.properties - local target_direction = getTargetDirection(si_props) - local def_node_id = getDefaultNode(si_props, target_direction) - return linkables_om:lookup { - Constraint { "node.id", "=", tostring(def_node_id) } - } -end - -function checkPassthroughCompatibility (si, si_target) - local si_must_passthrough = parseBool(si.properties["item.node.encoded-only"]) - local si_target_must_passthrough = parseBool(si_target.properties["item.node.encoded-only"]) - local can_passthrough = canPassthrough(si, si_target) - if (si_must_passthrough or si_target_must_passthrough) - and not can_passthrough then - return false, can_passthrough - end - return true, can_passthrough -end - -function findBestLinkable (si) - local si_props = si.properties - local target_direction = getTargetDirection(si_props) - local target_picked = nil - local target_can_passthrough = false - local target_priority = 0 - local target_plugged = 0 - - for si_target in linkables_om:iterate { - Constraint { "item.node.type", "=", "device" }, - Constraint { "item.node.direction", "=", target_direction }, - Constraint { "media.type", "=", si_props["media.type"] }, - } do - local si_target_props = si_target.properties - local si_target_node_id = si_target_props["node.id"] - local priority = tonumber(si_target_props["priority.session"]) or 0 - - Log.debug(string.format("Looking at: %s (%s)", - tostring(si_target_props["node.name"]), - tostring(si_target_node_id))) - - if not canLink (si_props, si_target) then - Log.debug("... cannot link, skip linkable") - goto skip_linkable - end - - if not haveAvailableRoutes(si_target_props) then - Log.debug("... does not have routes, skip linkable") - goto skip_linkable - end - - local passthrough_compatible, can_passthrough = - checkPassthroughCompatibility (si, si_target) - if not passthrough_compatible then - Log.debug("... passthrough is not compatible, skip linkable") - goto skip_linkable - end - - local plugged = tonumber(si_target_props["item.plugged.usec"]) or 0 - - Log.debug("... priority:"..tostring(priority)..", plugged:"..tostring(plugged)) - - -- (target_picked == NULL) --> make sure atleast one target is picked. - -- (priority > target_priority) --> pick the highest priority linkable(node) - -- target. - -- (priority == target_priority and plugged > target_plugged) --> pick the - -- latest connected/plugged(in time) linkable(node) target. - if (target_picked == nil or - priority > target_priority or - (priority == target_priority and plugged > target_plugged)) then - Log.debug("... picked") - target_picked = si_target - target_can_passthrough = can_passthrough - target_priority = priority - target_plugged = plugged - end - ::skip_linkable:: - end - - if target_picked then - Log.info(string.format("... best target picked: %s (%s), can_passthrough:%s", - tostring(target_picked.properties["node.name"]), - tostring(target_picked.properties["node.id"]), - tostring(target_can_passthrough))) - return target_picked, target_can_passthrough - else - return nil, nil - end - -end - -function findUndefinedTarget (si) - -- Just find the best linkable if default nodes module is not loaded - if default_nodes == nil then - return findBestLinkable (si) - end - - -- Otherwise find the default linkable. If the default linkable is not - -- compatible, we find the best one instead. We return nil if the default - -- linkable does not exist. - local si_target = findDefaultLinkable (si) - if si_target then - local passthrough_compatible, can_passthrough = - checkPassthroughCompatibility (si, si_target) - if canLink (si.properties, si_target) and passthrough_compatible then - Log.info(string.format("... default target picked: %s (%s), can_passthrough:%s", - tostring(si_target.properties["node.name"]), - tostring(si_target.properties["node.id"]), - tostring(can_passthrough))) - return si_target, can_passthrough - else - return findBestLinkable (si) - end - end - return nil, nil -end - -function lookupLink (si_id, si_target_id) - local link = links_om:lookup { - Constraint { "out.item.id", "=", si_id }, - Constraint { "in.item.id", "=", si_target_id } - } - if not link then - link = links_om:lookup { - Constraint { "in.item.id", "=", si_id }, - Constraint { "out.item.id", "=", si_target_id } - } - end - return link -end - -function checkLinkable(si, handle_nonstreams) - -- only handle stream session items - local si_props = si.properties - if not si_props or (si_props["item.node.type"] ~= "stream" - and not handle_nonstreams) then - return false - end - - -- Determine if we can handle item by this policy - if endpoints_om:get_n_objects () > 0 and - si_props["item.factory.name"] == "si-audio-adapter" then - return false - end - - return true, si_props -end - -si_flags = {} - -function checkPending () - local pending_linkables = pending_linkables_om:get_n_objects () - - -- We cannot process linkables if some of them are pending activation, - -- because linkables do not appear in the same order as nodes, - -- and we cannot resolve target node references until all linkables - -- have appeared. - - if self.pending_error_timer then - self.pending_error_timer:destroy () - self.pending_error_timer = nil - end - - if pending_linkables ~= 0 then - -- Wait for linkables to get it sync - Log.debug(string.format("pending %d linkable not ready", - pending_linkables)) - self.events_skipped = true - - -- To make bugs in activation easier to debug, emit an error message - -- if they occur. policy-node should never be suspended for 20sec. - self.pending_error_timer = Core.timeout_add(20000, function() - self.pending_error_timer = nil - if pending_linkables ~= 0 then - Log.message(string.format("%d pending linkable(s) not activated in 20sec. " - .. "This should never happen.", pending_linkables)) - end - end) - - return true - elseif self.events_skipped then - Log.debug("pending linkables ready") - self.events_skipped = false - scheduleRescan () - return true - end - - return false -end - -function checkFollowDefault (si, si_target, has_node_defined_target) - -- If it got linked to the default target that is defined by node - -- props but not metadata, start ignoring the node prop from now on. - -- This is what Pulseaudio does. - -- - -- Pulseaudio skips here filter streams (i->origin_sink and - -- o->destination_source set in PA). Pipewire does not have a flag - -- explicitly for this, but we can use presence of node.link-group. - if not has_node_defined_target then - return - end - - local si_props = si.properties - local target_props = si_target.properties - local reconnect = not parseBool(si_props["node.dont-reconnect"]) - local is_filter = (si_props["node.link-group"] ~= nil) - - if config.follow and default_nodes ~= nil and reconnect and not is_filter then - local def_id = getDefaultNode(si_props, getTargetDirection(si_props)) - - if target_props["node.id"] == tostring(def_id) then - local metadata = metadata_om:lookup() - -- Set target.node, for backward compatibility - metadata:set(tonumber(si_props["node.id"]), "target.node", "Spa:Id", "-1") - Log.info (si, "... set metadata to follow default") - end - end -end - -function handleLinkable (si) - if checkPending () then - return - end - - local valid, si_props = checkLinkable(si) - if not valid then - return - end - - -- check if we need to link this node at all - local autoconnect = parseBool(si_props["node.autoconnect"]) - if not autoconnect then - Log.debug (si, tostring(si_props["node.name"]) .. " does not need to be autoconnected") - return - end - - Log.info (si, string.format("handling item: %s (%s)", - tostring(si_props["node.name"]), tostring(si_props["node.id"]))) - - ensureSiFlags(si) - - -- get other important node properties - local reconnect = not parseBool(si_props["node.dont-reconnect"]) - local exclusive = parseBool(si_props["node.exclusive"]) - local si_must_passthrough = parseBool(si_props["item.node.encoded-only"]) - - -- find defined target - local si_target, has_defined_target, has_node_defined_target - = findDefinedTarget(si_props) - local can_passthrough = si_target and canPassthrough(si, si_target) - - if si_target and si_must_passthrough and not can_passthrough then - si_target = nil - end - - -- if the client has seen a target that we haven't yet prepared, schedule - -- a rescan one more time and hope for the best - local si_id = si.id - if has_defined_target - and not si_target - and not si_flags[si_id].was_handled - and not si_flags[si_id].done_waiting then - Log.info (si, "... waiting for target") - si_flags[si_id].done_waiting = true - scheduleRescan() - return - end - - -- find fallback target - if not si_target and (reconnect or not has_defined_target) then - si_target, can_passthrough = findUndefinedTarget(si) - end - - -- Check if item is linked to proper target, otherwise re-link - if si_flags[si_id].peer_id then - if si_target and si_flags[si_id].peer_id == si_target.id then - Log.debug (si, "... already linked to proper target") - -- Check this also here, in case in default targets changed - checkFollowDefault (si, si_target, has_node_defined_target) - return - end - local link = lookupLink (si_id, si_flags[si_id].peer_id) - if reconnect then - if link ~= nil then - -- remove old link - if ((link:get_active_features() & Feature.SessionItem.ACTIVE) == 0) then - -- Link not yet activated. We don't want to remove it now, as that - -- may cause problems. Instead, give up for now. A rescan is scheduled - -- once the link activates. - Log.info (link, "Link to be moved was not activated, will wait for it.") - return - end - si_flags[si_id].peer_id = nil - link:remove () - Log.info (si, "... moving to new target") - end - else - if link ~= nil then - Log.info (si, "... dont-reconnect, not moving") - return - end - end - end - - -- if the stream has dont-reconnect and was already linked before, - -- don't link it to a new target - if not reconnect and si_flags[si.id].was_handled then - si_target = nil - end - - -- check target's availability - if si_target then - local target_is_linked, target_is_exclusive = isLinked(si_target) - if target_is_exclusive then - Log.info(si, "... target is linked exclusively") - si_target = nil - end - - if target_is_linked then - if exclusive or si_must_passthrough then - Log.info(si, "... target is already linked, cannot link exclusively") - si_target = nil - else - -- disable passthrough, we can live without it - can_passthrough = false - end - end - end - - if not si_target then - Log.info (si, "... target not found, reconnect:" .. tostring(reconnect)) - - local node = si:get_associated_proxy ("node") - if reconnect and si_flags[si.id].was_handled then - Log.info (si, "... waiting reconnect") - return - end - - local client_id = node.properties["client.id"] - if client_id then - local client = clients_om:lookup { - Constraint { "bound-id", "=", client_id, type = "gobject" } - } - local message - if reconnect then - message = "no target node available" - else - message = "target not found" - end - if client then - client:send_error(node["bound-id"], -2, message) - end - end - - if not reconnect then - Log.info (si, "... destroy node") - node:request_destroy() - end - else - createLink (si, si_target, can_passthrough, exclusive) - si_flags[si.id].was_handled = true - - checkFollowDefault (si, si_target, has_node_defined_target) - end -end - -function unhandleLinkable (si) - local valid, si_props = checkLinkable(si, true) - if not valid then - return - end - - Log.info (si, string.format("unhandling item: %s (%s)", - tostring(si_props["node.name"]), tostring(si_props["node.id"]))) - - -- remove any links associated with this item - for silink in links_om:iterate() do - local out_id = tonumber (silink.properties["out.item.id"]) - local in_id = tonumber (silink.properties["in.item.id"]) - if out_id == si.id or in_id == si.id then - if out_id == si.id and - si_flags[in_id] and si_flags[in_id].peer_id == out_id then - si_flags[in_id].peer_id = nil - elseif in_id == si.id and - si_flags[out_id] and si_flags[out_id].peer_id == in_id then - si_flags[out_id].peer_id = nil - end - silink:remove () - Log.info (silink, "... link removed") - end - end - - si_flags[si.id] = nil -end - -default_nodes = Plugin.find("default-nodes-api") - -metadata_om = ObjectManager { - Interest { - type = "metadata", - Constraint { "metadata.name", "=", "default" }, - } -} - -endpoints_om = ObjectManager { Interest { type = "SiEndpoint" } } - -clients_om = ObjectManager { Interest { type = "client" } } - -devices_om = ObjectManager { Interest { type = "device" } } - -linkables_om = ObjectManager { - Interest { - type = "SiLinkable", - -- only handle si-audio-adapter and si-node - Constraint { "item.factory.name", "c", "si-audio-adapter", "si-node" }, - Constraint { "active-features", "!", 0, type = "gobject" }, - } -} - -pending_linkables_om = ObjectManager { - Interest { - type = "SiLinkable", - -- only handle si-audio-adapter and si-node - Constraint { "item.factory.name", "c", "si-audio-adapter", "si-node" }, - Constraint { "active-features", "=", 0, type = "gobject" }, - } -} - -links_om = ObjectManager { - Interest { - type = "SiLink", - -- only handle links created by this policy - Constraint { "is.policy.item.link", "=", true }, - } -} - --- listen for default node changes if config.follow is enabled -if config.follow and default_nodes ~= nil then - default_nodes:connect("changed", function () - scheduleRescan () - end) -end - --- listen for target.node metadata changes if config.move is enabled -if config.move then - metadata_om:connect("object-added", function (om, metadata) - metadata:connect("changed", function (m, subject, key, t, value) - if key == "target.node" or key == "target.object" then - scheduleRescan () - end - end) - end) -end - -function findAssociatedLinkGroupNode (si) - local si_props = si.properties - local node = si:get_associated_proxy ("node") - local link_group = node.properties["node.link-group"] - if link_group == nil then - return nil - end - - -- get the associated media class - local assoc_direction = getTargetDirection(si_props) - local assoc_media_class = - si_props["media.type"] .. - (assoc_direction == "input" and "/Sink" or "/Source") - - -- find the linkable with same link group and matching assoc media class - for assoc_si in linkables_om:iterate() do - local assoc_node = assoc_si:get_associated_proxy ("node") - local assoc_link_group = assoc_node.properties["node.link-group"] - if assoc_link_group == link_group and - assoc_media_class == assoc_node.properties["media.class"] then - return assoc_si - end - end - - return nil -end - -function onLinkGroupPortsStateChanged (si, old_state, new_state) - local new_str = tostring(new_state) - local si_props = si.properties - - -- only handle items with configured ports state - if new_str ~= "configured" then - return - end - - Log.info (si, "ports format changed on " .. si_props["node.name"]) - - -- find associated device - local si_device = findAssociatedLinkGroupNode (si) - if si_device ~= nil then - local device_node_name = si_device.properties["node.name"] - - -- get the stream format - local f, m = si:get_ports_format() - - -- unregister the device - Log.info (si_device, "unregistering " .. device_node_name) - si_device:remove() - - -- set new format in the device - Log.info (si_device, "setting new format in " .. device_node_name) - si_device:set_ports_format(f, m, function (item, e) - if e ~= nil then - Log.warning (item, "failed to configure ports in " .. - device_node_name .. ": " .. e) - end - - -- register back the device - Log.info (item, "registering " .. device_node_name) - item:register() - end) - end -end - -function ensureSiFlags (si) - -- prepare flags table - if not si_flags[si.id] then - si_flags[si.id] = {} - end -end - -function checkFiltersPortsState (si) - local si_props = si.properties - local node = si:get_associated_proxy ("node") - local link_group = node.properties["node.link-group"] - - ensureSiFlags(si) - - -- only listen for ports state changed on audio filter streams - if si_flags[si.id].ports_state_signal ~= true and - si_props["item.factory.name"] == "si-audio-adapter" and - si_props["item.node.type"] == "stream" and - link_group ~= nil then - si:connect("adapter-ports-state-changed", onLinkGroupPortsStateChanged) - si_flags[si.id].ports_state_signal = true - Log.info (si, "listening ports state changed on " .. si_props["node.name"]) - end -end - -linkables_om:connect("object-added", function (om, si) - local si_props = si.properties - - -- Forward filters ports format to associated virtual devices if enabled - if config.filter_forward_format then - checkFiltersPortsState (si) - end - - if si_props["item.node.type"] ~= "stream" then - scheduleRescan () - else - handleLinkable (si) - end -end) - -linkables_om:connect("object-removed", function (om, si) - unhandleLinkable (si) - scheduleRescan () -end) - -devices_om:connect("object-added", function (om, device) - device:connect("params-changed", function (d, param_name) - scheduleRescan () - end) -end) - -metadata_om:activate() -endpoints_om:activate() -clients_om:activate() -linkables_om:activate() -pending_linkables_om:activate() -links_om:activate() -devices_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/restore-stream.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/restore-stream.lua deleted file mode 100644 index 06867d42..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/restore-stream.lua +++ /dev/null @@ -1,499 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author George Kiagiadakis --- --- Based on restore-stream.c from pipewire-media-session --- Copyright © 2020 Wim Taymans --- --- SPDX-License-Identifier: MIT - --- Receive script arguments from config.lua -local config = ... or {} -config.properties = config.properties or {} -config_restore_props = config.properties["restore-props"] or false -config_restore_target = config.properties["restore-target"] or false -config_default_channel_volume = config.properties["default-channel-volume"] or 1.0 - --- preprocess rules and create Interest objects -for _, r in ipairs(config.rules or {}) do - r.interests = {} - for _, i in ipairs(r.matches) do - local interest_desc = { type = "properties" } - for _, c in ipairs(i) do - c.type = "pw" - table.insert(interest_desc, Constraint(c)) - end - local interest = Interest(interest_desc) - table.insert(r.interests, interest) - end - r.matches = nil -end - --- applies properties from config.rules when asked to -function rulesApplyProperties(properties) - for _, r in ipairs(config.rules or {}) do - if r.apply_properties then - for _, interest in ipairs(r.interests) do - if interest:matches(properties) then - for k, v in pairs(r.apply_properties) do - properties[k] = v - end - end - end - end - end -end - --- the state storage -state = State("restore-stream") -state_table = state:load() - --- simple serializer {"foo", "bar"} -> "foo;bar;" -function serializeArray(a) - local str = "" - for _, v in ipairs(a) do - str = str .. tostring(v):gsub(";", "\\;") .. ";" - end - return str -end - --- simple deserializer "foo;bar;" -> {"foo", "bar"} -function parseArray(str, convert_value, with_type) - local array = {} - local val = "" - local escaped = false - for i = 1, #str do - local c = str:sub(i,i) - if c == '\\' then - escaped = true - elseif c == ';' and not escaped then - val = convert_value and convert_value(val) or val - table.insert(array, val) - val = "" - else - val = val .. tostring(c) - escaped = false - end - end - if with_type then - array["pod_type"] = "Array" - end - return array -end - -function parseParam(param, id) - local route = param:parse() - if route.pod_type == "Object" and route.object_id == id then - return route.properties - else - return nil - end -end - -function storeAfterTimeout() - if timeout_source then - timeout_source:destroy() - end - timeout_source = Core.timeout_add(1000, function () - local saved, err = state:save(state_table) - if not saved then - Log.warning(err) - end - timeout_source = nil - end) -end - -function findSuitableKey(properties) - local keys = { - "media.role", - "application.id", - "application.name", - "media.name", - "node.name", - } - local key = nil - - for _, k in ipairs(keys) do - local p = properties[k] - if p then - key = string.format("%s:%s:%s", - properties["media.class"]:gsub("^Stream/", ""), k, p) - break - end - end - return key -end - -function saveTarget(subject, target_key, type, value) - if target_key ~= "target.node" and target_key ~= "target.object" then - return - end - - local node = streams_om:lookup { - Constraint { "bound-id", "=", subject, type = "gobject" } - } - if not node then - return - end - - local stream_props = node.properties - rulesApplyProperties(stream_props) - - if stream_props["state.restore-target"] == false then - return - end - - local key_base = findSuitableKey(stream_props) - if not key_base then - return - end - - local target_value = value - local target_name = nil - - if not target_value then - local metadata = metadata_om:lookup() - if metadata then - target_value = metadata:find(node["bound-id"], target_key) - end - end - if target_value and target_value ~= "-1" then - local target_node - if target_key == "target.object" then - target_node = allnodes_om:lookup { - Constraint { "object.serial", "=", target_value, type = "pw-global" } - } - else - target_node = allnodes_om:lookup { - Constraint { "bound-id", "=", target_value, type = "gobject" } - } - end - if target_node then - target_name = target_node.properties["node.name"] - end - end - state_table[key_base .. ":target"] = target_name - - Log.info(node, "saving stream target for " .. - tostring(stream_props["node.name"]) .. - " -> " .. tostring(target_name)) - - storeAfterTimeout() -end - -function restoreTarget(node, target_name) - - local stream_props = node.properties - local target_in_props = nil - - if stream_props ["target.object"] ~= nil or - stream_props ["node.target"] ~= nil then - target_in_props = stream_props ["target.object"] or - stream_props ["node.target"] - - Log.debug (string.format ("%s%s%s%s", - "Not restoring the target for ", - stream_props ["node.name"], - " because it is already set to ", - target_in_props)) - - return - end - - local target_node = allnodes_om:lookup { - Constraint { "node.name", "=", target_name, type = "pw" } - } - - if target_node then - local metadata = metadata_om:lookup() - if metadata then - metadata:set(node["bound-id"], "target.node", "Spa:Id", - target_node["bound-id"]) - end - end -end - -function jsonTable(val, name) - local tmp = "" - local count = 0 - - if name then tmp = tmp .. string.format("%q", name) .. ": " end - - if type(val) == "table" then - if val["pod_type"] == "Array" then - tmp = tmp .. "[" - for _, v in ipairs(val) do - if count > 0 then tmp = tmp .. "," end - tmp = tmp .. jsonTable(v) - count = count + 1 - end - tmp = tmp .. "]" - else - tmp = tmp .. "{" - for k, v in pairs(val) do - if count > 0 then tmp = tmp .. "," end - tmp = tmp .. jsonTable(v, k) - count = count + 1 - end - tmp = tmp .. "}" - end - elseif type(val) == "number" then - tmp = tmp .. tostring(val) - elseif type(val) == "string" then - tmp = tmp .. string.format("%q", val) - elseif type(val) == "boolean" then - tmp = tmp .. (val and "true" or "false") - else - tmp = tmp .. "\"[type:" .. type(val) .. "]\"" - end - return tmp -end - -function moveToMetadata(key_base, metadata) - local route_table = { } - local count = 0 - - key = "restore.stream." .. key_base - key = string.gsub(key, ":", ".", 1); - - local str = state_table[key_base .. ":volume"] - if str then - route_table["volume"] = tonumber(str) - count = count + 1; - end - local str = state_table[key_base .. ":mute"] - if str then - route_table["mute"] = str == "true" - count = count + 1; - end - local str = state_table[key_base .. ":channelVolumes"] - if str then - route_table["volumes"] = parseArray(str, tonumber, true) - count = count + 1; - end - local str = state_table[key_base .. ":channelMap"] - if str then - route_table["channels"] = parseArray(str, nil, true) - count = count + 1; - end - - if count > 0 then - metadata:set(0, key, "Spa:String:JSON", jsonTable(route_table)); - end -end - - -function saveStream(node) - local stream_props = node.properties - rulesApplyProperties(stream_props) - - if config_restore_props and stream_props["state.restore-props"] ~= false then - local key_base = findSuitableKey(stream_props) - if not key_base then - return - end - - Log.info(node, "saving stream props for " .. - tostring(stream_props["node.name"])) - - for p in node:iterate_params("Props") do - local props = parseParam(p, "Props") - if not props then - goto skip_prop - end - - if props.volume then - state_table[key_base .. ":volume"] = tostring(props.volume) - end - if props.mute ~= nil then - state_table[key_base .. ":mute"] = tostring(props.mute) - end - if props.channelVolumes then - state_table[key_base .. ":channelVolumes"] = serializeArray(props.channelVolumes) - end - if props.channelMap then - state_table[key_base .. ":channelMap"] = serializeArray(props.channelMap) - end - - ::skip_prop:: - end - - storeAfterTimeout() - end -end - -function build_default_channel_volumes (node) - local def_vol = config_default_channel_volume - local channels = 2 - local res = {} - - local str = node.properties["state.default-channel-volume"] - if str ~= nil then - def_vol = tonumber (str) - end - - for pod in node:iterate_params("Format") do - local pod_parsed = pod:parse() - if pod_parsed ~= nil then - channels = pod_parsed.properties.channels - break - end - end - - while (#res < channels) do - table.insert(res, def_vol) - end - - return res; -end - -function restoreStream(node) - local stream_props = node.properties - rulesApplyProperties(stream_props) - - local key_base = findSuitableKey(stream_props) - if not key_base then - return - end - - if config_restore_props and stream_props["state.restore-props"] ~= false then - local props = { "Spa:Pod:Object:Param:Props", "Props" } - - local str = state_table[key_base .. ":volume"] - props.volume = str and tonumber(str) or nil - - local str = state_table[key_base .. ":mute"] - props.mute = str and (str == "true") or nil - - local str = state_table[key_base .. ":channelVolumes"] - props.channelVolumes = str and parseArray(str, tonumber) or - build_default_channel_volumes (node) - - local str = state_table[key_base .. ":channelMap"] - props.channelMap = str and parseArray(str) or nil - - -- convert arrays to Spa Pod - if props.channelVolumes then - table.insert(props.channelVolumes, 1, "Spa:Float") - props.channelVolumes = Pod.Array(props.channelVolumes) - end - if props.channelMap then - table.insert(props.channelMap, 1, "Spa:Enum:AudioChannel") - props.channelMap = Pod.Array(props.channelMap) - end - - Log.info(node, "restore values from " .. key_base) - local param = Pod.Object(props) - Log.debug(param, "setting props on " .. tostring(node)) - node:set_param("Props", param) - end - - if config_restore_target and stream_props["state.restore-target"] ~= false then - local str = state_table[key_base .. ":target"] - if str then - restoreTarget(node, str) - end - end -end - -if config_restore_target then - metadata_om = ObjectManager { - Interest { - type = "metadata", - Constraint { "metadata.name", "=", "default" }, - } - } - - metadata_om:connect("object-added", function (om, metadata) - -- process existing metadata - for s, k, t, v in metadata:iterate(Id.ANY) do - saveTarget(s, k, t, v) - end - -- and watch for changes - metadata:connect("changed", function (m, subject, key, type, value) - saveTarget(subject, key, type, value) - end) - end) - metadata_om:activate() -end - -function handleRouteSettings(subject, key, type, value) - if type ~= "Spa:String:JSON" then - return - end - if string.find(key, "^restore.stream.") == nil then - return - end - if value == nil then - return - end - local json = Json.Raw (value); - if json == nil or not json:is_object () then - return - end - - local vparsed = json:parse() - local key_base = string.sub(key, string.len("restore.stream.") + 1) - local str; - - key_base = string.gsub(key_base, "%.", ":", 1); - - if vparsed.volume ~= nil then - state_table[key_base .. ":volume"] = tostring (vparsed.volume) - end - if vparsed.mute ~= nil then - state_table[key_base .. ":mute"] = tostring (vparsed.mute) - end - if vparsed.channels ~= nil then - state_table[key_base .. ":channelMap"] = serializeArray (vparsed.channels) - end - if vparsed.volumes ~= nil then - state_table[key_base .. ":channelVolumes"] = serializeArray (vparsed.volumes) - end - - storeAfterTimeout() -end - - -rs_metadata = ImplMetadata("route-settings") -rs_metadata:activate(Features.ALL, function (m, e) - if e then - Log.warning("failed to activate route-settings metadata: " .. tostring(e)) - return - end - - -- copy state into the metadata - moveToMetadata("Output/Audio:media.role:Notification", m) - -- watch for changes - m:connect("changed", function (m, subject, key, type, value) - handleRouteSettings(subject, key, type, value) - end) -end) - -allnodes_om = ObjectManager { Interest { type = "node" } } -allnodes_om:activate() - -streams_om = ObjectManager { - -- match stream nodes - Interest { - type = "node", - Constraint { "media.class", "matches", "Stream/*", type = "pw-global" }, - }, - -- and device nodes that are not associated with any routes - Interest { - type = "node", - Constraint { "media.class", "matches", "Audio/*", type = "pw-global" }, - Constraint { "device.routes", "is-absent", type = "pw" }, - }, - Interest { - type = "node", - Constraint { "media.class", "matches", "Audio/*", type = "pw-global" }, - Constraint { "device.routes", "equals", "0", type = "pw" }, - }, -} -streams_om:connect("object-added", function (streams_om, node) - node:connect("params-changed", saveStream) - restoreStream(node) -end) -streams_om:activate() diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/sm-objects.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/sm-objects.lua deleted file mode 100644 index c22b29d7..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/sm-objects.lua +++ /dev/null @@ -1,103 +0,0 @@ --- WirePlumber --- --- Copyright © 2023 Collabora Ltd. --- @author George Kiagiadakis --- --- SPDX-License-Identifier: MIT --- --- The script exposes a metadata object named "sm-objects" that clients can --- use to load objects into the WirePlumber daemon process. The objects are --- loaded as soon as the metadata is set and are destroyed when the metadata --- is cleared. --- --- To load an object, a client needs to set a metadata entry with: --- --- * subject: --- The ID of the owner of the object; you can use 0 here, but the --- idea is to be able to restrict which clients can change and/or --- delete these objects by using IDs of other objects appropriately --- --- * key: "" --- This is the name that will be used to identify the object. --- If an object with the same name already exists, it will be destroyed. --- Note that the keys are unique per subject, so you can have multiple --- objects with the same name as long as they are owned by different subjects. --- --- * type: "Spa:String:JSON" --- --- * value: "{ type = , --- name = , --- args = { ...object arguments... } }" --- The object type can be one of the following: --- - "pw-module": loads a pipewire module: `name` and `args` are interpreted --- just like a module entry in pipewire.conf --- - "metadata": loads a metadata object with `metadata.name` = `name` --- and any additional properties provided in `args` --- - -on_demand_objects = {} - -object_constructors = { - ["pw-module"] = LocalModule, - ["metadata"] = function (name, args) - local m = ImplMetadata (name, args) - m:activate (Features.ALL, function (m, e) - if e then - Log.warning ("failed to activate on-demand metadata `" .. name .. "`: " .. tostring (e)) - end - end) - return m - end -} - -function handle_metadata_changed (m, subject, key, type, value) - -- destroy all objects when metadata is cleared - if not key then - on_demand_objects = {} - return - end - - local object_id = key .. "@" .. tostring(subject) - - -- destroy existing object instance, if needed - if on_demand_objects[object_id] then - Log.debug("destroy on-demand object: " .. object_id) - on_demand_objects[object_id] = nil - end - - if value then - local json = Json.Raw(value) - if not json:is_object() then - Log.warning("loading '".. object_id .. "' failed: expected JSON object, got: '" .. value .. "'") - return - end - - local obj = json:parse(1) - if not obj.type then - Log.warning("loading '".. object_id .. "' failed: no object type specified") - return - end - if not obj.name then - Log.warning("loading '".. object_id .. "' failed: no object name specified") - return - end - - local constructor = object_constructors[obj.type] - if not constructor then - Log.warning("loading '".. object_id .. "' failed: unknown object type: " .. obj.type) - return - end - - Log.info("load on-demand object: " .. object_id .. " -> " .. obj.name) - on_demand_objects[object_id] = constructor(obj.name, obj.args) - end -end - -objects_metadata = ImplMetadata ("sm-objects") -objects_metadata:activate (Features.ALL, function (m, e) - if e then - Log.warning ("failed to activate the sm-objects metadata: " .. tostring (e)) - else - m:connect("changed", handle_metadata_changed) - end -end) diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/static-endpoints.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/static-endpoints.lua deleted file mode 100644 index 5f9a9757..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/static-endpoints.lua +++ /dev/null @@ -1,36 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author Julian Bouzas --- --- SPDX-License-Identifier: MIT - --- Receive script arguments from config.lua -local endpoints_config = ... - -function createEndpoint (factory_name, properties) - -- create endpoint - local ep = SessionItem ( factory_name ) - if not ep then - Log.warning (ep, "could not create endpoint of type " .. factory_name) - return - end - - -- configure endpoint - if not ep:configure(properties) then - Log.warning(ep, "failed to configure endpoint " .. properties.name) - return - end - - -- activate and register endpoint - ep:activate (Features.ALL, function (item) - item:register () - Log.info(item, "registered endpoint " .. properties.name) - end) -end - - -for name, properties in pairs(endpoints_config) do - properties["name"] = name - createEndpoint ("si-audio-endpoint", properties) -end diff --git a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/suspend-node.lua b/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/suspend-node.lua deleted file mode 100644 index 8b5783b3..00000000 --- a/buildroot-external/rootfs-overlay/etc/wireplumber/scripts/suspend-node.lua +++ /dev/null @@ -1,59 +0,0 @@ --- WirePlumber --- --- Copyright © 2021 Collabora Ltd. --- @author George Kiagiadakis --- --- SPDX-License-Identifier: MIT - -om = ObjectManager { - Interest { type = "node", - Constraint { "media.class", "matches", "Audio/*" } - }, - Interest { type = "node", - Constraint { "media.class", "matches", "Video/*" } - }, -} - -sources = {} - -om:connect("object-added", function (om, node) - node:connect("state-changed", function (node, old_state, cur_state) - -- Always clear the current source if any - local id = node["bound-id"] - if sources[id] then - sources[id]:destroy() - sources[id] = nil - end - - -- Add a timeout source if idle for at least 5 seconds - if cur_state == "idle" or cur_state == "error" then - -- honor "session.suspend-timeout-seconds" if specified - local timeout = - tonumber(node.properties["session.suspend-timeout-seconds"]) or 5 - - if timeout == 0 then - return - end - - -- add idle timeout; multiply by 1000, timeout_add() expects ms - sources[id] = Core.timeout_add(timeout * 1000, function() - -- Suspend the node - -- but check first if the node still exists - if (node:get_active_features() & Feature.Proxy.BOUND) ~= 0 then - Log.info(node, "was idle for a while; suspending ...") - node:send_command("Suspend") - end - - -- Unref the source - sources[id] = nil - - -- false (== G_SOURCE_REMOVE) destroys the source so that this - -- function does not get fired again after 5 seconds - return false - end) - end - - end) -end) - -om:activate()