SliderListener register but sliderValueChanged event not firing


#1

Hi,

I'm new to JUCE and I've decided to start things off nice and slow with a wolume slider plugin.  I've created an audio plugin project and added a Slider to control the output gain of the device.  My Editor inherits from SliderListener and inside its constructor calls:

slider.addListener(this)

I also have the code to set the gain in the processor but it never gets that far.  I've basically just been taking the parts I need from JuceDemoPlugin (which is working fine).  My gain slider shows up and I can move it fine inside the JUCE Plugin Host.

My problem is that the sliderValueChanged() function is never getting called when I move the slider.

Here is the SliderListener code I've added.

class GainAudioProcessorEditor  : public AudioProcessorEditor, public SliderListener

{

...

void sliderValueChanged (Slider* slider) override;
...
Slider gainSlider;

}


GainAudioProcessorEditor::GainAudioProcessorEditor (GainAudioProcessor& p)

    : AudioProcessorEditor (&p), gainSlider ("gain")

{

    // Make sure that before the constructor has finished, you've set the

    // editor's size to whatever you need it to be.

    setSize (400, 300);

    addAndMakeVisible (gainSlider);

    gainSlider.setSliderStyle (Slider::Rotary);

    gainSlider.addListener (this);

    gainSlider.setRange (0.0, 1.0, 0.01);

    gainSlider.setValue(1.0);

}



void GainAudioProcessorEditor::sliderValueChanged (Slider* slider)

{

    gainStatus = "Slider Changed!";

    if (AudioProcessorParameter* param = getParameterFromSlider (slider))

    {

        // It's vital to use setValueNotifyingHost to change any parameters that are automatable

        // by the host, rather than just modifying them directly, otherwise the host won't know

        // that they've changed.

        param->setValueNotifyingHost ((float) slider->getValue());

    }

}

Is there a step besides adding a listener that I've missed?

I've been debugging by modifying strings inside labels to see if the function is called.  Is there a better way for me to debug using the JUCE Plugin Host? 


#2

Well, it doesn't look like you're actually changing the label's text, so sliderValueChanged() could be getting called. I believe you should use Label::setText() for what you're wanting. It looks like you're just assigning a value to String when the slider value changes. 

Using the debugger in your IDE is the best way to debug. You could just set a breaking in the function to see when it gets called. Also, you could see what the value of that String is. The Plugin Host Demo is great for debugging.

By the way, you should use Slider::Listener instead of SliderListener for new code. That's just there for compatibility.


#3

Sorry should've been more clear.  I update the string in that section and in the GainAudioProcessorEditor::paint() I draw the string to the window to see if it's been updated.  I tried subclassing Slider::Listener instead of SliderListener but it didn't help.

If I set breakpoints in Xcode and then use the plugin in the plugin host they will still fire?


#4

Are you repainting when the slider changes value? Maybe that could make it appear to not be working.

Yeah, you can just open the plugin in the Plugin Host Demo and use a debugger like you normally would. You may have to build the plugin host in debug mode and use that. I can't remember. To make it easier to load a plugin I've created, I have a directory which contains all of my JUCE projects and I add that directory to the list of plugin directories. Then I just have to scan for plugins whenever I create a new one. I usually set up some get plugins for test purposes, so I'll just save that state to make it easy to open up again.

If you want, you could post all of your code and I could try it out.


#5

Thanks a lot!  I'm at work right now and all the code is on my computer at home so I'll post it tonight.  

 

Even knowing that I can set breakpoints in the plugin host in debug mode will help greatly.


#6

/*
  ==============================================================================
    This file was auto-generated by the Introjucer!
    It contains the basic framework code for a JUCE plugin processor.
  ==============================================================================
*/
#ifndef PLUGINPROCESSOR_H_INCLUDED
#define PLUGINPROCESSOR_H_INCLUDED
#include "../JuceLibraryCode/JuceHeader.h"

//==============================================================================
/**
*/
class DistortionAudioProcessor  : public AudioProcessor
{
public:
    //==============================================================================
    DistortionAudioProcessor();
    ~DistortionAudioProcessor();
    //==============================================================================
    void prepareToPlay (double sampleRate, int samplesPerBlock) override;
    void releaseResources() override;
    void processBlock (AudioSampleBuffer&, MidiBuffer&) override;
    //==============================================================================
    AudioProcessorEditor* createEditor() override;
    bool hasEditor() const override;
    //==============================================================================
    const String getName() const override;
    int getNumParameters() override;
    float getParameter (int index) override;
    void setParameter (int index, float newValue) override;
    const String getParameterName (int index) override;
    const String getParameterText (int index) override;
    const String getInputChannelName (int channelIndex) const override;
    const String getOutputChannelName (int channelIndex) const override;
    bool isInputChannelStereoPair (int index) const override;
    bool isOutputChannelStereoPair (int index) const override;
    bool acceptsMidi() const override;
    bool producesMidi() const override;
    bool silenceInProducesSilenceOut() const override;
    double getTailLengthSeconds() const override;
    //==============================================================================
    int getNumPrograms() override;
    int getCurrentProgram() override;
    void setCurrentProgram (int index) override;
    const String getProgramName (int index) override;
    void changeProgramName (int index, const String& newName) override;
    //==============================================================================
    void getStateInformation (MemoryBlock& destData) override;
    void setStateInformation (const void* data, int sizeInBytes) override;
    AudioProcessorParameter* gain;
private:
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DistortionAudioProcessor)
};

