steam-audio icon indicating copy to clipboard operation
steam-audio copied to clipboard

[C-API] iplBinauralEffectApply appears to not be thread safe when used with unique instance of IPLBinauralEffect on each thread

Open Frooxius opened this issue 8 months ago • 2 comments

System Information

  • Steam Audio version: 4.6.0 / 4.5.3
  • Operating System and version: Windows 11
  • CPU architecture: x64-64

Issue Description I am integrating Steam Audio with my custom engine using the C API (using a C# wrapper: https://github.com/Mirsario/SteamAudio.NET)

My audio engine is heavily multi-threaded. I call the iplBinauralEffectApply to spatialize multiple audio sources from multiple thread workers.

Each thread processes one audio source. Each audio source has its own instance of IPLBinauralEffect and own instances of buffers - meaning those aren't shared across threads.

When I render audio this way, the resulting audio has random blips and corruptions, often just in a single channel. If I make this processing single threaded, then everything is fine.

I tried replacing the 4.5.3 version of the library from the C# wrapper with 4.6.0 and the issue still occurs.

I am unsure if this is a bug or if I am using the API incorrectly - the documentation doesn't seem to indicate that the method would not be thread safe, but perhaps I'm missing something?

I have also confirmed that the issue isn't coming from other parts of my audio engine - if I replace the iplBinauralEffectApply call with just a copy of the input buffers to output, everything is fine (except not spatialized of course).

Steps To Reproduce Steps to reproduce the behavior:

  1. Create IPL context & IPLHRTF (happens on single thread)
  2. Create IPLBinauralEffect & buffers for multiple audio sources (also on single thread)
  3. Run iplBinauralEffectApply from multiple threads, each thread processing each individual audio source using its own instance of IPLBinauralEffect

I can provide sample project if needed, but I first wanted to check if I'm doing something wrong with the API.

I've rendered both the single-threaded and multi-threaded behavior into .wav files here (WARNING: The glitches in multithreaded one are loud): AudioFiles.zip

Frooxius avatar Mar 27 '25 01:03 Frooxius

I have found additional information. If I run iplHRTFCreate to create a unique instance of IPLHRTF for each instance of IPLBinauralEffect, then the issue also disappears.

It seems that it occurs when all instances of IPLBinauralEffect share the same instance of IPLHRTF, then they are not thread-safe, but if each one has its own copy, then they can be executed fine.

However this feels "wrong" to me, given that iplHRTFCreate function is heavy to instantiate and it's bound to each IPLBinauralEffect at instantiation of the binaural effect - meaning I couldn't just have a small pool of IPLHRTF instances and then use those dynamically when rendering each binaural effect.

Is this the intended way to use them?

Frooxius avatar Mar 27 '25 07:03 Frooxius

@Frooxius You are correct, in that IPLHRTF is currently safe to use only from a single thread. If you need to use an HRTF from multiple threads, you'll have to create an IPLHRTF on each thread. Since IPLHRTF is a rather heavyweight object, this may be less than ideal. Some thoughts on how to make this better:

  • If you use a thread pool with a fixed number of threads, and run each IPLBinauralEffect on one of those threads, then you only need a single IPLHRTF per thread, rather than per source. You can re-use an IPLHRTF with multiple IPLBinauralEffect objects, as long as they all run on the same thread.
  • There is probably room to refactor IPLHRTF so that the non-thread-safe portion is its own object that can be created per-thread, while the bulk of the object (which is just the HRTF data with various pre-calculations applied) can be shared safely across threads. However, this will be a more involved task.

lakulish avatar Apr 02 '25 18:04 lakulish