What's wrong with my simple Parameter Smoothing? [SOLVED]

#1

Hello,

I’ve built my own parameter smoothing function because for some reason in my install of JUCE it can’t see SmoothedValue (even though the file is there in my JUCE folder).

So I call it glide() and it looks like this:

void glide(float& parameter, float target, float scalar)
{
    parameter = parameter - scalar * (parameter - target);
}

I’m testing it in AudioDeviceIOCallback() with only 1 channel to output a sinewave:

for (int i = 0; i < numSamples; ++i)
        {
            glide(freq, newFreq, 0.0005);
            outputChannelData[0][i] = std::sin (2.0f * pi * freq * static_cast<float>(pos++) / sampleRate);
        }

I change the frequency by setting the variable using std::cin in main() (I’m running a console app).

The complete code looks like this:

#include "../JuceLibraryCode/JuceHeader.h"
#define PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062

//==============================================================================

class AudioApp : public AudioIODeviceCallback
{
private:
    float freq, newFreq;
    int pos;
    float sampleRate;

    AudioDeviceManager dm;

public:
    AudioApp()
    {
        dm.addAudioCallback(this);
        dm.initialiseWithDefaultDevices(0,1);        
    }
    
    void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
                                    float** outputChannelData, int numOutputChannels,
                                    int numSamples) override
    {   
        ///********** Barebones way of creating a sineWave (with glide)
        for (int i = 0; i < numSamples; ++i)
        {
            glide(freq, newFreq, 0.0005);
            outputChannelData[0][i] = std::sin (2.0f * PI * freq * static_cast<float>(pos++) / sampleRate);
        }
        //*****************************************************************************************/
    }
    
    void audioDeviceAboutToStart(AudioIODevice* device) override
    {
        sampleRate = device->getCurrentSampleRate();
    }
    
    void audioDeviceStopped() override
    {
    }

    void glide(float& parameter, float target, float scalar)
    {
        parameter = parameter - scalar * (parameter - target);
    }

    void setFreq(double value)
    {
        newFreq = value;
    }
};

int main (int argc, char* argv[])
{
    AudioApp iLoveTheJUCEForums;
    
    while (true)
    {
        float userFreq;
        std::cout << "Enter frequency: ";
        std::cin >> userFreq;
        iLoveTheJUCEForums.setFreq(userFreq);
    }

    return 0;
}

2 Questions:

1: Why does my glide function always appear to glide to the destination from a very high number, no matter what frequency I set, or where the previous frequency was. (The sinewave sounds like I’m trying to create a sci-fi laser sound)

2: Why can’t I just plonk glide() in place of freq in my sinewave generating algorithm, modifying it to return a float, and either remove (or not) the pass by reference?.. i.e.:

float glide(float parameter, float target, float scalar)
{
    return parameter = parameter - scalar * (parameter - target);
}

Thank you so much

#2

When you change the frequency of an oscillator, you’ll need a phase which you update with the current frequency each sample.

Try something like this:

for (int i = 0; i < numSamples; ++i)
{
    glide(freq, newFreq, 0.0005);
    outputChannelData[0][i] = std::sin (phase);
    phase +=  2.0f * pi * freq  / sampleRate
}

phase should be a member of your processor, and you might want to modulo it once it’s larger than 2*pi

It’s a common misconception, that in an oscillator you use the frequency to generate the sine-wave. You actually use the phase.
With a constant frequency, you can replace the phase by the term you used above, but not with a changing frequency.

#3

I don’t quite understand… in my algorithm I have a variable pos, position I assume. And frequency multiplies pos++ divided by the samplerate. isn’t pos the phase in that case? and so changing the frequency, will change the pos (phase) at a faster rate?

In any case, are you saying then that there is nothing wrong with my glide algorithm? :joy:

I tried to test it out by having glide() control gain instead of frequency, but curiously that appears to have no effect at all now! :frowning:

Here’s my complete code (since it’s so simple), and I understand if you don’t want to do my debugging for me :wink:

Thanks again!

#include "../JuceLibraryCode/JuceHeader.h"
#define PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062

//==============================================================================

class AudioApp : public AudioIODeviceCallback
{
private:
    float freq, newFreq, volume, newVolume;
    int pos;
    float sampleRate;

    AudioDeviceManager dm;

public:
    AudioApp()
    {
        dm.addAudioCallback(this);
        dm.initialiseWithDefaultDevices(0,2);
        setFreq(440);        
    }
    
    void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
                                    float** outputChannelData, int numOutputChannels,
                                    int numSamples) override
    {   
        ///********** Barebones way of creating a sineWave (with glide)
            for (int sample = 0; sample < numSamples; ++sample)
            {
                // Do your per sample maths:
                // glide(freq, newFreq, 0.0005);
                glide(volume, newVolume, 0.05);
                float sinOsc = std::sin (2.0f * PI * freq * static_cast<float>(pos++) / sampleRate);
                for (int channel = 0; channel < numOutputChannels; channel++)
                {
                    outputChannelData[channel][sample] = sinOsc * volume;
                }
            }
        //*****************************************************************************************/
    }
    
    void audioDeviceAboutToStart(AudioIODevice* device) override
    {
        sampleRate = device->getCurrentSampleRate();
    }
    
    void audioDeviceStopped() override
    {
    }

    void glide(float& parameter, float target, float scalar)
    {
        parameter = parameter - scalar * (parameter - target);
    }

    void setFreq(float value)
    {
        newFreq = value;
    }

    void setVolume(float value)
    {
        newVolume = value;
        std::cout << "volume set to: " << value << std::endl;
    }
};

int main (int argc, char* argv[])
{
    AudioApp iLoveTheJUCEForums;
    
    while (true)
    {
        float userInput;
        std::cout << "Enter volume: ";
        std::cin >> userInput;
        iLoveTheJUCEForums.setVolume(userInput);
    }

    return 0;
}
#4

In your volume example, freq is always 0 as you’re not updating freq but only newFreq, so it should be:

    void setFreq(float value)
    {
        freq = value;
    }

Works like a charm. Also with my frequency with phase fix above :slight_smile:

With storing pos you don’t store the current phase, just a parameter you can calculate the current phase with, if you have a constant frequency. With variable frequency, you need to calculate the phase increment for the current frequency and add it to the phase sample per sample.

1 Like
#5

Good catch! Thank you!

Yes indeed, it works! Aww man, pretty proud right now :slight_smile:

Yes, I kind of get what you mean, but I’ll save it for another day/project.

Thank you so much