How to set the value of an AudioParameterChoice from the editor

Hello there.
I’m trying to develop an app which permits to the user to instantiate several audio tracks, in order to generate a mixer by sampling audio files placed inside a folder.

I’m not sure how to set an AudioParameterChoice for handling the reproduction state of an audio file (i.e.: put an audio file to pause if it is playing, stop it and so on).

This is my AudioChannel, based on the AudioProcessor class

class AudioChannel : public AudioProcessor
{
public:
    AudioChannel(juce::File audioPath)
    {
        addParameter(reproductionState = new juce::AudioParameterChoice("state", "State", states, 0));
    //  ... ...  //
    }

    void prepareToPlay(double sampleRate, int samplesPerBlock) override
    {
        reproductionState->setValueNotifyingHost(float(5));

        transportPtr->prepareToPlay(samplesPerBlock, sampleRate);
    }

    void processBlock(juce::AudioSampleBuffer& buffer, juce::MidiBuffer&) override
    {
        auto totalNumInputChannels = getTotalNumInputChannels();
        auto totalNumOutputChannels = getTotalNumOutputChannels();

        for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
            buffer.clear(i, 0, buffer.getNumSamples());

        reproductionStateChanged();

        juce::AudioSourceChannelInfo bufferToFill(buffer);
        bufferToFill.clearActiveBufferRegion();

        transportPtr->getNextAudioBlock(bufferToFill);
        float gain = juce::Decibels::decibelsToGain(volume->get());
        transportPtr->setGain(gain);

    }

    void reproductionStateChanged()
    {
        auto newState = reproductionState->getCurrentValueAsText();
        if (newState != actualState)
        {
            actualState = newState;
        }
    }

    juce::StringArray states{ "Stopped", "Starting", "Stopping", "Pausing", "Paused", "Playing" };
    juce::AudioParameterChoice* reproductionState;
    juce::String actualState = "Awakening";
    juce::AudioParameterFloat* volume;
}

And this is the function in the PluginEditor, which is in charge of changing the reproduction state of an audio file:

void PluginEditor::playButtonClicked(juce::AudioProcessorGraph::Node::Ptr node,
    juce::TextButton* button)
{
    auto params = node->getProcessor()->getParameters();
    params[1]->beginChangeGesture();
    params[1]->setValueNotifyingHost(float(5));
    params[1]->endChangeGesture();

    button->setEnabled(false);
    
}

However, the code doesn’t work, since setValueNotifyingHost only accepts values between 0.0 and 1.0. Is there any chance to change the value of the AudioProcessorChoice directly from the editor? Which function should be used for the scope?

Thanks in advance for any answer

Sorry, I had to re-write the post due to some typos. Hope that everything is clear now.

Notice that: the AudioProcessor are added to an AudioProcessorGraph, then each single node is recalled (check the code above) in order to retrieve the parameters for modifying characteristics such as the volume of an audio track

Divide the index by the number of choices you have in the parameter. In general, you can get the Range associated with the parameter, and use that to convert to 0…1.

1 Like

So you’re suggesting to take index of the parameter and divide it by the number of choices?

In my case I’d have 1 as the index (since 0 is reserved for setting the volume), and 6 (with a range from 0 to 5) as the number of choices. But I don’t get the next step: I mean, in my code I would call setValueNotifyingHost, but how do I match the value that I will get with this function with the number of choice describe by the AudioParameterChoice?

Right now, if I call setValueNotifyingHost with a value major than 1, the AudioParameterChoice is not affected and remains the same.

I made a minor mistake. You’d divide by the number of choices - 1, because 5 is the highest choice number, and that goes with the value 1 in the “normalized” value range of 0.0-1.0.

So you’d pass, for example, 5/5.0.

But better (in general) would be to get the NormalizedRange associated with that parameter, and call its convertTo0To1() function, passing that the choice number you want, and passing its result to setValueNotifyingHost(). That way if the range ever changes, you don’t have to hunt down the code that divides by the original range (minus 1).

1 Like

same as for all other paramer types. you just spawn a juce::ParameterAttachment and use it to update the parameter value. if you need conversions between normalized and denormalized values also spawn the parameter’s juce::RangedAudioParameter& from your apvts (or other parameter handling class of choice), cause it has these conversion functions and you don’t have to think about the mapping of values so much anymore

1 Like

So, I must use AudioParameterValueTreeState, right? Consider one important thing: since I’m developing a mixer which spawns each time a node to be assigned to an AudioProcessorGraph, I have to retrieve the node, then call getProcessore() for accessing the parameters of that specific AudioProcessor.

I’m asking it since I did everything to avoid the usage of AudioParameterValueTreeState, but probably the answer is: I MUST use it, and that’s it :grin:

Ok, so you’re suggestion implies to pass from an AudioParameterChoice to an AudioParameterFloat with a normalized range, right? I will experiment this, thanks for the advice :slightly_smiling_face:

i just assumed you use apvts because most people do but technically you only need any sublass of AudioProcessorParameter

1 Like

Yeah, the problem is that I’m working with an AudioProcessorGraph, so each time I have to instantiate objects and retrieve each single AudioProcessor.

However, maybe I found out a workaround: if it will work, I will post my solution right here

Sorry, just read this now:
You don’t have to use AudioProcessorValueTreeState. There is an ParameterAttachment base class. Instead of AudioProcessorValueTreeState::ComboBoxAttachment you can use ComboBoxParameterAttachment.

But to your actual question:
Ideally you convert the AudioProcessorParameter to the actual type using dynamic_cast:

// member variable:
juce::AudioParameterChoice* choice = nullptr;

// in constructor of your control
choice = dynamic_cast<juce::AudioParameterChoice*>(processor.getParameter ("paramID"));
jassert (choice);  // If you get an error, the parameter doesn't exist or is of different type

// when you want to set it
*choice = 5; // uses the operator=()

By using the correct type it uses the operator=() to do the right thing, convert everything consistently etc.

Hope that helps

2 Likes

Thank you for your answer, @daniel !

One question: I don’t understand where I have to declare the variable choice. You said in the constructor, but how can I refer to the processor that I’m instantiating?