How to add multiple SamplerSound's for a single note and discriminate by velocity?



I am trying to load a piano sample library. As much as I can see by the API docs, there isn't a default way to load all samples for a note and assign velocity limits (different sample to be played for different velocity ranges), is there?

What would be the best/recommended approach here?


Thanks in advance for any responses.


Think I found what I was looking for.

I am going to subclass Synthesiser and override noteOn and noteOff. I would also need my own version of SamplerSound, to override appliesToNote.

I will refactor appliesToNote -> appliesTo( note, velocity ) and will call that from noteOn and noteOff in the Synthesiser inheritor.

If this isn't the right direction, I would appreciate any hints. Thanks!

BTW, @jules and everyone else involved into shaping this library - beautiful code!


I've subclassed SamplerSound and Synthesiser. SamplerSoundLayer, has a velocity range field and appliesTo( note, velocityRange ) method.

But overriding noteOn and noteOff in Synthesiser I've hit an issue with private fields, which aren't accessible through public/protected methods.

With noteOn I've used the public getter methods and it's overriden successfully, but noteOff sets the keyIsDown private field of SynthesiserVoice and reads the sustainPedalsDown private field of Synthesiser.

I am not sure what can I do in this case...


If you hit the same problem - here is what I came up with.

Sampler : Synthesiser.

// Sampler.h


#include "../JuceLibraryCode/JuceHeader.h"

class Sampler : public Synthesiser
    void noteOn (
        const int midiChannel,
        const int midiNoteNumber,
        const float velocity
    ) override;



// Sampler.cpp

#include "Sampler.h"
#include "SamplerSoundLayer.h"

void Sampler::noteOn (const int midiChannel,
                          const int midiNoteNumber,
                          const float velocity)
    const ScopedLock sl (lock);

    for (int i = sounds.size(); --i >= 0;)
        SynthesiserSound* const soundSource = sounds.getUnchecked(i);
        SamplerSoundLayer* const sound = static_cast<SamplerSoundLayer* const> ( soundSource );

        if ( sound->appliesTo ( midiNoteNumber, velocity )
             && sound->appliesToChannel ( midiChannel ) )
            // If hitting a note that's still ringing, stop it first (it could be
            // still playing because of the sustain or sostenuto pedal).
            for ( int j = voices.size(); --j >= 0; )
                SynthesiserVoice* const voice = voices.getUnchecked (j);
                if ( voice->getCurrentlyPlayingNote() == midiNoteNumber
                     && voice->isPlayingChannel ( midiChannel ) )
                    stopVoice ( voice, 1.0f, true );
            startVoice ( findFreeVoice ( sound, midiChannel, midiNoteNumber, isNoteStealingEnabled() ),
                        sound, midiChannel, midiNoteNumber, velocity );

SamplerSoundLayer : SamplerSound.

// SamplerSoundLayer.h


#include "../JuceLibraryCode/JuceHeader.h"

class SamplerSoundLayer : public SamplerSound
    SamplerSoundLayer (
        const String& name,
        AudioFormatReader& source,
        const BigInteger& midiNotes,
        int midiNoteForNormalPitch,
        Range<float> velocityRange,
        double attackTimeSecs,
        double releaseTimeSecs,
        double maxSampleLengthSeconds
    bool appliesTo( int midiNoteNumber, float velocity );
    Range<float> velocity;



// SamplerSoundLayer.cpp

#include "SamplerSoundLayer.h"

SamplerSoundLayer::SamplerSoundLayer (const String& soundName,
                            AudioFormatReader& source,
                            const BigInteger& notes,
                            const int midiNoteForNormalPitch,
                            Range<float> velocityRange,
                            const double attackTimeSecs,
                            const double releaseTimeSecs,
                            const double maxSampleLengthSeconds)
    :   SamplerSound(
        velocity( velocityRange )


bool SamplerSoundLayer::appliesTo( int midiNoteNumber, float velocity )
    bool appliesToMidiNote = appliesToNote( midiNoteNumber );
    bool isInVelocityRange = this->velocity.contains( velocity );

    return appliesToMidiNote && isInVelocityRange;



Nikolay Tsenkov