How to detune voices

projucer
windows
audio

#1

Hello everyone,

lets say I have a Synthesiser with 8 Voices, how am I supposed to detune them all?

Thanks in advance


#2

Are you / your plugin / app synthesising the voices?

If so then it’s quite trivial. Just change the frequency of each of your oscillators by some degree. If you want to detune by say +5cent.

    double root = std::exp (std::log(2) / 1200); // cent root
    double cent = pow(root, 5); // raise to the number of cent 

double detunedFreq = originalFreq * cent;

Where std::exp (std::log(2) / 1200) is the 12th hundred root of an octave or 100th root of a note.


#3

Where did that equation for pitch come from?


#4

I worked it out.
I have changed to be clearer as it looked a little dirty…
The math is just an given octave is double its base frequency and on a logarithmic scale we use log(2) and that a cent is just 100th root of a note and a note is just the 12th root of an octave then we just need the exponent of log(2) / 1200
The exponent is a number when in this case is multiplied by itself 1200 times = 2
Then take the cent root and raise it to the power of the number of scent

This should also work if you want to work in semi-tones

double root = std::exp (std::log(2) / 12); // semi-tone root
double tones = pow(root, 0.05); // raise to the number of semi tones

#5

It’s maybe simpler to just detune the note in the linear MIDI Note scale before calculating the frequency.


#6
double midiNoteToFrequency(double midiNote)
{
    return std::exp ((midiNote-69) * std::log(2) / 12) * 440;
}

And while we are on that subject you can do that like this…
You can supply a midi note of say 44.05


#7

FYI we also provide MidiMessage::getMidiNoteInHertz()


#8

First, thank you all for your participation :slight_smile:

Are you / your plugin / app synthesising the voices?
I currently have a regular Synthesiser set up like this

mySynth.clearVoices();

for (int i = 0; i < 16; i++)
{
	mySynth.addVoice(new SynthVoice());
}

mySynth.clearSounds();
mySynth.addSound(new SynthSound());

And this is my SynthVoice Class

#pragma once

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

class SynthVoice : public SynthesiserVoice
{
	public:
		bool canPlaySound(SynthesiserSound* sound) {
			return dynamic_cast<SynthSound*>(sound) != nullptr;
		}
		
		void startNote(int midiNoteNumber, float velocity, SynthesiserSound* sound, int currentPitchWheelPosition) {
			
			env1.trigger = 1;
			level = velocity;
			frequency = MidiMessage::getMidiNoteInHertz(midiNoteNumber);
		}

		void stopNote(float velocity, bool allowTailOff) {
			env1.trigger = 0;
			allowTailOff = true;

			if (velocity == 0)
				clearCurrentNote();
		}

		void pitchWheelMoved(int newPitchWheelValue) {

		}

		void controllerMoved(int controllerNumber, int newControllerValue) {

		}
		 
		void renderNextBlock(AudioBuffer<float> &outputBuffer, int startSample, int numSamples) {
			env1.setAttack(2000);
			env1.setDecay(500);
			env1.setSustain(0.8);
			env1.setRelease(2000);

			

			for (int sample = 0; sample < numSamples; sample++) {

				double theWave = osc1.saw(frequency + (rand() % 5)); //DETUNING
				double theSound = env1.adsr(theWave, env1.trigger) * level;
				double filteredSound = filter1.lores(theSound, 100, 0.1);

				for (int channel = 0; channel < outputBuffer.getNumChannels(); channel++) {
						outputBuffer.addSample(channel, startSample, theSound);
				}
				++startSample;
			}
		}

private:
	double level, frequency;

	maxiOsc osc1;
	maxiEnv env1;
	maxiFilter filter1;

};

So how do I set up multiple voices so I can detune them all? Do I add multiple Oscillators, add all the detuned signals together and divide them with the number of Osc’s?
(I’m using the Oscillator from Maximilian)

Thanks in advance


#9

First of all it may make more sense to have a little restructuring, either put your filters,envs and oscillators, detune amount into arrays or have a voice structure with them as members you can then do something like this

    for (int sample = 0; sample < numSamples; sample++) {
        
        float sum = 0.0;
        
        for(int v=0; v< numVoice; c++)
        {
            float theWave = osc[v].saw(frequency + detune[v]); //DETUNING
            float theSound = env[v].adsr(theWave, env[v].trigger) * level;
            float filteredSound = filter[v].lores(theSound, 100, 0.1);
            sum += theSound;
        }
        
        for (int channel = 0; channel < outputBuffer.getNumChannels(); channel++) {
            outputBuffer.addSample(channel, startSample, sum);
        } ++startSample;
    }

That won’t work straight away but you need to work to something like that to achieve what your trying to do. Also you want to keep the detune amount fairly consistent if not constant between each sample frame (not between each voice though) otherwise you’ll be producing more of a noise than a voice. You don’t really need to be using doubles - far too much precision for most audio. The only real use for double is maybe accurately working out coefficients e.t.c.

Would help to know if this synth is monophonic with voices or polyphonic with voices as there is another level of abstraction needed to be added in the later.


#10

Neat, thank you!

My Synth is polyphonic, but im thinking off making it monophonic because I’m aiming to create a Reese-Bass specialized VST :slight_smile: