1

I'm learning how to use the Steinberg VST 2.4 SDK (or rather, the 2.x portion that comes with the 3.6.0 version). I've created a simple synthesizer designed to play a sine wave at a constant frequency during its lifetime. Here is the code for this synth:

static const float TAU = 3.14159f * 2;

AudioEffect* createEffectInstance(audioMasterCallback audioMaster) {
    return new VSTTest(audioMaster);
}

VSTTest::VSTTest(audioMasterCallback audioMaster) : AudioEffectX(audioMaster, 0, NUM_PARAMS), //NUM_PARAMS is 0
                                                    fDeltaTime(1.0f / getSampleRate()), //time per sample (1.0 / 44100, presumably)
                                                    fFrequency(100.0f), //frequency of the wave (100 Hz)
                                                    fAmplitude(0.5f), //amplitude of the wave
                                                    fPos(0.0f) { //position of the wave in the x direction
    setNumInputs(0);
    setNumOutputs(2);
    canProcessReplacing();
    isSynth();
}

VSTTest::~VSTTest(void) {

}

void VSTTest::processReplacing(float** input, float** output, VstInt32 numFrames) {
    for (VstInt32 i = 0; i < numFrames; i++) {
        output[0][i] = fAmplitude * sin(fPos);
        output[1][i] = fAmplitude * sin(fPos);

        fPos += fFrequency * TAU * fDeltaTime;

        if (fPos >= TAU) {
            fPos = fmod(fPos, TAU);
        }
    }
}

void VSTTest::setSampleRate(float fSampleRate) {
    fDeltaTime = 1.0f / fSampleRate;
}

The problem is that when the VST is loaded as a channel in FL Studio, I can hear (and see) it changing pitch a couple of times over about 20 seconds until it settles on a final pitch that isn't even correct (deviates by about 10 Hz). Why is this happening?

NmdMystery
  • 2,778
  • 3
  • 32
  • 60

2 Answers2

0

You are adding a huge number to fPos, it should just be incremented by 1. The formula you are looking for is:

sinf(2.0 * M_PI * fPos++ * fFrequency / fSampleRate);
Nik Reiman
  • 39,067
  • 29
  • 104
  • 160
  • This algorithm isn't very great because of the clicking that occurs when changing pitch. Instead of counting samples, you're supposed to use a float that represents the phase of a wave. This way, when the frequency is increased you take into account the phase of the previous frequency so that the wave doesn't suddenly jump from one position to another in both the X/Y direction. That's what fPos is for. I know frequency is supposed to be constant here, but either way, that doesn't really explain the shift in frequency - I'm incrementing fPos with a runtime constant. – NmdMystery Mar 16 '14 at 00:23
  • The algorithm doesn't cause clicking (or at least it shouldn't), the original code is here: https://github.com/teragonaudio/KickMaker/blob/master/source/effects/Waveforms/fxSinewave.cpp It's a synth I wrote ages ago which also uses pitch modification and does not click when modifying the pitch. – Nik Reiman Mar 16 '14 at 11:22
  • There's a question on here somewhere about this. I forgot where it is exactly - Google isn't turning anything up. Imagine, though, that you jumped from 100 Hz to 107 Hz within one sample. Doing things this way would give you an X value as if the frequency had been 107 Hz the whole time - the phase might jump ahead a whole PI, which would definitely cause some clicking. IE, if `fPos` is 100, you'd jump from a phase of ~1.42 to ~1.54, rather than smoothly from ~1.42 to ~1.44 if you'd taken the previous phase into account. – NmdMystery Mar 16 '14 at 15:26
0

Well this isn't exactly a complete answer, but this was solved by calculating the delta time in resume().

void VSTTest::resume(void) {
    fDeltaTime = 1.0 / getSampleRate();
}

I'm not sure why this needs to be done or how it solves the problem, but it does. Also, I should note that fDeltaTime should not be initialized in the constructor since there's no guarantee getSampleRate() is correct at that time.

NmdMystery
  • 2,778
  • 3
  • 32
  • 60