Sine wave plugin randomly changes pitch

Hello all. I have a simple audio plugin that just produces a continuous sine wave at 261.63hz (C4). To achieve this I have the following members:

float currentPhase = 0.0;
float phasePerSample = 0.0;
float toneFunc (float phase){
  return std::sin(phase * juce::MathConstants<float>::twoPi) * 0.025f;
}

In the prepareToPlay function I set phasePerSample as:

phasePerSample = 261.63f / (float)sampleRate;

Then in the processBlock function I have:

for(auto i = 0; i < buffer.getNumSamples(); i++){
        const float sampleValue = toneFunc(currentPhase);
        for(auto chan = 0; chan < buffer.getNumChannels(); chan++){
            buffer.setSample(chan, i, sampleValue);
        }
        currentPhase += phasePerSample;
    }

This is basically my version of a HelloWorld project. Every plugin I’ve made so far (which isn’t many but still done a few) I start with this to verify that everything is building and outputting correctly. And for all the others everything has worked exactly as you’d expect.

However today I’ve run into an odd issue where the pitch of the tone randomly shifts. Usually its within about 2 semitones, but has jumped by as much as 5 semitones (Its never perfectly on note, just within those ranges). The length of time that it holds a particular tone varies from half a second to multiple seconds.

I am completely stumped. I’ve poured over both the editor and processor files, and checked my math. I’ve also made sure that i’m using the same AudioPluginHost to test the plugin as I have for the others. The only change that I have made compared to previous projects is that I’m now using CMake + CLion instead of Projucer + XCode. Surely that can’t be causing this though right? Any help would be massively appreciated!

Just a guess, but it may be worth trying.

Code in the processBlock() routine is resource-sensitive. It could be that calling out to an external function, toneFunc(), has some impact. I doubt it’s that but you could write put your toneFunc() function code inline to test.

Again, I doubt this is an issue but you could change your ‘auto’ types to ‘int’ for more efficiency (very little gain, but best practice nevertheless is to keep everything as lean as possible in processBlock() ).

It seems you keep on adding to currentPhase in all eternity. It will soon loose it’s precision if you keep on doing that. Better to limit the value you store in currentPhase to between 0…twoPi. You can do

if (currentPhase > twoPi)
    currentPhase -= twoPi;

Or you could at least change it to a double…

1 Like

correct, but In the OP the phase counts to 1, so you can use
currentPhase = fmod( currentPhase, 1.f);

Changing the types to double did indeed fix the issue. Also I don’t how I’ve been getting away without looping the phase value.

In theory you’d have gotten away with it when the frequency was lower because there would be less distance between zero and the wrapped value. Higher frequencies would have suffered more.

You can safely assume that every compiler since 1970 will inline this function call if optimizations are enabled.

Changing the types to double did indeed fix the issue

This. I’m not in the team double for audio signals, but filter coefficients and phase counters are the two things were single precision definitely isn’t enough.

Two things to check out:

ToneGeneratorAudioSource

dsp::Phase

Matt