Custom Synthesiser Voice Constructor breaks synth

I have a a class derived from SynthesiserVoice called SynthVoice. It works normally but now I’d like to add a AudioProcessorValueTreeState::Listener to it so it can respond to parameter changes.
So I inherit the Listener class in my SynthVoice and I need to call the vts.addParameterListener(ID, this) function in the SynthVoice() constructor.
Now as soon as I write a constructor in the SynthVoice class it stops outputting sound.
I tried removing all the arguments and created an empty constructor:
SynthVoice(){}
And it still stops outputting sound.
I also tried calling the superclass constructor(SynthesizerVoice) by doing this:
SynthVoice():SynthesiserVoice(){};
and it still breaks the voice.

Is there a reason why adding an empty constructor changes the behaviour of the voice? and why? Also, is there a better way to respond to Parameter changes than using a Listener in the synthvoice class?

1 Like

I have the exact same problem with an empty constructor for SynthVoice…
anyone?

Did a bit more research as to why the problem exists:
SynthesiserVoice is an abstract class (but has an empty constructor as far as I can tell).

I then tried invoking the (empty) constructor in my derived class constructor:

class SynthVoice : public SynthesiserVoice
{
public:
SynthVoice::SynthVoice() : SynthesiserVoice{}
{

}

But that didn’t help - when this empty constructor is there the SynthVoice is broken cannot make a sound.
And when I remove the empty constructor it works again…
What on earth is going on??

It works for me, exactly your usecase, just with the difference I hand over a reference to the AudioProcessorValueTreeState:

I don’t see a reason why you would need to call the base class constructor, if you don’t supply any arguments

1 Like

Exactly.
I suspect my troubles stem from maybe having the synthVoice in just a .h file (no .cpp) as per theAudioProgrammers youtube examples - maybe this in some way collides with Vis.Studio 2019 … I’ll try to restructure and see how that goes…

Restructured the SynthVoice class into a .h and .cpp (and did a clean of the build in Visual Studio).
But no luck.
I still cannot implement a constructor for the derived class SynthVoice (derived from juce::SynthesiserVoice ) without the synthVoice going silent.
And again when I remove my constructor the sound from the voice is generated again.

I would have been surprised, if that was the culprit.

Can you show how you create and add the voices? My hunch is that they never end up in the synth due to some bogus by-copy assignment or whatever could have gone wrong

1 Like

I’m pretty sure you are right - I copied it from theAudioProgrammers youtube tutorial - but now that I study it it does look kind of suspect (and I seem to remember that he explained it by something that he wasn’t sure he understood himself - but that it seemed to work…)
The synthVoice related code is gathered below:

Processor.h declarations:

Synthesiser mySynth;
SynthVoice* myVoice;

In the Processor constructor:

mySynth.clearVoices(); // this clears any garbage in the voices of our synth - cajo
for (int i = 0; i < 5; i++) { // we define the polyphony as 5 voices
mySynth.addVoice(new SynthVoice());
}
mySynth.clearSounds(); //this clears the sounds
mySynth.addSound(new SynthSound());

In the processor ::processBlock:

for (size_t i = 0; i < mySynth.getNumVoices(); i++)
{
if (myVoice = dynamic_cast<SynthVoice*>(mySynth.getVoice(i)))
{
myVoice->setEnvelopeParams(APVTS_tree.getRawParameterValue(tree_ampAtt_ID)->load(), APVTS_tree.getRawParameterValue(tree_ampDec_ID)->load(),
APVTS_tree.getRawParameterValue(tree_ampSus_ID)->load(), APVTS_tree.getRawParameterValue(tree_ampRel_ID)->load());
myVoice->setOscType(APVTS_tree.getRawParameterValue(tree_osc1Waveform_ID)->load());
}
}

Especially the part in the processBlock is hard to understand at (least for me) it seems to compare and assign at the same time (the “=” sign in the if statement)…

That is a trick to execute the block only, if the result of dynamic_cast was successful, i.e. mySynth.getVoice(i) is actually of type SynthVoice. Otherwise it is nullptr, which converts to false if implicitly casted to bool.
It looks even nicer, if you have mySynth not declared outside, but in that if statement:

if (auto* myVoice = dynamic_cast<SynthVoice*>(mySynth.getVoice(i)))

That way myVoice is not visible outside of that scope, because you cannot know what it is outside anyway, so it would be hazardous to use it.

Next improvement is to have the lookup of the values only once outside the loop. Even better to keep a pointer to the parameters, since getRawParameterValue() is an unnecessary lookup. Not very expensive, but avoidable.

But for the actual problem I don’t have an idea at the moment…

1 Like

I like your suggested solution better (the myVoice variable “dangling around” without any purpose or use bothered me :slight_smile:

So I implemented that change…

I’m stumped… I think I need to play with one of the JUCE tutorials using SynthVoice and see if I can create an constructor there, or find an alternate route to instantiating objects on SynthVoice creation (but darn thats what the constructor is for :slight_smile:

@cjohs Did you ever figure out what’s wrong? I have the same exact problem that i needed to do something in the constructor and it ends up with no sound being played.