diff --git a/src/audio/pipewire/PwJamesDspPlugin.cpp b/src/audio/pipewire/PwJamesDspPlugin.cpp index 42c7d4cc..4f9737b9 100644 --- a/src/audio/pipewire/PwJamesDspPlugin.cpp +++ b/src/audio/pipewire/PwJamesDspPlugin.cpp @@ -2,6 +2,10 @@ #include #include +#if defined(__x86_64__) || defined(__i386__) +#include +#endif + extern "C" { #ifdef DEBUG_FPE #include @@ -78,6 +82,20 @@ void PwJamesDspPlugin::process(float* left_in, float* right_out, size_t length) { +#if defined(__x86_64__) || defined(__i386__) + // Denormals are computed ~10x slower on x86. The DSP state decays into the + // denormal range once the input turns into digital silence (e.g. right after + // a stream stops), the filter no longer fits into the graph quantum and + // every cycle ends in an xrun, which keeps one CPU core busy forever. + // Enable flush-to-zero / denormals-are-zero once per processing thread. + static thread_local bool denormals_flushed = false; + + if (!denormals_flushed) { + _mm_setcsr(_mm_getcsr() | 0x8040u); // FTZ | DAZ + denormals_flushed = true; + } +#endif + if (bypass) { memcpy(left_out, left_in, length * sizeof (float)); diff --git a/src/audio/pulseaudio/wrapper/gstjamesdsp.c b/src/audio/pulseaudio/wrapper/gstjamesdsp.c index e3b86a8d..7da62f5d 100644 --- a/src/audio/pulseaudio/wrapper/gstjamesdsp.c +++ b/src/audio/pulseaudio/wrapper/gstjamesdsp.c @@ -7,6 +7,10 @@ #include #include +#if defined(__x86_64__) || defined(__i386__) +#include +#endif + GST_DEBUG_CATEGORY_STATIC (gst_jamesdsp_debug); #define GST_CAT_DEFAULT gst_jamesdsp_debug /* Filter signals and args */ @@ -241,6 +245,18 @@ gst_jamesdsp_transform_ip(GstBaseTransform *base, GstBuffer *buf) { GstClockTime timestamp, stream_time; GstMapInfo map; +#if defined(__x86_64__) || defined(__i386__) + /* See PwJamesDspPlugin::process(): denormal DSP state after the input + * turns to digital silence is ~10x slower on x86 and makes the pipeline + * overrun. Enable flush-to-zero / denormals-are-zero once per thread. */ + static __thread gboolean denormals_flushed = FALSE; + + if (!denormals_flushed) { + _mm_setcsr(_mm_getcsr() | 0x8040u); /* FTZ | DAZ */ + denormals_flushed = TRUE; + } +#endif + timestamp = GST_BUFFER_TIMESTAMP (buf); stream_time = gst_segment_to_stream_time(&base->segment, GST_FORMAT_TIME, timestamp);