To poll or thread or . . . .?


#1

Hi all!
I’m new here, so hello. I’m totally new to programming real-time stuff. I have a dynamic library that needs to be called many times per second to process audio frames. I’m (trying) to use JUCE as a GUI to control the model parameters. So far I have managed to get JUCE to load and call my lib with no problems. My library works fine if it is called in a loop like so:

while(1){
mdl_step(pointers to parameters and stuff);
}

. . . but then everything locks up and I can’t change any parameters or do anything else. I’d like to be able to change parameters while my loops is running. If anyone could point me in the direction of some reading material or provide any clues I’d be massively grateful.

Thanks!


C++ threads vs JUCE threads - threading tutorials?
#2

Use the Thread-Class for your processing, be sure to be thread-safe. If your sharing parameters between the GUI and the processing, use a Critical Section, and lock as short as possible.


#3

Great. Thanks for the reply. I have never multi-threaded anything before so I can see this being quite a challenge. I just have a few sliders and I’d like to pass the values from the sliders into my lib that is running in an infinite loop. Something like

while(1){
param = slider->getValue();
mdl_step(&param);
}

But I guess if I set param in one thread, the other thread just has to loop calling the step function. I’m guessing this kind of design must be fairly common? If anyone could post a really simple source code or even pseudo code example of that would be great. Could I skip threads all together and use a loop to just update parameters and check for an exit signal? Sorry to be an (enthusiastic) moron.

:slight_smile:


#4

ok this is pseudo code:





class myThread : public Thread
{
    ...
    ...
	void run()
    {
       while (! threadShouldExit())
        {
			{
				Scopedlock (myCriticalSection);
				local_copy_param = slider_param
			};	

			mdl_step(&local_copy_param);
        }
    }
}


On the GUI-Thread:

{
	when the slider is changed
	
	{
			Scopedlock (myCriticalSection);
			slider_param = slider->getValue();
	}
	
}

#5

This is seriously appreciated. Thanks so much. I’ll be sure to let you know how I get on.


#6

Ok, I’m failing quite badly at this. :cry: Let me tell you where I am. So far I have gone through haydxn’s wonderful JUCE tutorial and am able to make the basic interface that I need for my application. I have a cursory knowledge of C and know very little about C++ (I’m working on this) but am generally able to cobble bits of code together to do what I want.

At the moment I have a:

class MainComponent : public Component, public SliderListener

With all of the gui components in that I need and have tried to make a thread class based on the provided pseudocode:

[code]class myThread : public Thread
{
private:
// dll stuf
void* lHandle;
void (mdl_terminate)(void);
void (mdl_step)(int nbrInputArgs, double inputArgs, int nbrOutputArgs, double
outputArgs);
void (*mdl_initiate)(void);

//IO stuff
int nInputs;
int nOutputs;	
double nothing;

double local_copy_param;

public:
CriticalSection myCriticalSection;

void run()
{
	lHandle = PlatformUtilities::loadDynamicLibrary(T("controller"));
	mdl_initiate	= (void(*)(void))PlatformUtilities::getProcedureEntryPoint(lHandle, T("_initiateController"));	
	mdl_step		= (void(*)(int nbrInputArgs, double* inputArgs, int nbrOutputArgs, double* outputArgs))PlatformUtilities::getProcedureEntryPoint(lHandle, T("_getControllerOutput"));
	mdl_terminate	= (void(*)(void))PlatformUtilities::getProcedureEntryPoint(lHandle, T("_performCleanup"));

	nInputs = 1;
	nOutputs = 0;

	nothing = 0.0;
	mdl_initiate();

	while (! threadShouldExit())
	{
		{
			ScopedLock::ScopedLock (myCriticalSection);
			local_copy_param = MainComponent::getGain(); // ?!? -erm . . 
		};   
		
		mdl_step(nInputs, &local_copy_param,  nOutputs, &nothing);
	}
}

~myThread()
{
	mdl_terminate();
	PlatformUtilities::freeDynamicLibrary(lHandle);
}

}[/code]

