Skip to content

Threads

passivist edited this page Oct 15, 2016 · 6 revisions

Threads

Adding a thread

Threads allow the simultaneous execution of two processes on the same CPU. Our program as it is implemented right now will be blocked for the duration of loading the sound file. This can be quite long depending on the size of the file and the speed of the system. In general we want to avoid this kind of behavior we want the user to still be able to interact with the GUI while loading the file for example. It is sensible to run the function that is doing the loading on a different background thread.

In JUCE the objects AudioProcessor and AudioProcessorEditor are already running on two seperate threads (called the audio and the messaging-thread) already. We can also implement a background thread on each of these objects by making the class inherit from the Thread class. For loading the buffer we will implement a background thread for the AudioProcessorEditor object. We do that by adding Thread to the list of classes AudioProcessorEditor inherits from:

class GrrnlrrAudioProcessorEditor  : public AudioProcessorEditor,
                                     public Thread

As discussed before we sometimes have to implement virtual functions when we inherit from a class. To find out which functions we can take a look at the file with the class definition for Thread. All the definitions for the JUCE classes live in the 'modules' folder in the JUCE directory. This particular file can be found at modules/juce_core/library. A lot of IDEs also have a feature look up definitions for classes:

jump to definition xcode

This is some of the information we find in the file 'thread.h':

/**
    Encapsulates a thread.

    Subclasses derive from Thread and implement the run() method, in which they
    do their business. The thread can then be started with the startThread() method
    and controlled with various other methods.

    This class also contains some thread-related static methods, such
    as sleep(), yield(), getCurrentThreadId() etc.

    @see CriticalSection, WaitableEvent, Process, ThreadWithProgressWindow,
         MessageManagerLock
*/

We can see that we have to implement the run() method. To do that we first declare it in the header of the AudioProcessorEditor class:

void run() override;

We also define the function:

void GrrnlrrAudioProcessorEditor::run()
{
    
}

We also have to initialize the thread in the constructors initializer list, start it in the constructors body and stop it in the deconstructor:

GrrnlrrAudioProcessorEditor::GrrnlrrAudioProcessorEditor (GrrnlrrAudioProcessor& p)
    : AudioProcessorEditor (&p), Thread("sample loading thread"), processor (p)
{
    
    formatManager.registerBasicFormats();
    startThread();
    
    String path = "/Users/raffaelseyfried/dev/eigene/GRRNLRR/Resources/Piano_D11_High.wav";
    loadSample(path);
    
    setSize (400, 300);
}

GrrnlrrAudioProcessorEditor::~GrrnlrrAudioProcessorEditor()
{
    stopThread(4000);
}

Note what we give the thread a name when we initialize it. This is quite useful for debugging purposes. This is a good point to recompile and see if everything is still working.

Checking the status of buffers

What we want to implement now is an algorithm that checks in a predefined interval if a buffer needs to be loaded or deleted. To achieve this we declare two more functions in the AudioProcessorEditor class:

void checkForPathToOpen();
void checkForBuffersToFree();

The loop we want to have in the end can be sketched out like this: run loop

The run function first checks if the thread the function is running on is currently in the process of shutting down. If that's not the case checkForPathToOpen is called. checkForPathToOpen checks if there is a path to load a sample with. If there is then loadSample is called. Afterwards checkForBuffersToFree is called. This functions deletes any buffers that are not needed anymore. The run function looks like this:

void GrrnlrrAudioProcessorEditor::run()
{
    while(! threadShouldExit()){
        checkForPathToOpen();
        checkForBuffersToFree();
        wait(500);
    }
}

The exclamation mark (!) is a logical negation, changing true to false and false to true for boolean values. In this case it negates the bool threadShouldExit returns. So while the thread should not exit the run functions loops. The wait(int) causes the thread from which the function is called to wait for the time in millisecond specified by the first argument.

Thread Safe Sample Management](https://github.com/passivist/GRNLR/wiki/Thread-Safe-Sample-Management)

Clone this wiki locally