ChangeBroadcaster can queue at most one message. Why?


#1

ChangeBroadcaster::sendChangeMessage can only queue one message at a time. It calls ChangeListenerList::sendChangeMessage which is reproduced below:

void ChangeListenerList::sendChangeMessage (void* const objectThatHasChanged) throw()
{
    const ScopedLock sl (lock);

    if ((! messagePending) && (listeners.size() > 0))
    {
        lastChangedObject = objectThatHasChanged;
        postMessage (new Message (0, 0, 0, objectThatHasChanged));
        messagePending = true;
    }
}

At most one message can be posted until messagePending is set to false which occurs when the main message loop drains the queue by calling ChangeListenerList::sendSynchronousChangeMessage.

My question is what was the intent for limiting to queue to a single message? The rest of the post is background detailing the problem whose solution is influenced by the answer.

In some mix automation code I recently wrote I use a ChangeBroadcaster to send mixer commands to the GUI which moves the sliders which in turn shape the mix. This works fine when mixing in real time, but some messages are dropped when rendering the mix to a file. I use the same code in both to send and process the mix commands. In the real time case, the mixing code is called by the audio callback, in the file rendering case it is called by an instance of a class derived from ThreadWithProgressWindow.

What seems to be happening is that the main message thread consuming the queue is running too slowly to drain the queue before the file rendering thread attempts to post another message. Since the queue is not drained, messagePending is true and the message is dropped. I’ve found evidence of dropped messages in the debugger and even theoretically it makes sense: one minute of real time audio is rendered to file in less than 3 seconds.

To fix it, I see 3 alternatives: (1) I could slow down the file rendering loop, so that it doesn’t drop messages, (2) I could write seperate code to render the mix that doesn’t rely on messaging to change the mix controls, but instead codes it directly, or (3) change the ChangeListenerList code to allow multiple messages. What to pick?

Fix (1) is unsatisfactory, because making it work well across a variety of machines will make it unbearably slow. File rendering should work faster than real time, shouldn’t it? Fix (2) would give the most accurate solution as commands could be applied immediately instead of being queued, but why have two pieces of code that do the same thing? That leaves fix (3) which to seems to be the best choice. But I always hesitate before modifying external libraries and the messagePending guard seems to be intentional. It would be nice know why and make an informed decision before cutting open the body so to speak.

One more item: the first mixer automation solution I tried used a variation of 2 that relied on the ChangeBroadcaster only for the visuals. The audio processing of the mixer commands was done entirely during the getNextAudioBlock of my mixer class. The mixer in turn was the source for an AudioTransportSource instance which had a read ahead buffer of 2048. The first track (source) and master worked fine, but subsequent tracks were noisy when the volume or pan changed. Manual mixing worked fine–no glitches. To add to further confusion, the noise disappeared entirely when I turned off the read ahead buffer in the AudioTransportSource. Another piece of the puzzle: I stored past history of the mixer commands to smooth the transitions with a linear interpolation between previous and past values; without this, even manual mixing is noisy. I was never able to figure out why the glitches occurred. My best guess is that the read ahead thread is setting the previous values when it leaves the mixer’s getNextAudioBlock which are then used as previous values applied during the audio callback use of the method call. I came up with my present solution instead of working a way around this problem.


#2

The answer to that is given in the documentation for ChangeListener. http://www.rawmaterialsoftware.com/juce/api/classChangeListener.html

ChangeListeners are best suited when you only need to know that something has changed, which is not the same as being notified for every change.

ActionListeners don’t coalesce, but they’re also not synchronous. To me it sounds like you just need to be using an event buffer of some kind, and the change message just to alert interested parties that one or more items may be in the buffer.


#3

Hi Jules,

With changeListenerCallback we could send an object as a message,
but with actionListenerCallback we have to send string.

How could we tackle a situation in which we have to have multiple
messages sent as objects.

Thanks in advance.


#4

The changelistener and actionlistener classes are provided for common situations where you need basic messaging, they’re not the only way of doing things.

If you’ve got really specific or complex things that you need to send, just create a subclass of Message and you can post anything you want.


#5

Thanks Jules, I tried that and its working ok!


#6