Custom Sampler Class - Inheriting from SamplerVoice

What I want to do is to write a custom sampler class, inheriting all the existing functionality of SamplerVoice, but being able to add/change some things in the methods. I have tried to do this, but keep getting stuck, can anyone give me some help with the right way to do this?

you could try posting some code where you expect the issue to be

Yes sure.

#include "SamplerSynthVoice.h"


SamplerSynthVoice::SamplerSynthVoice() {
    
}

bool SamplerSynthVoice::canPlaySound (juce::SynthesiserSound* sound)
{
    return dynamic_cast<const juce::SamplerSound*> (sound) != nullptr;
}

void SamplerSynthVoice::startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound* s, int /*currentPitchWheelPosition*/)
{
    if (auto* sound = dynamic_cast<const juce::SamplerSound*> (s))
    {
        pitchRatio = pow(2, ((midiNoteNumber - 69) / 12.0)) * (440);

        sourceSamplePosition = 0.0;
        
        adsr.noteOn();
    }
}

void SamplerSynthVoice::stopNote (float velocity, bool allowTailOff)
{
    adsr.noteOff();
    if (!allowTailOff || !adsr.isActive()) {
        clearCurrentNote();
    }
}

void SamplerSynthVoice::pitchWheelMoved (int newValue) {}
void SamplerSynthVoice::controllerMoved (int controllerNumber, int newValue) {}

//==============================================================================
void SamplerSynthVoice::renderNextBlock (juce::AudioBuffer<float>& outputBuffer, int startSample, int numSamples)
{
    if (auto* playingSound = static_cast<SamplerSynthSound*> (getCurrentlyPlayingSound().get()))
    {
        auto& data = *playingSound->data;
        const float* const inL = data.getReadPointer (0);
        const float* const inR = data.getNumChannels() > 1 ? data.getReadPointer (1) : nullptr;

        float* outL = outputBuffer.getWritePointer (0, startSample);
        float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getWritePointer (1, startSample) : nullptr;

        while (--numSamples >= 0)
        {
            auto pos = (int) sourceSamplePosition;
            auto alpha = (float) (sourceSamplePosition - pos);
            auto invAlpha = 1.0f - alpha;

            // just using a very simple linear interpolation here..
            float l = (inL[pos] * invAlpha + inL[pos + 1] * alpha);
            float r = (inR != nullptr) ? (inR[pos] * invAlpha + inR[pos + 1] * alpha)
                                       : l;

            auto envelopeValue = adsr.getNextSample();

            l *= gain * envelopeValue;
            r *= gain * envelopeValue;

            if (outR != nullptr)
            {
                *outL++ += l;
                *outR++ += r;
            }
            else
            {
                *outL++ += (l + r) * 0.5f;
            }

            sourceSamplePosition += pitchRatio;

            if (sourceSamplePosition > playingSound->length)
            {
                stopNote (0.0f, false);
                break;
            }
        }
    }
}

#pragma once
#include <JuceHeader.h>
#include "SamplerSynthSound.h"

class SamplerSynthVoice    : public juce::SynthesiserVoice
{
public:
    SamplerSynthVoice();

    ~SamplerSynthVoice() override;

    //==============================================================================
    bool canPlaySound (juce::SynthesiserSound*) override;

    void startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound*, int pitchWheel) override;
    void stopNote (float velocity, bool allowTailOff) override;

    void pitchWheelMoved (int newValue) override;
    void controllerMoved (int controllerNumber, int newValue) override;

    void renderNextBlock (juce::AudioBuffer<float>&, int startSample, int numSamples) override;
    using juce::SynthesiserVoice::renderNextBlock;

private:
    //==============================================================================
    double pitchRatio = 0;
    double sourceSamplePosition = 0;
    float gain = 0.2f;

    juce::ADSR adsr;

    JUCE_LEAK_DETECTOR (SamplerSynthVoice)
};

Above is the cpp and header file for the custom sampler voice.

#pragma once
#include <JuceHeader.h>

class SamplerSynthSound    : public juce::SynthesiserSound
{
public:
    SamplerSynthSound (const juce::String& name,
                  juce::AudioFormatReader& source,
                  const juce::BigInteger& midiNotes,
                  int midiNoteForNormalPitch,
                  double attackTimeSecs,
                  double releaseTimeSecs,
                       double maxSampleLengthSeconds) {
        soundName = name;
        
    };

    ~SamplerSynthSound() override;

    //==============================================================================
    const juce::String& getName() const noexcept                  { return soundName; }

    juce::AudioBuffer<float>* getAudioData() const noexcept       { return data.get(); }

    //==============================================================================
    void setEnvelopeParameters (juce::ADSR::Parameters parametersToUse)    { params = parametersToUse; }

    //==============================================================================
    bool appliesToNote (int midiNoteNumber) override;
    bool appliesToChannel (int midiChannel) override;

private:
    //==============================================================================
    friend class SamplerSynthVoice;

    juce::String soundName;
    std::unique_ptr<juce::AudioBuffer<float>> data;
    double sourceSampleRate;
    juce::BigInteger midiNotes;
    int length = 0, midiRootNote = 0;

    juce::ADSR::Parameters params;

    JUCE_LEAK_DETECTOR (SamplerSynthSound)
};

And here is the custom sampler sound.

This code is all copied from the original implementations in the built in sampler classes, I just wanted to have custom classes so I could change some things (gain and ADSR).
I have done a similar thing with the SynthVoice and sound, and that works fine.

I call these from the PluginProcessor.cpp, the same way as you’d call juce::SamplerSound or Voice. But when I compile in XCode, the error says undefined symbols, with very little other information.

I appreciate this is a lot of code to read, I just would like to know if I am going about this to best way.

I have now fixed my error, it was an ‘undefined symbols’ error where I had forgotten to implement some of the classes with {}.