Table of Contents
- Getting started
- The demo project
- Thread-safe techniques
- Reading the audio on the background thread
- See also
This tutorial shows how to play and loop audio stored in an AudioSampleBuffer object using thread-safe techniques. A technique for loading the audio data on a background thread is also introduced.
Platforms: Windows, Mac OS X, Linux
This tutorial leads on from Tutorial: Looping audio using the AudioSampleBuffer class. If you haven't done so already, you should read that tutorial first.
Download the demo project for this tutorial here: tutorial_looping_audio_sample_buffer_advanced.zip. Unzip the project and open it in your IDE.
If you need help with this step, see Tutorial: Getting started with the Projucer.
The demo project implements similar behaviour to the demo project from Tutorial: Looping audio using the AudioSampleBuffer class. It allows the user to open an audio file that is loaded into a buffer and played in a loop. One major difference in this tutorial is that the audio system is kept running, rather than shutting it down each time we browse for a file. This is achieved by using some helpful classes for communicating between threads in a thread-safe manner.
You should recall in Tutorial: Looping audio using the AudioSampleBuffer class how we solved the potential problem of the audio thread and the message thread accessing potentially incomplete or corrupted data. Just before we browsed for a file we shut down the audio system. Then, once a file was selected, we opened the file and restarted the audio system. This is clearly an impractical and cumbersome method in a real application!
The ReferenceCountedObject class is a useful tool for passing messages and data between threads. Here, we store our AudioSampleBuffer object and the playback position in a ReferenceCountedObject class. To help with debugging, and to help illustrate how the class works, we also include
name member (although this isn't strictly necessary for the class to function):
typedef at the top of the class is an important part in implementing a ReferenceCountedObject subclass. Rather than storing our
ReferenceCountedBuffer object in a raw pointer, we store it in a
ReferenceCountedBuffer::Ptr type. It is this that manages the reference count of the object (incrementing and decrementing as necessary) and its lifetime (deleting the object when the reference count reaches zero). We can also store an array of
ReferenceCountedBuffer objects using the ReferenceCountedArray class.
MainContentComponent class we store both an array and a single instance:
buffers member keeps hold of our buffers in the array until we are absolutely sure they are no longer needed by the audio thread. The
currentBuffer member holds the currently selected buffer.
MainContentComponent class inherits from the Thread class:
This is used to implement our background thread. Our overridden Thread::run() function is as follows:
Here, we check whether there are any buffers to be freed, then our thread waits for 500ms or to be woken up (using the Thread::notify() function). Essentially, this means that the check will occur at least every 500ms. The
checkForBuffersToFree() function searches through our
buffers array to see if any buffers can be freed:
- : It is useful to remember to iterate over the array in reverse in these situations. It is easier to avoid corrupting the array index access if we remove items as we iterate over the array.
- : This retains a copy of a buffer at the specified index.
- : If the reference count at this point is equal to 2 then we know that the audio thread can't be using the buffer and we can remove it from the array. One of these two references will be in the
buffersand the other will be in the local
buffervariable. The removed buffer will delete itself as the
buffervariable goes out of scope (as this will be the last remaining reference).
Of course, we need to start the thread as our application starts, which we do in our
openButtonClicked() function is similar to the
openButtonClicked() function from Tutorial: Looping audio using the AudioSampleBuffer class with some minor differences:
Here the differences are that we:
- Allocate a new instance of our
- Read the audio data into the AudioSampleBuffer object that it contains.
- Make it the current buffer.
- Add it to our array of buffers.
To clear the current buffer we can just set its value to
getNextAudioBlock() function is similar to the
getNextAudioBlock() function from Tutorial: Looping audio using the AudioSampleBuffer class except we need to access our current
ReferenceCountedBuffer object and the AudioSampleBuffer object it contains.
The important changes are:
- : We retain a copy of the
currentBuffermember. After this point in the function it doesn't matter if the
currentBuffermember is changed on another thread since we have taken a local copy.
- : We output silence if the
currentBuffermember when we took a copy.
- : We access the AudioSampleBuffer object within the
- : We get the current playback position for the buffer.
- : After modifying the current playback position, we store it back in the
This algorithm ensures that
ReferenceCountedBuffer objects aren't deleted on the the audio thread. It is not a good idea to allocate or free memory on the audio thread. The
ReferenceCountedBuffer objects will only be deleted on our background thread.
Our application still reads the audio data on the message thread. This is not ideal since this blocks the message thread and large files could take some time to load. In fact, we can also use our background thread to perform this task.
First, add the following member to the
Now change the
openButtonClicked() function to swap the full path of the file into this member:
Here we also wake up the background thread since we are going to call a function on the background thread to open the file.
run() function should be updated as follows:
checkForPathToOpen() function checks the
chosenPath member by swapping it into a local variable:
pathToOpen variable is an empty string then we know there isn't a new file to open. The remainder of the code in this function should be familiar to you.
Run the application again and it should still function correctly.
- The final code for this section can be found in the
MainComponent_02.cppfile within the
Sourcedirectory of the demo project.
This tutorial has introduced some useful techniques for passing data between threads, especially in an audio application. After reading this tutorial you should be able to:
- Implement a subclass of the ReferenceCountedObject class.
- Maintain the lifetime of a ReferenceCountedObject in a multi-threaded application.
- Implement a background thread to perform tasks such as deleting objects that are no longer needed and performing file reading operations.
Generated on Fri Jan 12 2018 09:51:15 for JUCE by 1.8.13