#endif  // PLUGINPROCESSOR_H_INCLUDED

 


/*
  ==============================================================================
    This file was auto-generated by the Introjucer!
    It contains the basic framework code for a JUCE plugin processor.
  ==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
class FloatParameter : public AudioProcessorParameter
{
public:
    
    FloatParameter (float defaultParameterValue, const String& paramName)
    : defaultValue (defaultParameterValue),
    value (defaultParameterValue),
    name (paramName)
    {
    }
    
    float getValue() const override
    {
        return value;
    }
    
    void setValue (float newValue) override
    {
        value = newValue;
    }
    
    float getDefaultValue() const override
    {
        return defaultValue;
    }
    
    String getName (int maximumStringLength) const override
    {
        return name;
    }
    
    String getLabel() const override
    {
        return String();
    }
    
    float getValueForText (const String& text) const override
    {
        return text.getFloatValue();
    }
    
private:
    float defaultValue, value;
    String name;
};
//==============================================================================
DistortionAudioProcessor::DistortionAudioProcessor()
{
    addParameter (gain  = new FloatParameter (1.0,  "Gain"));
}
DistortionAudioProcessor::~DistortionAudioProcessor()
{
}
//==============================================================================
const String DistortionAudioProcessor::getName() const
{
    return JucePlugin_Name;
}
int DistortionAudioProcessor::getNumParameters()
{
    return 0;
}
float DistortionAudioProcessor::getParameter (int index)
{
    return 0.0f;
}
void DistortionAudioProcessor::setParameter (int index, float newValue)
{
}
const String DistortionAudioProcessor::getParameterName (int index)
{
    return String();
}
const String DistortionAudioProcessor::getParameterText (int index)
{
    return String();
}
const String DistortionAudioProcessor::getInputChannelName (int channelIndex) const
{
    return String (channelIndex + 1);
}
const String DistortionAudioProcessor::getOutputChannelName (int channelIndex) const
{
    return String (channelIndex + 1);
}
bool DistortionAudioProcessor::isInputChannelStereoPair (int index) const
{
    return true;
}
bool DistortionAudioProcessor::isOutputChannelStereoPair (int index) const
{
    return true;
}
bool DistortionAudioProcessor::acceptsMidi() const
{
   #if JucePlugin_WantsMidiInput
    return true;
   #else
    return false;
   #endif
}
bool DistortionAudioProcessor::producesMidi() const
{
   #if JucePlugin_ProducesMidiOutput
    return true;
   #else
    return false;
   #endif
}
bool DistortionAudioProcessor::silenceInProducesSilenceOut() const
{
    return false;
}
double DistortionAudioProcessor::getTailLengthSeconds() const
{
    return 0.0;
}
int DistortionAudioProcessor::getNumPrograms()
{
    return 1;   // NB: some hosts don't cope very well if you tell them there are 0 programs,
                // so this should be at least 1, even if you're not really implementing programs.
}
int DistortionAudioProcessor::getCurrentProgram()
{
    return 0;
}
void DistortionAudioProcessor::setCurrentProgram (int index)
{
}
const String DistortionAudioProcessor::getProgramName (int index)
{
    return String();
}
void DistortionAudioProcessor::changeProgramName (int index, const String& newName)
{
}
//==============================================================================
void DistortionAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    // Use this method as the place to do any pre-playback
    // initialisation that you need..
}
void DistortionAudioProcessor::releaseResources()
{
    // When playback stops, you can use this as an opportunity to free up any
    // spare memory, etc.
}
void DistortionAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    // In case we have more outputs than inputs, this code clears any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    // I've added this to avoid people getting screaming feedback
    // when they first compile the plugin, but obviously you don't need to
    // this code if your algorithm already fills all the output channels.
    for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
        buffer.clear (i, 0, buffer.getNumSamples());
    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    for (int channel = 0; channel < getNumInputChannels(); ++channel)
    {
        buffer.applyGain(channel, 0, buffer.getNumSamples(), gain->getValue());
        //float* channelData = buffer.getWritePointer(channel);
        //int numSamples = buffer.getNumSamples();
        
        /* Clear all samples */
        //for (int j = 0; j < numSamples; ++j) {
            /* Passthrough */
        //    channelData[j] = channelData[j] * gain->getValue();
        //}
    }
    /*
     boost
     gain = ((boost/100)*100)+1 (Equation 01) 
     
     distort
     x = (1+k)*(x)./(1+k*abs(x)) (Equation 02)
     Where k = 2*a/(1-a) (Equation 03)
     Where a = sin(((drive+1)/101)*(pi/2)) (Equation 04)
     
     tone
     function [filteredwave] = distfftfilter (wave, tone, fs)
     filterfrequency = ((fs-2000)/2)*sin((tone/101)*(pi/2))+1000; % rescaling the tone into filter frequency,
     fNorm = filterfrequency / (fs/2); % Set the norm value
     [b,a] = butter(2, fNorm, 'low'); % Butterworth Low-Pass Filter
     filteredwave = filtfilt(b, a, wave); % Return toned signal
     */
}
//==============================================================================
bool DistortionAudioProcessor::hasEditor() const
{
    return true; // (change this to false if you choose to not supply an editor)
}
AudioProcessorEditor* DistortionAudioProcessor::createEditor()
{
    return new DistortionAudioProcessorEditor (*this);
}
//==============================================================================
void DistortionAudioProcessor::getStateInformation (MemoryBlock& destData)
{
    // You should use this method to store your parameters in the memory block.
    // You could do that either as raw data, or use the XML or ValueTree classes
    // as intermediaries to make it easy to save and load complex data.
}
void DistortionAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
    // You should use this method to restore your parameters from this memory block,
    // whose contents will have been created by the getStateInformation() call.
}
//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new DistortionAudioProcessor();
}

 


