Hi Dan,
why don't you answer on the mailing list?
Dan Mills wrote:
On Fri, 2008-08-22 at 20:26 +0200, Olivier Guilyardi
wrote:
In Jackbeat I perform sample rate conversion using libsamplerate in the realtime
thread. I have recently realized that might be a mistake in regard to
performance. The SRC is used on audio samples which sample rate might differ
from the external (Jack) one, and also as a simple pitch shifter.
<Snip>
In this regard, I thought about setting a (or
several) non-RT helper thread(s)
up, where the heavy stuff would happen, and feed the converted content into the
RT thread through a ringbuffer. I understand that this approach would require
implementing a sync callback for the helper threads to get ready (fill buffers,
etc...) whenever the transport position changes.
That is a fairly common approach, one thread per core is good. I have
code that reads audio, sample rate converts it, time-stretches it if
appropriate and sticks it in a ring buffer. The realtime thread does not
pay any attention to the transport at all, it just handles the realtime
controls (some mixing and routing in my case).
I am unsure what you mean by "core".
One thing you do want to watch is that the disk IO
threads should
probably run at a realtime priority (just lower then the one for the
audio thread), otherwise a make -j10 on the kernel can result in
ringbuffer under run.
Disk IO is another problem to me. I would prefer to focus on computation load
here, that is: helper threads that performs audio conversion/transformation,
etc.... I assume that the thread priority you advise also concerns this though.
There are a few details to making everything work
right when you have
more playbacks then available cores, and the approach does not work well
if you need to vary things like timestretch in realtime.
Ah right, thanks, I had forgotten about this. Indeed, the good side of my
current approach (where the SRC is performed in the realtime thread) is that it
gives the user a good experience when varying the pitch: the effect is
immediate. So I'm not operating on live audio input, but I do handle live
"control" input, and in this regard the audio sample content is not exactly
known in advance anymore (since it is transformed according to realtime user
requests).
I'm asking
this because the SRC computation load currently has an impact on the
output quality (ticks, xruns) with many tracks or on slower systems, and I'm
thinking about adding real pitch and time shifting using Rubberband in the near
future, which is certainly going to be a lot heavier.
If you need to be able to vary the timestrtch in real time with a file
playing, any ring buffer after the timestretcher will need to be short,
otherwise it is fairly straightforward.
That worries me a bit. If my ringbuffer is really short, for responsive user
control, and the time stretching performed in a non-RT thread, isn't this going
to make things worse?
What about the following idea:
Say I make a Converter object, which performs time stretching. It doesn't know
anything about threading.
I also have a Reader object, with an internal ringbuffer, it reads raw audio
data, converts it using the Converter, and fills its ringbuffer with the
converted content.
The Reader objects exposes read(double time_ratio, int nframes) to the realtime
thread. When this function gets called, the Reader checks if it has data time
stretched according to this time ratio in its internal ringbuffer. If it does it
simply returns it. Otherwise, it calls the Converter on the fly, discarding the
irrelevant data in the ringbuffer, and causing a sudden small overhead.
Once the time ratio has stopped varying (the user has stopped playing with the
knob), the ringbuffer rapidly gets filled with data at the right time ratio, and
the overhead vanishes.
Volatile is your friend as is pthread_mutex_trylock
(Particularly handy
if you have a vector of disk readers and a small handful of threads
servicing them).
Thanks, I knew little about volatile, I've read some more about it, and already
fixed a possible bug in my code. Yes, I recently read about
pthread_mutex_trylock being acceptable in the realtime thread on the jack-devel
mailing list.
Regards,
--
Olivier Guilyardi / Samalyse