-
Notifications
You must be signed in to change notification settings - Fork 12
Threads
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 because we want the user to still be able to interact with the GUI while loading the file. 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). 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 feature look up definitions for classes:
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 that 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.
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:
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 buffer that is 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 is not being shut down the run functions loops. The wait(int)
statement causes the thread from which the function is called to wait for the time in millisecond specified by the first argument.