/*

This file was auto-generated by the Introjucer!
It contains the basic framework code for a JUCE plugin editor.

==============================================================================
*/
#ifndef PLUGINEDITOR_H_INCLUDED
#define PLUGINEDITOR_H_INCLUDED
#include “…/JuceLibraryCode/JuceHeader.h”
#include “PluginProcessor.h”

//==============================================================================
/**
/
class DistortionAudioProcessorEditor : public AudioProcessorEditor, public SliderListener
{
public:
DistortionAudioProcessorEditor (DistortionAudioProcessor&);
~DistortionAudioProcessorEditor();
//==============================================================================
void paint (Graphics&) override;
void resized() override;
void sliderValueChanged (Slider
slider) override;
void sliderDragStarted (Slider* slider) override;
void sliderDragEnded (Slider* slider) override;
private:
// This reference is provided as a quick way for your editor to
// access the processor object that created it.
Slider gainSlider;
String gainStatus;

DistortionAudioProcessor&amp; getProcessor() const {
    return static_cast&lt;DistortionAudioProcessor&amp;&gt; (processor);
}
AudioProcessorParameter* getParameterFromSlider (const Slider*) const;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DistortionAudioProcessorEditor)

};

#endif // PLUGINEDITOR_H_INCLUDED

 


/*
  ==============================================================================
    This file was auto-generated by the Introjucer!
    It contains the basic framework code for a JUCE plugin editor.
  ==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"

//==============================================================================
DistortionAudioProcessorEditor::DistortionAudioProcessorEditor (DistortionAudioProcessor& p)
    : AudioProcessorEditor (&p), gainSlider ("gain")
{
    // Make sure that before the constructor has finished, you've set the
    // editor's size to whatever you need it to be.
    setSize (400, 300);
    addAndMakeVisible (gainSlider);
    gainSlider.setSliderStyle (Slider::Rotary);
    gainSlider.addListener (this);
    gainSlider.setRange (0.0, 1.0, 0.01);
    gainSlider.setValue(1.0);
}
DistortionAudioProcessorEditor::~DistortionAudioProcessorEditor()
{
}
//==============================================================================
void DistortionAudioProcessorEditor::paint (Graphics& g)
{
    g.fillAll (Colours::white);
    g.setColour (Colours::black);
    g.setFont (15.0f);
    //String s = getProcessor().gain->getName(60) + " " + String(getProcessor().gain->getValue());
    //g.drawFittedText(s, 0, 0, 60, 10, Justification::left, 1, 0);
    g.drawFittedText(gainStatus, 0, 0, 60, 10, Justification::left, 1, 0);
}
void DistortionAudioProcessorEditor::resized()
{
    // This is generally where you'll want to lay out the positions of any
    // subcomponents in your editor..
    gainSlider.setBounds (20, 60, 150, 40);
}
void DistortionAudioProcessorEditor::sliderValueChanged (Slider* slider)
{
    gainStatus = "Slider Changed!";
    if (AudioProcessorParameter* param = getParameterFromSlider (slider))
    {
        // It's vital to use setValueNotifyingHost to change any parameters that are automatable
        // by the host, rather than just modifying them directly, otherwise the host won't know
        // that they've changed.
        param->setValueNotifyingHost ((float) slider->getValue());
    }
}
void DistortionAudioProcessorEditor::sliderDragStarted (Slider* slider)
{
    if (AudioProcessorParameter* param = getParameterFromSlider (slider))
    {
        param->beginChangeGesture();
    }
}
void DistortionAudioProcessorEditor::sliderDragEnded (Slider* slider)
{
    if (AudioProcessorParameter* param = getParameterFromSlider (slider))
    {
        param->endChangeGesture();
    }
}
AudioProcessorParameter* DistortionAudioProcessorEditor::getParameterFromSlider (const Slider* slider) const
{
    if (slider == &gainSlider) {
        return getProcessor().gain;
    }
    return nullptr;
}