Added function to class, compiler says no

So, I’ve extended the Synthesizer class and added a function called ‘setAttack’. This function sets the attack parameter of an ADSR-envelope I’ve added.
But, the parameter change is handled by PluginProcessor.cpp, so I’ve added the following functions below. But, somehow the compiler thinks there is no function called ‘setAttack’ inside the Synthesizer class… What’s going on? I’m using XCode in this case.

Compiler error in JuceAudioPluginDemo.cpp:
No member named ‘setAttack’ in ‘juce::SynthesiserVoice’.

This is my code:

Synthesizer.h

public:
...    
void setAttack(float attack)
{
    mADSR.stageValue[EnvelopeGenerator::ENVELOPE_STAGE_ATTACK] = attack;
}

JucePluginAudioDemo.cpp

void JuceDemoPluginAudioProcessor::setAttack(float attack)
{
    for (int i = 0; i < mNumVoices; i++)
    {
        static_cast<SineWaveSound*>(synth.getVoice(i)->setAttack(attack));
    }
}

SynthesiserVoice has NO setAttack call as it also seems you’ve already understood with you casting.
However you’re calling setAttack prior to casting it…

Right, but how would one to add such functionality without modifying the juce library files? Or is there no way around it and just make a local copy and edit the juce files?

cpp has learning curve if you’re not coming from it and it’s only getting worse :wink: since c++17 and future standards reshape the syntax…

You’re not providing enough code snippets to actually understand what you’re trying to do.
And as with code there are many ways to do the same thing. each one would do it different.

  1. you can always keep your own voice pointers in a vector / juce::Array / etc…

  2. you can change your code to (I didn’t test it since I’m also not a fluent cpp dev. but if this not working simply set it to a pointer variable first).

    (dynamic_cast<SineWaveSound*>(synth.getVoice(i))->setAttack(attack);
    
  3. set all “voices” to use the same attack float* so you’ll have a single object in memory instead of setting it up each time.

  4. use juce::Value and juce::Value::referTo in order to sync multiple objects with one main object which might be better for some situations.

Thanks for you reply!

I will look into the various options you mentioned.

And yes, I’m very new to c++, coming from c#/java. So some things confuse the hell out of me. But, taking babysteps and looking at references helps a lot. And also the help from the responsive Juce community :wink:

the proper c++ syntax for casting is:

if( auto thing = dynamic_cast<Thing*>(someOtherThing) )
{
    thing->doSomething();
}
1 Like

I just wonder if this won’t have a performance hit?

It’s validating the instanceof so I guess it’s costly. And if I’m 100% sure the object is the type I’m going to cast why should we test it?

Then use static_cast

Rail

Using dynamic_cast works. I realized I was using the wrong syntax all along:

return dynamic_cast<SineWaveVoice*>(synth.getVoice(i)->setReleaseAmp(release));
instead of
return dynamic_cast<SineWaveVoice*>(synth.getVoice(i))->setReleaseAmp(release);

Lol feel free to ask the folks in ##c++ who help write the STL about it if
you don’t believe me.

Or just in case
http://www.boost.org/doc/libs/1_58_0/libs/conversion/cast.htm

so your debug version ensure you are not mistaken about the 100% sure :slight_smile:

To avoid the performance penalty of testing and still be on the safe side you might try this:

jassert( dynamic_cast<Thing*>(someOtherThing) );
static_cast<Thing*>(someOtherThing)->doSomething();

(Of course you need to test all possible use cases with the debug build.)

1 Like

fwiw, from the juce style guide:

..instead, always prefer to write it like this, which reduces the scope of the pointer, making it impossible to write code that accidentally uses a null pointer:

if(Foo* f = getFoo())
    f->doSomethingElse();
 
// f is out-of-scope here, so a null-pointer dereference is impossible.
​(This also results in more compact, cleaner code).
3 Likes

Thanks for pointing me to the style guide. It is always a good read.
With respect to the current problem I don’t think that the rule you cited should be applied here. After all a named pointer variable isn’t even required. The chances of unadvertedly using a dangling pointer are thus zero.
Note that the Idiom that I proposed is also present in the juce source code.
(like here: AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees)

sure. the method I proposed is also all over the place in Juce code. The folks who write/wrote juce definitely didn’t stick to their style guide 100% haha

for example: