wwmm / easyeffects

Limiter, compressor, convolver, equalizer and auto volume and many other plugins for PipeWire applications
GNU General Public License v3.0
6.6k stars 270 forks source link

Feature request: Flush Plugin Caches with Silence #2938

Open GhostNaN opened 8 months ago

GhostNaN commented 8 months ago

For a while now I have been experiencing popping/crackling when starting a new audio stream. This issue is already well known as seen here https://github.com/wwmm/easyeffects/issues/1856 and here https://github.com/wwmm/easyeffects/issues/1392 So I understand that the issue is:

plugins you are using still has some of the old data saved internally when you play something new.

After doing my own testing, I can also confirm running this: aplay -r 48000 -f S24_3LE /dev/zero as suggested by someone seems to "fix" this issue. Although, it's less than ideal to just have a silent audio stream constantly playing in the background.

So would it be possible to implement some sort of feature to just play silence after a stream ends? For example, having a setting in preferences that just adds 50 ms of silence or something? The idea is just to flush the cache of the "old data" to avoid this issue.

Digitalone1 commented 8 months ago

Did you already enable the inactivity time option with a huge number (like 1 hour)?

GhostNaN commented 8 months ago

30s, off, 3600s no change. It's not because easyeffects is going inactive.

wwmm commented 8 months ago

30s, off, 3600s no change. It's not because easyeffects is going inactive.

Run pw-dot so we can see each link state. Its output file can be viewed with the command xdot. If the links are there and still in the active state then EE is already playing silence. But there are situations where PipeWire may have paused the pipeline on its own. We create passive links so it can do that when possible.

So would it be possible to implement some sort of feature to just play silence after a stream ends? For example, having a setting in preferences that just adds 50 ms of silence or something? The idea is just to flush the cache of the "old data" to avoid this issue.

There isn't an elegant or straightforward way to do that. PipeWire is the one managing each filter state and there isn't a pipeline object with flushing mechanisms like in GStreamer. What we could try is some kind of hack. Creating a node like the one from the test signal that plays silence and linking it to our virtual sink. If that is going to have some unexpected side effect is hard to say without trying it.

wwmm commented 8 months ago

But there are situations where PipeWire may have paused the pipeline on its own.

It always does it on its own. What I meant is that it could have done that before we usually remove the links between the filters. The removal of the links is what the timeout controls.

GhostNaN commented 8 months ago

It always does it on its own. What I meant is that it could have done that before we usually remove the links between the filters.

Like when a node suspends with ["session.suspend-timeout-seconds"] = 5 ?

The removal of the links is what the timeout controls.

I have been using qpwgraph to visualize what is going on and I could see the links being removed with the timeout.

There isn't an elegant or straightforward way to do that. PipeWire is the one managing each filter state and there isn't a pipeline object with flushing mechanisms like in GStreamer. What we could try is some kind of hack. Creating a node like the one from the test signal that plays silence and linking it to our virtual sink. If that is going to have some unexpected side effect is hard to say without trying it.

Yeah, I didn't think this was going to be clean. And your idea of implementation was effectively what I was asking for. If you find this to be too janky/hacky, I understand.

wwmm commented 8 months ago

Like when a node suspends with ["session.suspend-timeout-seconds"] = 5 ?

I am not sure. Depending on how complex the processing graph is PipeWire may or may not be able to automatically stop sending buffers to filters in pipelines whose players/recorders are inactive. We unlink the filters exactly as a workaround for the fact PipeWire is not always able to avoid wasting CPU in these situations. It is not clear to me if PipeWire is reading the timeout you are talking about when deciding if it has to stop calling filters callbacks or not.

Yeah, I didn't think this was going to be clean. And your idea of implementation was effectively what I was asking for. If you find this to be too janky/hacky, I understand.

Compared to what is possible to do in GStreamer it is definitely not good. But it is not the first time that the lack of flushing has caused bad side effects. Eventually we will have to force some kind of flushing. I will try to find some time to investigate how hard it will be to put a hack solution in place.

zDEFz commented 1 month ago

This also fixed my issues!

like you said, aplay -r 48000 -f S24_3LE /dev/zero

Stopped using EE now. Using pipewire. In the bin with it :D https://docs.pipewire.org/page_module_parametric_equalizer.html

No more issues.