. . . but I have absolutely no idea how to strap this all together into a working application. Does my main component need to be a Thread subclass? How do I set the separate threads off and running? So sorry to be asking all these abstract questions. Once I have something simple up and running I’m sure I’ll be able to improve my knowledge and get ideas from the documentation and source code, but until I can get something running it is hard to learn by tweaking.


#7

have a look at the Juce-Thread Demo, and learn C++ (buy a good book :slight_smile:

  • add a Constructor to myThread

something like this
PseudoCode:


MainComponent : public Component
{

public:

	void buttonClicked( Button* button )
	{
		if (button==startThreadButton)
		{
			thread.startThread();
		};
	};	
	
	void SliderChangeCallBack() 
	{
			value=mySlider->getValue();
			{
				ScopedLock(thread.getCriticalSection())
				thread.setParam(value);
			};		
	};

private:

	myThread thread;

}

// add to myThread

class myThread

{

	void CriticalSection& getCriticalSection()
	{
		return myCriticalSection;
	}

}


#8

and don’t call any GUI-Objects from the Thread, cause its not ThreadSafe, always make a local copy in ScopedLock


#9

chkn you are a legend! Thanks for taking the time to respond yet again. I’ll plough onwards.

I had a little look but it doesn’t use ScopedLock. I’ll have a harder little look and try to figure out exactly what it’s doing.

guilty as charged :). I actually own about 4 different recommended C++, but as soon as the examples switch to a payroll or employee class, my eyes glaze over. I always go back to the warm fuzzy comfort of a scripting language because I don’t like going back to square one. Now that I’m working with real-time processing and want to lean JUCE it will force me to finally learn C++ properly.

Thanks again for the info . . . . . .I’ll see what I can do!


#10

Thanks again for the help. I’m learning a lot as I go. I have something that compiles and runs but I’m not totally sure I got the criticalsection and scoped lock stuff correct. This is because I can comment out the scopedlock calls and everything still runs fine. Therefore, I don’t know If I’ve got the right end of the stick here. :expressionless: Anyway, I’m happy that a thread is going, so yippee on that front.

In the GUI bit:

void sliderValueChanged (Slider* slider) { value = slider->getValue(); { ScopedLock(thread->getCriticalSection()); thread->setParam(value); }; }

And myThread:

[code]//stuff

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include “juce.h”

class myThread : public Thread
{
private:
CriticalSection myCriticalSection;
double local_param_copy;

public:
myThread () : Thread (“superThreadLikeSuperTed”)
{
CriticalSection myCriticalSection;
local_param_copy = 1.0; // give it a start val so it’s not all wacky

	//All the DLL loading and initalisation in here
	
}

~myThread ()
{
	//All of the DLL unloading
	
}

CriticalSection& getCriticalSection()
{
	return myCriticalSection;
}

void setParam(double param)
{
	local_param_copy = param;
}



void run()
{
	while (! threadShouldExit())
	{
		DBG(T("Produce this message once per 200 ms and show a random int beteen 0 and 50 ->")+String(Random::getSystemRandom().nextInt(50)));
		DBG(T("local_param_copy = ")+ String(local_param_copy));
		wait(200);
	}
}

};
#endif //MYTHREAD_H[/code]


#11

yes, it will run without the locks (in this case). i mentioned the CriticalSections, because you have to care that you have no undefined states, for example when two threads accessing the same data-structure at the same time, this can happen every time! If you lock one CriticalSection, the other thread can’t get into it.
You can remove “CriticalSection myCriticalSection” in the Constructor, you already defined it in the class (its not a pointer)…


#12

OK. Is there a simple modification that I can make to the code that will result in a nasty error if I don’t use the locks? This is so that I can compare lock-on versus lock-off behaviour.

[quote]You can remove “CriticalSection myCriticalSection” in the Constructor, you already defined it in the class (its not a pointer)…
[/quote]

Ooops, spot the newbie.

Anyway, I’m very happy that with your help so far that I’m able to get some sort of application up and running.It’s good for the motivation!

Thanks again!


#13

first, please try to understand why you may need CriticalSecitions/Mutex/Atomics operators etc. Then you know the answer :wink:

starting points: