Getting a variable to update from my slider when it changes (ie. onValueChange)?


#1

I see in the SineSynth demo, the method used to update a variable “targetFrequency” from the frequency slider is very simple:

MainContentComponent()
{
    addAndMakeVisible (frequencySlider);
    frequencySlider.setRange (50.0, 5000.0);
    frequencySlider.setSkewFactorFromMidPoint (500.0);
    frequencySlider.setValue (currentFrequency, dontSendNotification);  // [6]
    frequencySlider.onValueChange = [this] { targetFrequency = frequencySlider.getValue(); };

My application is a bit more complicated because my sliders are being created automatically from a LabeledSlider (GroupComponent) class. So the syntax has to be different. I can get everything to work except the variable updating.

Here’s what I have as a similar example:

void MainComponent::createOscGroup()
{
	Array<Component*> oscGroupSliders;

	LabeledSlider* oscFrequencySlider = new LabeledSlider("Frequency");
	oscGroupSliders.add(oscFrequencySlider);
	
	oscFrequencySlider->setValue(oscFrequencyValue);
	oscFrequencySlider->setDoubleClickReturnValue(true, oscFrequencyDefaultValue);
	oscFrequencySlider->setRange(20.0, 20000.0, 1);
	oscFrequencySlider->setNumDecimalPlacesToDisplay(0);
	oscFrequencySlider->setSkewFactorFromMidPoint(500);
	oscFrequencySlider->onValueChange = [this, oscFrequencySlider] { oscFrequencyValue = oscFrequencySlider->getValue(); };

Functions are defined this way in my LabeledSlider class cpp:

LabeledSlider::LabeledSlider(const String& name)
{
	setLookAndFeel(&labeledComponentLookAndFeel); //Sets lookand feel for labeledslider (ie. labeledcomponent)

    Slider* slider = new Slider();
    slider->setTextBoxStyle(Slider::TextEntryBoxPosition::TextBoxBelow, false, textBoxWidth, textBoxHeight);
    slider->setSliderStyle(Slider::SliderStyle::RotaryHorizontalVerticalDrag);
	slider->setNumDecimalPlacesToDisplay(1);
	slider->setLookAndFeel(&sliderLookAndFeel); //Sets LookAndFeel for sliders

    mSlider = std::unique_ptr<Slider>(slider);
    addAndMakeVisible(slider);
    
    setText(name);
    setTextLabelPosition(Justification::centredTop);
    setSize(componentWidth,componentHeight);


}


void LabeledSlider::resized()
{
    Slider* slider = mSlider.get();
    slider->setBounds(getLocalBounds().reduced(componentMargin));
}

void LabeledSlider::setRange(double min, double max, double newInterval = 0)
{
	Slider* slider = mSlider.get();
	slider->setRange(min, max, newInterval);
}

void LabeledSlider::setNumDecimalPlacesToDisplay(int decimals)
{
	Slider* slider = mSlider.get();
	slider->setNumDecimalPlacesToDisplay(decimals);
}

void LabeledSlider::setSkewFactorFromMidPoint(double skewMidPoint)
{
	Slider* slider = mSlider.get();
	slider->setSkewFactorFromMidPoint(skewMidPoint);
}

void LabeledSlider::setValue(double newValue)
{
	Slider* slider = mSlider.get();
	slider->setValue(newValue);
}

double LabeledSlider::getValue() const
{
	Slider* slider = mSlider.get();
	slider->getValue();
	return getValue();
}

void LabeledSlider::onValueChange()
{
	Slider* slider = mSlider.get();
	slider->onValueChange();
}

void LabeledSlider::setDoubleClickReturnValue(bool doubleClickEnable, double doubleClickValue)
{
	Slider* slider = mSlider.get();
	slider->setDoubleClickReturnValue(doubleClickEnable, doubleClickValue);
}

Everything I can test of those functions is working fine except I can’t get the onValueChange to work and I don’t know if the getValue is correct.

Any correction on how I should have written these to make them work? Thanks.

I’m just looking to get some basic connection between GUI and audio going. I’ve got a sine wave and I want to control it’s frequency with a knob like the tutorial to start. Nothing too fancy.


#2

not a solution, but do this:

mSlider.get()->whateverYouWantToCall()

#3

I’m still bad at lambdas, but from looking at your code I think the following is going on.

In your MainComponent Constructor you are calling the function LabeledSlider::onValueChange() on your LabeledSlider instance oscFrequencySlider. But the Slider::onValueChange()is an std::function.

What I would do is give you MainComponent the ability to get access to the Slider in your LabeledSlider class. Something like

oscFrequencySlider->getSlider()->onValueChange() = ...

and a function like

Slider* LabeledSlider::getSlider()
{
    return mSlider.get();
}

#4

and yes, as @matkatmusic said, you can replace all your

Slider* slider = mSlider.get();
slider->...;

with mSlider.get()->...
:wink:


#5

do this instead:

mSlider = std::make_unique<Slider>();
mSlider->setTextBoxStyle(Slider::TextEntryBoxPosition::TextBoxBelow, false, textBoxWidth, textBoxHeight);
mSlider->setSliderStyle(Slider::SliderStyle::RotaryHorizontalVerticalDrag);
mSlider->setNumDecimalPlacesToDisplay(1);
mSlider->setLookAndFeel(&sliderLookAndFeel); //Sets LookAndF

#6

Here’s an option:

change your LabeledSlider constructor to accept a lambda in the constructor and assign it to your slider’s callback like this:

LabeledSlider::LabeledSlider(const String& name, std::function<void()> valueChange)
{
   mSlider = std::make_unique<Slider>();
   mSlider.get()->onValueChange = valueChange;
}

then when you construct your LabeledSlider, pass in a lambda along with the name of your label.

LabeledSlider slider("slider", 
                    []() 
{ 
    DBG( "slider value changed"); 
    //put anything else you want to have happen 
    //when the slider's value changes here.
} 
                    );

#7

Nonono… please don’t ever write code like that!

Not only is it shorter and clearer to just write

mSlider->whateverYouWantToCall()

but by using the operator-> it gives the smart pointer class an opportunity to use an assertion to tell you if you attempt to call a method on a nullptr. Adding the spurious get() call is not just pointless, it’s also bad practice!


#8

Cppref says the behavior is undefined actually if .get() == nullptr.

Also:

Exceptions
1) may throw, e.g. if pointer defines a throwing operator

But there’s nothing in there about an assert in the cppref docs @jules. Are we talking about the same smart pointer?

Do you mean this?

00163       pointer
00164       operator->() const
00165       {
00166     _GLIBCXX_DEBUG_ASSERT(get() != 0);
00167     return get();
00168       }

https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.4/a01404.html

Or this?

_LIBCPP_INLINE_VISIBILITY _Tp* operator->() const throw() {return __ptr_;}

#9

Yes, I know it’s undefined behaviour and all I said was that it gives libraries the opportunity to assert, not that it’s required by the standard.

The relevant point here for beginners is just that writing over-verbose stuff like myPointer.get()->doSomething() makes code less readable, harder to refactor and (in some but not all std libraries) stops the libraries helping you catch mistakes.


#10

I just wanted to know where that assertion was, that’s all. Maybe it was in the ScopedPointer class? it’s a really useful idea, though. I’m surprised the std::unique_ptr implementation doesn’t have it.