Hi there,
I have an app developed with JUCE (v7). I am not directly the developper of this app but I have a line on him when needed.
I am developing a specific audio driver porting Dante protocole into this app. And running all of this on intel machines with multi-cores (Efficient & Performance) and a lowlatency linux kernel.
My driver is compliant with the AudioIODevice class.
I have then 2 threads in my driver when running the JUCE app : one for push/pulling sample to/from the Dante driver, one to push/pull sample to/form JUCE as a callback. They have to synchronize, naturally at some point…
Combining both Audinate examples and JUCE Alsa driver, I have ringbuffers inbetween to allow different buffer/vector size on each side (JUCE callback vector size is settable from 32 to 4096 samples in the interface, driver push/pull block size is not, fixed to 32 samples). And a mechanism to notify (with atomic) between the threads.
Both of these threads have an RT priority in the POSIX system (realtime -99) Thank’s to Thread::RealtimeOptions structure from JUCE).
However in this configuration, I have weird artefacts (from regular clicks to ring modulator effect) with bigger block size on JUCE callback (>= 256 samples).
There probably is something wrong with my code but…
1/ it was working great in JUCE 6.1.3 and
2/ strangely, those artefacts stop completely and audio comes back clean as soon as I use the chrt
command of Linux to set JUCE Callback priority to something else (and lesser priority) than RT/-99.
=> I really don’t understand why this change would make things better…
Also, changing the priority
value in the RealtimeOptions struct
does not seem to affect the effective priority of the threads reported by htop
command.
Any idea/pointer on this kind of things will be appreciated!
Thank’s a lot
Benjamin
The old embedded maxim applies: two realtime threads in the same app is one too many realtime threads.
You could probably get better results if you have one realtime thread doing the i/o of both queues.
But to address your issue with priorities, without knowing more details about your app it sounds, at first, like you’re running into the classic contention issue and the threads, being equivalent priority, are producing a race condition for the associated resources.
If you really do have to have two realtime threads, set one slightly lower priority than the other (by a single unit even suffices), so that the system is not racing to supply enough ticks to both threads … when you change the priorities with chrt, you give the kernel just enough slice to defeat the race condition and things smooth out.
Do you think your “maxim” applies to plugins that run in a host? If so, I do not agree
Thank you for your answer @austrianaudioJV
I do agree about the maxim and that it would be better with only one realtime thread. However, I have some constraints on the Dante/Audinate side that made me write the code like this…
It may possible to do it very differently but implies a big refactoring that will take more time than I have right now.
I also see clearly the race conditions you are mentioning and thought the same thing.
The shared resource however (ringbuffer) is supposed to be non-blocking. Only the write & read heads are atomic and only one of the thread modify each of them. Close to a lock-free-fifo (or so I hope!).
Last question remains: I cannot make JUCE set the priority just a little lesser with the priority
field of the RealtimeOptions struct
. From my first rough test (I have to investigate further) this parameter does not seem to have any visible effect… Why is that?
I will continue to investigate the code of JUCE for this last question and try to evaluate the refactoring need for a 1 realtime thread version.
Anyone has already fiddle with this parameter?
I saw this post as well : startRealtimeThread examples - #3 by lcapozzi
But it is centered around Apple problems…
Thank’s
Benjamin
My bad on this one. Sorry, I had an error in my code (not enough caffeine?). Once it’s correctly programmed, a lesser value in the struct
effectively means a lesser priority shown easily with htop
.
I’ll try this config as a quick fix and probably dig into the code again to have only one realtime thread later.
Best,
Benjamin
1 Like