GetDunne Wiki

Notes from the desk of Shane Dunne, software development consultant

User Tools

Site Tools


overview

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
overview [2017/08/30 16:09]
shane [The SynthSound object]
overview [2017/08/30 16:43] (current)
shane [The SynthSound object and class juce::SynthesiserSound]
Line 12: Line 12:
  
 The processor needs to be able to notify the GUI editor when it changes one or more synth parameters (e.g. when a new preset is selected), so it can update the GUI display. This can be done in any number of ways, but I chose to have the //VanillaJuceAudioProcessor// class also derive from the //juce::ChangeBroadcaster//, and the //VanillaJuceAudioProcessorEditor// inherit from //juce::ChangeListener//. The processor calls its //sendChangeMessage()// function to notify the editor, which results in a call to the editor's //changeListenerCallback()// function. The processor needs to be able to notify the GUI editor when it changes one or more synth parameters (e.g. when a new preset is selected), so it can update the GUI display. This can be done in any number of ways, but I chose to have the //VanillaJuceAudioProcessor// class also derive from the //juce::ChangeBroadcaster//, and the //VanillaJuceAudioProcessorEditor// inherit from //juce::ChangeListener//. The processor calls its //sendChangeMessage()// function to notify the editor, which results in a call to the editor's //changeListenerCallback()// function.
 +
 +To understand how parameter changes are propagated in the reverse direction---from GUI to synthesizer---we need the following overview some of the objects which make up the DSP aspect of VanillaJuce.
  
 ===== The "Synth" objects ===== ===== The "Synth" objects =====
Line 90: Line 92:
 </code> </code>
  
-Just by looking at this, you don't have to delve into the source for class //juce::Synthesiser// to see that its voice-assigning code most likely calls //canPlaySound()// to ensure that a given //juce::SynthesiserVoice//-derived object can actually play the given sound, and if so, calls //startNote()// with the current MIDI note number, key-down velocity and pitch-wheel position, plus a pointer to the sound object. Hence, unless we choose to add a lot of extra member variables to our //SynthVoice// class, the only way our voice objects know what sound to make will be via the //SynthesiserSound* sound// parameter to //startNote()//.+Just by looking at this, you don't have to delve into the source for class //juce::Synthesiser// to see that its voice-assigning code most likely calls //canPlaySound()// to ensure that a given voice can actually play the given sound, and if so, calls //startNote()// with the current MIDI note number, key-down velocity and pitch-wheel position, plus a pointer to the sound object. Hence, unless we choose to add a lot of extra member variables to our //SynthVoice// class, the only way our voice objects know what sound to make will be via the //SynthesiserSound* sound// parameter to //startNote()//.
  
 +So, here is the VanillaJuce //SynthSound// class declaration:
 +<code cpp>
 +class SynthSound : public SynthesiserSound
 +{
 +private:
 +    Synth& synth;
 +
 +public:
 +    SynthSound(Synth& ownerSynth);
 +    
 +    // our sound applies to all notes, all channels
 +    bool appliesToNote(int /*midiNoteNumber*/) override { return true; }
 +    bool appliesToChannel(int /*midiChannel*/) override { return true; }
 +
 +    // pointer to currently-used parameters bundle
 +    SynthParameters* pParams;
 +
 +    // call to notify owner Synth, that parameters have changed
 +    void parameterChanged();
 +};
 +</code>
 +Member variable //synth// object is a reference to the //Synth// object (which never changes).
 +//pParams// is a pointer to the currently-selected preset. I've made //pParams// public so the //VanillaJuceAudioProcessor// object (which creates and "owns" the one //SynthSound// object) can change it whenever a different preset is selected, so it points to the appropriate entry in the //programBank// array.
 +
 +All the //Gui...// class constructors take a //SynthSound*// argument, so they can use the //pParams// member to access the current parameter values, in order to display and modify them. Furthermore, whenever any part of the GUI changes a parameter value, it calls the //parameterChanged()// function, which is just this:
 +<code cpp>
 +void SynthSound::parameterChanged()
 +{
 +    synth.soundParameterChanged();
 +}
 +</code>
 +//Synth::soundParameterChanged()// simply iterates over all active (currently-sounding) voices, and calls their //soundParameterChanged()// function. (I looked at the code for //juce::Synthesiser// to see how it handles iterating over all voices.)
 +<code cpp>
 +void Synth::soundParameterChanged()
 +{
 +    // Some sound parameter has been changed. Notify all active voices.
 +    const ScopedLock sl(lock);
 +
 +    for (int i = 0; i < voices.size(); ++i)
 +    {
 +        SynthVoice* const voice = dynamic_cast<SynthVoice*>(voices.getUnchecked(i));
 +        if (voice->isVoiceActive())
 +            voice->soundParameterChanged();
 +    }
 +}
 +</code>
 +The code for //SynthVoice::soundParameterChanged()// is not so trivial, but all it really does is re-initialize the currently sounding note so that the sound changes to reflect whatever was changed in the GUI.
  
overview.1504109379.txt.gz · Last modified: 2017/08/30 16:09 by shane