Value(var(Array<val>()))


#1

Hi all,

I struggle with the usage of a Value containing a var(Array).

I’m working on a plugin to measure loudness, according to EBU R128 (http://www.klangfreund.com/lufsmeter, https://github.com/klangfreund/LUFSMeter).

The coarse mechanism

  1. The LUFSMeterAudioProcessor contains the ebu128LoudnessMeter object, which processes the audio blocks.

  2. The LUFSMeterAudioProcessorEditor is a Timer and in its timerCallback method, it polls the (so called momentary) loudness from the ebu128LoudnessMeter. This number is put into a Value object ( momentaryLoudnessValue.setValue (double (momentaryLoudness)) ).

  3. An instance of my GUI class LoudnessBar listens to this momentaryLoudnessValue. I.e. LoudnessBar contains a Value itself which referes to momentaryLoudnessValue and LoudnessBar is also a Value::Listener which listens to its own Value. When this Value changes, LoudnessBar::valueChanged() is called. There, if the current loudness differs from the previous loudness, repaint() is called.

I’m using the Value object because it makes it easy to “attach” different GUI elements to the same value, e.g. a bar as well as a number box. Anyway, if you have doubts about this design please let me know, I’m curious about different design patterns.

Multi channel

I tried to extend the plugin such that it determines the loudness of an arbitrary number of input channels on each channel. The LoudnessBar will plot as many bars as there are input channels.
I think a Value containing a var(Array) is the perfect match for this kind of task. But I just can’t figure out how to use it.

First approach:

LUFSMeterAudioProcessorEditor::LUFSMeterAudioProcessorEditor (LUFSMeterAudioProcessor* ownerFilter)
  : momentaryLoudnessValue (var(Array<var>())) // A copy of the argument is stored inside. I'm quite sure this is not needed at all.
...

LUFSMeterAudioProcessorEditor::timerCallback()
{
...
    // source:
    const Array<float>& momentaryLoudnessFromEbu128LM = getProcessor()->getMomentaryLoudness();
    // destination:
    Array<var>* momentaryLoudnessValuesArray = momentaryLoudnessValues.getValue().getArray();
    
    // A temporary hack to see if this approach might work:
    // Ensure that the size of momentaryLoudnessValuesArray is equal to the number of input channels.
    if (momentaryLoudnessValuesArray->size() != momentaryLoudnessFromEbu128LM.size())
    {
        // The number of input channels has changed -> Resize the Array of momentaryLoudnessValues
        momentaryLoudnessValuesArray->clear();
        momentaryLoudnessValuesArray->insertMultiple (0, var( double (-23.0)), momentaryLoudnessFromEbu128LM.size());
    }
    else
    {
        // WILL NEVER GET INTO THIS SECTION
        for (int k = 0; k != momentaryLoudnessFromEbu128LM.size() ; ++k)
        {
            double momentaryLoudnessOfTheKthChannel = double (momentaryLoudnessFromEbu128LM[k]);
            momentaryLoudnessValuesArray->set(k, var (momentaryLoudnessOfTheKthChannel));
        }
    }

...
}

It looks like changes to momentaryLoudnessValues.getValue().getArray() are simply not stored.

Second approach:

[code]LUFSMeterAudioProcessorEditor::timerCallback()
{

// source:
const Array& momentaryLoudnessFromEbu128LM = getProcessor()->getMomentaryLoudness();
// destination:
Array loudnessArray;

for (int k = 0; k != momentaryLoudnessFromEbu128LM.size() ; ++k)
{
    double momentaryLoudnessOfTheKthChannel = double (momentaryLoudnessFromEbu128LM[k]);
    loudnessArray.add (momentaryLoudnessOfTheKthChannel);
}
momentaryLoudnessValues.setValue (var (loudnessArray));
    // The original array in momentaryLoudnessValues is replaced by a copy of var (loudnessArray).


}

void LoudnessBar::paint (Graphics& g)
{

Array* loudnessArray = loudnessValue.getValue().getArray();

if (loudnessArray)
{
    const int numberOfChannels = loudnessArray->size();
    ...
    for (int k = 0; k != numberOfChannels; ++k)
    {
        double currentLoudnessOfChannelK = double ((*loudnessArray)[k]);
            // -> EXC_BAD_ACCESS
            // even thought k = 0 and loudnessArray->size() = 2
        ...
    }
}
...

}[/code]
On the top of the stack when this EXC_BAD_ACCESS happens:

var::var (const var& valueToCopy) : type (valueToCopy.type) { type->createCopy (value, valueToCopy.value); }
with the adress of valueToCopy = 0x7000000000000000.

Has anyone any experience with multiple values in a single Value object? Or any hints?

My system: Tip of Juce, OSX 10.7, XCode 4.2.1


#2

I got some time to investigate further…

The above is not working because e.g. momentaryLoudnessValues.getValue() makes a copy of the internally used var and changing such a copy doesn’t change anything inside the Value.

You can only alter a Value instance by using setValue or the operator=.
And this always results in releasing and assigning new memory.
Why is it implemented this way? Isn’t memory reallocation much more intense to compute than finding and changing a value at a given address?

Would you recommend the usage of a Value object for the communication between a measurement-algorithm and a visualizing (multichannel) vu display component? Or would you just poll via a timer?