How 2 queue a GUI setValue if the audioProcess is rendering?


#1

Hi, I just built a standard synth with a GUI and noticed that while moving asynchronous sliders, changes are made while the audio thread is straight in the “process” method.

To summarize :

  1. There is (at least) 2 threads, one responsible for the GUI (catching clicks, sliders rolls, menu changes etc…) and the second for the audio processing (rendering all notes, layers etc…)
  2. Both should be running as independently as possible in the sense that Thread A should never modify a value being currently used by Thread B.
  3. To solve this problem, as described here http://www.rawmaterialsoftware.com/viewtopic.php?f=8&t=6959&p=39008&hilit=asynchronous#p39008 I could either surround variables with mutex or put the changes messages of Thread A in a queue which one will synchronously be dequeued at the right time in Thread B

I thought that the GUI events were already queued and probably executed right before the audio process to start (and hopefully within this thread) but it seems not.
I know that I’m not the first in this case juce being made quite well to handle such problems: my question is what is the proper way (if there is one) to activate or to force executing the queue message at the proper time ?


#2

Okay… tell me if I’m wrong but after digging, it seems that the present async support is only used for postponing the redraw process in the gui thread one.
If so, I’m going to implement the event message queue for syncing messages between those process.


#3

Have a look at the SimpleDJ application in my signature for a working example.


#4

ok it seems that you made your own synchronized listener, very smart! I will try to implement something similar tomorrow.


#5

Considering the length of time I spent making it work, and work fast, you might be better off just using the implementation in VFLib. It is MIT licensed and there’s documentation:


#6

I’m just wondering now what happens if we do have multiple instances within the same host ? I saw your message queue is using a singleton but in that case would not the static be shared across all the loaded plugins ? It should work but just I feel this a bit risky in some case


#7

I just tried to add your lib:

  1. Added the folder to my project without including the files
  2. Added the header search path to the root folder “VFLib”
  3. I then added a single “vf_core.cpp” in order to try compiling my project, I got an error (see attached file)
  4. I completed the AppConfig.h with the mandatory /VFLib/AppConfigTemplate/AppConfig.h (even if not using any of those libs)
    -> It seems that vf_bind.h is trying to use the std::ref (and 5 other variables) which are not present (/usr/include/c++/4.2.1/cstdlib on OS X )
    [EDIT]

OK it seems that my <functional> does hot have [std::tr1::ref; std::tr1::bind; std::tr1::function; std::tr1::placeholders::_1; std::tr1::placeholders::_2;]

As <tr1/functional> does, I went to vf_core.h and changed in line 285

[code]#elif JUCE_MAC

include <tr1/functional>

#else
[/code]

Then enabled the tr1 for JUCE_MAC : in vf_Bind.h

#elif JUCE_MAC using std::tr1::ref; using std::tr1::bind; using std::tr1::function; using std::tr1::placeholders::_1; using std::tr1::placeholders::_2; #else


#8

Im typically using a sync queue based upon juces AbstractFifo class. Then the audio thread can push values and the main thread can pop them. Vinn, whats the advantage of your method over this?

Goes something like this:


// Threadsafe Queue for single producer, single consumer 
template <class ITEMTYPE, int NUMOFITEMS>
class SyncQueue
	{
	public:
		SyncQueue()  : abstractFifo (NUMOFITEMS)
		{
		}

		int Push(const ITEMTYPE* src, int numItems)
		{
			int start1, size1, start2, size2;			
			abstractFifo.prepareToWrite (numItems, start1, size1, start2, size2);

			for (int i=0; i<size1; ++i)
				myBuffer[start1+i] = src[i];
			
			for (int i=0; i<size2; ++i)
				myBuffer[start2+i] = src[size1+i];

			abstractFifo.finishedWrite (size1 + size2);
			return size1 + size2;
		}

		bool Push(const ITEMTYPE& e)
		{
			if (Push(&e, 1) == 1)
				return true;
			else
			{
				ITEMTYPE dummy;
				if (Pop(dummy))
					return Push(&e, 1) == 1;

				return false;
			}
		}

		int Pop(ITEMTYPE* dest, int numItems)
		{
			int start1, size1, start2, size2;			
			abstractFifo.prepareToRead (numItems, start1, size1, start2, size2);

			for (int i=0; i<size1; ++i)
				dest[i] = myBuffer[start1+i];

			for (int i=0; i<size2; ++i)
				dest[i+size1] = myBuffer[start2+i];

			abstractFifo.finishedRead (size1 + size2);
			return size1 + size2;
		}

		bool Pop(ITEMTYPE& dest)
		{
			return Pop(&dest, 1) == 1;
		}

		int Available() const
		{
			return abstractFifo.getNumReady();
		}

		int SpaceLeft() const
		{
			return abstractFifo.getFreeSpace();
		}

		bool Empty() const
		{
			return Available() == 0;
		}

		bool Full() const
		{
			return abstractFifo.getFreeSpace() == 0;
		}

		void Clear()
		{
			abstractFifo.reset();
		}

	private:
		AbstractFifo abstractFifo;
		ITEMTYPE myBuffer [NUMOFITEMS];
	};

#9

Vinn’s solution can queue any kind of method call to call them back when we want. Typically we would like all the GUI messages such as a knob changed to be executed right before the audio process to be done. But NEVER while the audio process is done.

The advantage of that queue is that you can put anything you want (i.e. any method call) inside regardless to which variable is currently accessed by the audio process (even if the method does change them).
Then only, we take care to dequeue it at the proper time (which as I said above is right before rendering the audio). In that manner you will never block the GUI if the audio is currently rendering neither get any unexpected bugs, typically a thread A reading a value which just passed to (let say) “NULL” because thread B is putting some new stuff inside at the same time.

  • Clean code : you queue methods and you do not need to overload the objects to do so
  • Faster : no mutex on variables, only on the queue

#10

Thanks for the reply. Im still curious of Yours/Vinns approach and will take a look at it.
I just dont feel pursuaded about it, though and im not sure i agree with the mentioned benefits:

For using a simple knob value in the audio thread i would typically just assign the (volatile) float of the knob to a local variable in the first line of my audio-thread processing function. For more complex data structures I use the SyncQueue above. In both cases no locks or mutexes whatsoever and no callback/observer inversion of control.


#11

[quote=“OBO”]Thanks for the reply. Im still curious of Yours/Vinns approach and will take a look at it.
I just dont feel pursuaded about it, though and im not sure i agree with the mentioned benefits:[/quote]

The SyncQueue is the same principle as the code in my library.


#12

Well… moving a knob in my case will possibly move other knobs with recursive modifiers, moreover I sometimes have to compute arrays of values based on a button value. If they do change during the processing it’s crash guaranteed. The worst becomes to loading preset while the synth is running, some mb of stuff is being loaded and this does not takes 1/88 a sec do achieve this :wink: therefore I load them aside and concurrently replace the current one with the new one once ready.