Wednesday, November 16, 2011

Tutorial: JACK Ringbuffers

Hey All,

A quick tutorial on basic Jack ringbuffer usage. Ringbuffers are an easy way to exchange data from one thread to another in a realtime safe way. This means that no thread will block when reading or writing, and hence you use ringbuffers in a real-time thread.

For this tutorial I'll be using the "standard" linux audio ringbuffer that comes with JACK. Its docs are available here: http://jackaudio.org/files/docs/html/ringbuffer_8h.html

So what were going to do is:
1. Setup a ring buffer
2. Register a JACK client, and give it a process callback
3. Write data in the "local" thread, ie: our main()
4. Make JACK print out any data it recieves in its RT thread

Note that here the JACK thread is our READ thread , and the main() thread is the WRITE thread. This is important, because a ringbuffer like the JACK one will only work in ONE direction.

Find the well commented source here: https://sites.google.com/site/harryhaaren/Home/main.cpp

Note the compile command is: g++ main.cpp `pkg-config --cflags --libs jack` (its also in the source.. but just to make sure :)

Responses welcome, -Harry

PS: Note there are many different implementations of ringbuffers, all with benefits of their own. The JACK ringbuffer is a simple and IMO easily usable one, and that's the reason I like it. I don't want a fancy impossible multi-read multi-write templated 13 class derived special oval ringbuffer, just something that does what it says on the tin :)

5 comments:

  1. Nice code. Here's a couple of comments:

    1. This test is not necessary:

    if ( result != sizeof(int) ) {

    There must be some weird
    bug in jack_ringbuffer_read for this to fail.


    2. There should probably be a comment that
    doing "std::cout <<" is not real-time safe.
    Doing so should only be done for debugging or
    educational purposes.

    ReplyDelete
  2. Same goes for this test:

    if (written != sizeof(int) ) {

    (see the source code of jack/ringbuffer.c. Your test can never succeed.)

    ReplyDelete
  3. Great example! How can you ensure that the main thread doesn't exit while the process callback is happening? Would it be safe to add something like:

    while (jack_ringbuffer_read_space(buffer))
    usleep(1000);

    after the for loop in the main thread?

    ReplyDelete
  4. Hi Tristan.

    Essentially yes, you could do that. I presume you're goal is to have the JACK thread read all the events before the program quits?

    Then your on the right track yup! -Harry

    ReplyDelete
  5. You don't need to sleep 1 second. It is easy to take the samplerate (48khz, for instance) and the block size (512 frames). So you can calculate how long it will take to process block. In this example, 10,666666667 milliseconds. So you will not need to wait this 0.99 seconds to close the program.

    ReplyDelete