[JUCE API] Stereo Link

SangHoon You·2025년 4월 17일

JUCE API

목록 보기
9/17

class MyParameters  : private juce::AudioProcessorValueTreeState::Listener
{
private:
	juce::AudioProcessorValueTreeState& mApvts;
    juce::AudioParameterBool* mParamLink;
    void parameterChanged (const juce::String& parameterID, float newValue) override;
}

Firts of all, add the listener to the class which has the apvts.


void MyParameters::parameterChanged(const juce::String& inParamId, float newValue)
{
    if(parameterID == MyParamId::Control::Tempo.getParamID())
    {
        if(newValue==1.0f)
        {
            DBG("Stereo Link ON");
        }
        else
        {
            DBG("Stereo Link OFF");
        }
    }
}

We have to define the apvts::listener's pure virtual funtion. inParamId means the parameter which changed and newvalue means true(1) and false(0).



if(inParamId == IdLink)
    {
        if(newValue==1.0f)
        {
            DBG("Stereo Link ON");
            mApvts.addParameterListener(IdTimeL, this);
            mApvts.addParameterListener(IdTimeR, this);
            mApvts.addParameterListener(IdNoteL, this);
            mApvts.addParameterListener(IdNoteR, this);
        }
        else
        {
            mApvts.removeParameterListener(IdTimeL, this);
            mApvts.removeParameterListener(IdTimeR, this);
            mApvts.removeParameterListener(IdNoteL, this);
            mApvts.removeParameterListener(IdNoteR, this);
            return;
            DBG("Stereo Link OFF");
        }
    }

If the link button has changed, delay time parameters are added to the listener. Because if L(master) changed, then R(slave) should fllowed.

But, parameterChanged() is not operated in AudioProcessor thread, actually in GUI thread, we need Time class.



class MyParameters  : private juce::AudioProcessorValueTreeState::Listener,
                      private juce::Timer
{
	private:
        void timerCallback() override;
        std::atomic<int> mChannelmaster;
        std::atomic<bool> mFlagLink;
}

mChannelmaster and mFlagLink are factors that Time class's pure virtual funtion needs. But those needs at parameterChanged() too, but there are in different thread, we need atomic type.



parameterChanged() 
{
    if(mFlagLink.load()==true)
      {
          return;
      }
    
    const int masterCurrent = mChannelmaster.load();
    const int masterNew = ( inParamId == IdTimeR || inParamId == IdNoteR ) ? 1 : 0 ;
    if(masterCurrent != masterNew)
    {
        mChannelmaster.store(masterNew);
    }
}

timerCallback()
{
    const int master = mChannelmaster.load();
    mFlagLink.store(true);
    for(int i=0;i<2;++i)
    {
        if( i != master)
        {
            *(mParamTime[i]) = mParamTime[master]->get();
            *(mParamNote[i]) = mParamNote[master]->getIndex();
        }
    }
    mFlagLink.store(false);
}
  • To decide the master channel, except the case that right channel changed, assigned to 0. Including the case that only link button is used.
  • Master channel is standard for adjustment, so the other channel folloed by master value.

What is the mFlagLink ?

In timecallback() function, modifying channels other than the master triggers the parameterChanged() function again. As a result, the master channel allocated between 0 and 1 in a kind of recursive loop. The variable in question is set up to prevent this behavior.

profile
Audio Plugin Developer

0개의 댓글