SynthesizerVoice/Midimessage based panning?


#1

Hi!

Is this possible? Pan the audio on a specific SynthesizerVoice? I tried using Synthesizervoice.ControllerMoved(10,0) (CC10 is supposed to be the panning?) for example, and also Midimessage.controllerEvent(channel, 10, 0), where channel = either 1 and also tried the channel where the happening note happens…

But no panning effect.

Samples are mono-audio, and synthesizer-channels are set to stereo-pairs(multi-channel).

  • Masqutti

#2

Create your own SynthesiserVoice derived class and override SynthesiserVoice::renderNextBlock()

Rail


#3

Thanks!

I tried that and it gives loads of "cannot access private member declared in class ‘juce::SamplerSound’.

Now this leads me thinking I’d need to Derive loads of classes and soon the whole API(except the parents that has pure virtual methods…) to override 1 method… So, I’m thinking there must be a better way? What am I doing wrong?.. :frowning:

Masqutti


#4

64 = center, 0=left, 127=right, yes?

So you need to write some kind of pan law where your stereo signal’s value in each channel is multiplied by that midi value.

You’re just converting midiCC messages into actual sample buffer changes.


#5

Yeah, I understood that, but I lack a bit C++ knowledge to derive the Synthvoice Class properly into my own, SamplerVoice-alike, class that has the RenderNextBlock overridden… I tried and I’m having a nightmare with the private members of the SynthesizerVoice class that are needed in overridden RenderNextBlock… :frowning:

I tried also just copying all the stuff in SamplerVoice and re-naming it to my own class named SamplerVoicePan, and it did the same trouble, SamplerSound’s private members couldn’t get access. 2 of those private variables are needed to run the renderNextBlock…

I understand that private cannot be accessed, but what’s the workaround? I know of getter or similar functions but without rewriting the API source to include getter methods, I don’t know how to get those variables to work… And I’d believe writing the source code is kinda wrong way to do it?

Edit: To define more clearly, errors are:

error C2248: ‘juce::SamplerSound::data’ : cannot access private member declared in class 'juce::SamplerSound’
error C2248: ‘juce::SamplerSound::length’ : cannot access private member declared in class ‘juce::SamplerSound’

Thanks for the effort!
Masqutti


#6

have you studied /JUCE-4.2.4/examples/audio plugin demo/source/* ?


#7

Not yet, but there seems to be pretty much what I need, thanks for the tip!!! :smile:

Masqutti


#8

You’re welcome! I’m currently staring at the examples for the TreeView class in the JuceDemo myself.


#9

I don’t know, but I guess aggregation like this could work:

class PannableSynthesiserVoice : public SynthesiserVoice {
public:
    PannableSynthesiserVoice (SynthesiserVoice* voice)
    : myVoice (voice),
      panning (0.5f) {}
    virtual ~PannableSynthesiserVoice () {}

    // like this for all needed virtuals:
    bool canPlaySound (SynthesiserSound *sound) override {
        myVoice->canPlaySound (sound);
    }

    void controllerMoved (int controllerNumber, int newControllerValue) override {
        if (controllerNumber == 10) {
            panning = newControllerValue;
        }
        myVoice->controllerMoved (controllerNumber, newControllerValue);
    }

    void renderNextBlock (AudioBuffer< float > &outputBuffer, int startSample, int numSamples) override {
        myVoice->renderNextBlock (outputBuffer, startSample, numSamples);
        outputBuffer.applyGain (0, startSample, numSamples, jmin (1.0f, panning * 2.0f));
        outputBuffer.applyGain (1, startSample, numSamples, jmin (1.0f, (1.0f - panning) * 2.0f));
    }

private:
    ScopedPointer<SynthesiserVoice> myVoice;
    float panning;
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PannableSynthesiserVoice);
};

(got longer than I thought…)


#10

Thanks alot!

I needed to also override StartNote and StopNote methods since they’re pure virtual ones without definitions(I guess?)… Did you try yours in practice or just came up with “from the top of your hat” ? :smiley:

I still didn’t got sound out of it and so I’m thinking did I add the voice the right way into my synthesiser…? Now it’s:

synth[midiChannel]->addVoice (new PannableSynthesiserVoice(new SamplerVoice()));

Aggregates seem just a tiny bit confusing but I think I got it… :smiley:


#11

I’m glad it worked… :slight_smile:
it was a completely theoretical idea, I haven’t tried it.
Just wanted to point you to aggregation as alternative to inheritance, as you actually don’t need access to the internals.


#12

Yeah but I didn’t get it to play anything yet so it almost worked :smiley: Do you think I add the voice & create the aggregate the right way to my synthesiser? (code above how I did it)… :slight_smile:


#13

Ah sorry, I thought after you used another method to initialise it worked, misunderstood that.
I am no expert with the Synthesiser classes.
Do you call the aggregated object, when you override noteOn etc.? Like the one example I gave for canPlaySound? To be safe, you might do that for ALL virtual methods.

void startNote (int midiNoteNumber, float velocity, SynthesiserSound *sound, int currentPitchWheelPosition) override {
    myVoice->startNote (midiNoteNumber, velocity, sound, currentPitchWheelPosition);
}
// etc...

and does it play without the PannableSynthesiserVoice() around?

Good luck…


#14

You mean if I do:

synth[midiChannel]->addVoice (new SamplerVoice()); ? Yeah then the synth plays ok!

Yeah I did as you showed with the StartNote and StopNote, but I’ll try overriding all the virtual ones… Thanks alot for the tips! :slight_smile:


#15

Aggregate didn’t work for some reason but I ended up deriving the whole SamplerVoice-class, and finalyl I got Samples out & ability to affect Voice’s rendernextblock. :slight_smile: