Skip to content
Snippets Groups Projects
Commit 1c5729c9 authored by Dylan Aïssi's avatar Dylan Aïssi
Browse files

Add results for test-case 2


Signed-off-by: default avatarDylan Aïssi <dylan.aissi@collabora.com>
parent 933f918f
Branches main
No related merge requests found
Pipeline #626740 passed
......@@ -12,7 +12,7 @@ linkcolor: blue
colorlinks: true
---
*[Draft report - 2023-10-30](https://gitlab.apertis.org/daissi/pipewire-evaluation)*
*[Report - 2023-10-31](https://gitlab.apertis.org/daissi/pipewire-evaluation)*
[PipeWire](https://pipewire.org/) is *de facto* the new audio server used by all
......@@ -248,13 +248,51 @@ The script `test-case-capture.py` can be used to record sound generated by the
board sound cards during the test (i.e. with changing capture sample rate of the
stream redirected to 1 output) to evaluate how the sound is affected.
**TODO**:
- Fix bug where capture sample rate is not changed when `start_sink("my-sink-B")`
is running.
- Reduce time to applied new property to a device or force the device to be
reinitialized with new properties. For now, we have to wait until the device
is IDLE otherwise changes (sample rate change) are not applied.
Something to improve in PW?
## Results
Currently, it's not possible to reconfigure a running device. That means, to
reconfigure the sample rate input, we have to force the device to be idle. In
other words, that means both **input and output** of this device must be idle.
Only forcing the device *input* to be idle is **not enough** to make pipewire
apply the changes.
During the test, two wav files are generated `test-case-capture1.wav` and
`test-case-capture2.wav` (both are available in `results/test-case-2/`). The
first one captures the sound coming from the board USB sound card while the
second capture the sound coming from the board built-in sound card.
In `test-case-capture1.wav`, we can hear the `test300.wav` tone without any
artefact and without any interruption. This stream is therefore not affected by
the reconfiguration of the input of the other sound card.
In the other file `test-case-capture2.wav`, the stream starts with the
`test300.wav` tone, then at *0:03* we stop hearing the tone to make the device
idle allowing the reconfiguration of the sample rate at 32000. At *0:08*, we start
hearing the tone mixed with the `MIC Input` (sample rate at 32000) for 10 secs.
At *0:18*, we have a new stream break for 5 secs for the reconfiguration of the
input rate at 44100. Then, the mixed stream (`test300.wav` tone and `MIC Input`)
restart at *0:18* for 10 secs and so on. The sample rate reconfiguration is
checked during the test in the `FORMAT` column of the `pw-top` output.
In this case, the output is affected by the reconfiguration because we have to
wait the device to be idle to make pipewire apply the new configuration. The
default timeout for switching a device to idle is defined in a wireplumber
config file (i.e. `/usr/share/wireplumber/main.lua.d/50-alsa-config.lua`) at 5
secondes (see *session.suspend-timeout-seconds*). It should be possible to
reduce the timeout to only 1 seconde (setting `0` disables suspend on idle, so
not the desired behavior), but that will still impact the direct output.
Moreover, the reconfiguration of sample rate was applied only on the input node
of the sound (i.e. in our example `alsa_input.platform-sound.stereo-fallback`) and
not on the whole device, but `pw-top`reported during the test that the output node
(i.e. `alsa_output.platform-sound.stereo-fallback`) was reconfigured in the same
way. That means both *input* and *output* are affected by the reconfiguration
on the *input*.
To summarize, the reconfiguration of audio capture impacts the direct output as
we have to stop the stream to make pipewire apply the changes, but it doesn't
impact the unrelated output. If feasible, adding a way to force pipewire to
reconfigure a device without impacting (or at least with by reducing its impact)
the current streams is probably a way to go.
# Test case 3: Capturing multiple inputs to multiple outputs
......@@ -422,7 +460,10 @@ built-in sound card, it's a little longer with *1min06* (+6secs) to fully recove
# Results summary
- Adding or removing an audio stream in a running output neither impacts that
output or any other system outputs. See *Test case 1: Results* for more details.
- TODO. See *Test case 2: Results* for more details.
- Reconfigurating of audio capture impacts the direct output as we have to make
the whole device (input **and** output) idle to make pipewire apply the changes,
but it doesn't impact the unrelated output. See *Test case 2: Results* for more
details.
- Starting and/or stopping inputs independently does not cause disruptions in the
outputs, but mixing streams from different sources gives sound arfects during
our test. See *Test case 3: Results* for more details.
......
File added
File added
#!/bin/sh
# Decrease timeout to suspend device from 5secs to 1sec
sudo mkdir -p /etc/wireplumber/main.lua.d/
sudo cp -a /usr/share/wireplumber/main.lua.d/50-alsa-config.lua \
/etc/wireplumber/main.lua.d/50-alsa-config.lua
sudo sed -i 's/--\["session.suspend-timeout-seconds"\] = 5/--\["session.suspend-timeout-seconds"\] = 1/g' /etc/wireplumber/main.lua.d/50-alsa-config.lua
systemctl restart --user wireplumber.service
#!/usr/bin/env python3
#from pwevaltools import *
from itertools import cycle
import json
import select
......@@ -30,12 +32,6 @@ def pw_cli_rate(device_id, rate):
subprocess.run(cmd_pw_cli, shell=True)
def pw_cli_reduce_timeout_idle(device_id):
cmd_pw_cli = "pw-cli s "+str(device_id)+" Props '{ params: [ \"session.suspend-timeout-seconds\", \"1\" ] }'"
print(f".. DBG: {cmd_pw_cli}")
subprocess.run(cmd_pw_cli, shell=True)
def pw_link(out_pattern, in_pattern):
cmd_pw_link = ["pw-link",str(out_pattern),str(in_pattern)]
subprocess.run(cmd_pw_link)
......@@ -56,16 +52,20 @@ def pw_metadata(out_pattern, in_pattern):
def create_loopback_device(name):
print(f"Creating a virtual device named {name}")
cmd_pw_loopback = "pw-loopback -m '[FL FR]' --capture-props='media.class=Audio/Sink node.name="+name+"'"
cmd_pw_loopback = "pw-loopback -m '[FL FR]'"
cmd_pw_loopback += " --capture-props='media.class=Audio/Sink node.name="+name+"'"
#cmd_pw_loopback += " --playback-props='node.name="+name+"'"
subprocess.Popen(cmd_pw_loopback, shell=True)
def create_loopback_mic_device(name):
print(f"Creating a virtual device named {name}")
cmd_pw_loopback = "pw-loopback -m '[FL FR]' --capture-props='media.class=Audio/Sink node.name="+name+"'"
cmd_pw_loopback += " --capture alsa_input.platform-sound.stereo-fallback --playback alsa_output.platform-sound.stereo-fallback"
cmd_pw_loopback = "pw-loopback -m '[FL FR]'"
cmd_pw_loopback += " --capture-props='media.class=Audio/Sink node.name="+name+" target.object=alsa_input.platform-sound.stereo-fallback'"
cmd_pw_loopback += " --playback-props='node.name="+name+" target.object=alsa_output.platform-sound.stereo-fallback'"
subprocess.Popen(cmd_pw_loopback, shell=True)
time.sleep(2) # Wait for the node to be up
def start_jackplay(my_sound):
......@@ -195,11 +195,13 @@ def start_sink(my_sink_name, my_wav, device_playback, my_id_used=None):
my_pw_dump = pw_dump("pw-dump-temp-"+my_sink_name+"-4.json")
pw_dot("pw-dot-temp-"+my_sink_name+"-4.png")
my_sink_node_group = "output."+my_sink_node_group
# This is required to identify which jackplay are already processsed
return my_jackplay_id
return my_jackplay_id, my_sink_node_group
def capture_mic_reconf(my_input_mic):
def capture_mic_reconf(my_input_mic, node_to_close):
my_pw_dump = pw_dump("pw-dump-temp-mic-0.json")
my_capture_id = None
for pw_obj in my_pw_dump:
......@@ -214,18 +216,12 @@ def capture_mic_reconf(my_input_mic):
my_capture_id = pw_obj["id"]
break
# Reduce timeout for setting a device IDLE, this is requied since we will
# change device's properties (sample rate), but those are applied only when
# device is IDLE.
#pw_cli_reduce_timeout_idle(my_capture_id)
# Doesn't work...
# Due to hardware limitation we cannot directly capture at 8K or 16K Hz
# Only 32000, 44100 and 480000 are supported
# So, instead we will switch between 32K and 48K to capture "MIC Input"
my_supported_sample_rates = [32000, 44100, 48000]
my_loopback_mic_name = "my_loopback_mic"
create_loopback_device(my_loopback_mic_name)
create_loopback_mic_device(my_loopback_mic_name)
pw_dot("pw-dot-temp-mic-0.png")
print("\nEverything is ready for capturing MIC input!")
......@@ -236,20 +232,30 @@ def capture_mic_reconf(my_input_mic):
# Change sample rate every 10 secs
for my_srate in cycle(my_supported_sample_rates):
pw_link_delete(my_input_mic, my_loopback_mic_name)
pw_link_delete(node_to_close, my_sound_cards[1])
pw_link_delete(my_loopback_mic_name+":output_FL", my_sound_cards[1]+":playback_FL")
pw_link_delete(my_loopback_mic_name+":output_FR", my_sound_cards[1]+":playback_FR")
# Need to wait until device is idle otherwise reconfigure is not applied,
# Something to improve in pipewire? adding an option to force a device
# reconfig without having to wait idle?
time.sleep(5)
print(f"Setting capture sample rate to {my_srate}")
# !!! FIXME !!! changing srate (i.e. pw_cli_rate) doesn't work when `start_sink("my-sink-B")` is running
pw_cli_rate(my_capture_id, my_srate)
time.sleep(5)
# Need to wait until device is IDLE otherwise reconfigure isn't applied,
# TODO: this part needs to be improved in pw? (force a device to be reinitilizaed if we change properties?)
pw_link(my_input_mic, my_loopback_mic_name)
pw_link(node_to_close, my_sound_cards[1])
pw_link(my_loopback_mic_name+":output_FL", my_sound_cards[1]+":playback_FL")
pw_link(my_loopback_mic_name+":output_FR", my_sound_cards[1]+":playback_FR")
# pw_dot("pw-dot-temp-mic-X.png")
print(f"Please check sample rate with pw-link") # For DEBUG
print(f"... sample rate for {my_input_mic} should be {my_srate}") # For DEBUG
a, b, c = select.select([sys.stdin], [], [], 10)
if (a):
break
pw_link_delete(my_input_mic, my_loopback_mic_name)
if __name__ == "__main__":
......@@ -264,13 +270,13 @@ if __name__ == "__main__":
my_input_mic = "alsa_input.platform-sound.stereo-fallback"
# Make sound with USB sound card
my_jackplay_id_used_A = start_sink("my-sink-A", "test300.wav", my_sound_cards[0])
my_jackplay_id_used_A, my_jackplay_node_group_A = start_sink("my-sink-A", "test300.wav", my_sound_cards[0])
# Make sound with onboard card
my_jackplay_id_used_B = start_sink("my-sink-B", "test300.wav", my_sound_cards[1], my_jackplay_id_used_A)
my_jackplay_id_used_B, my_jackplay_node_group_B = start_sink("my-sink-B", "test300.wav", my_sound_cards[1], my_jackplay_id_used_A)
# Capture MIC INPUT on board with audio input reconfiguration
capture_mic_reconf(my_input_mic)
capture_mic_reconf(my_input_mic, my_jackplay_node_group_B)
# End of test case 2
print("Press any key to stop the playback...")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment