Search code examples
c#audiosynthesizer

Synthesizer Slide from One Frequency to Another


I'm writing a synthesizer in C# using NAudio. I'm trying to make it slide smoothly between frequencies. But I have a feeling I'm not understanding something about the math involved. It slides wildly at a high pitch before switching to the correct next pitch.

What's the mathematically correct way to slide from one pitch to another?

Here's the code:

public override int Read(float[] buffer, int offset, int sampleCount) { int sampleRate = WaveFormat.SampleRate;

        for (int n = 0; n < sampleCount; n++)
        {
            if (nextFrequencyQueue.Count > 0)
            {                    
                nextFrequency = nextFrequencyQueue.Dequeue();
            }

            if (nextFrequency > 0 && Frequency != nextFrequency)
            {
                if (Frequency == 0) //special case for first note
                {
                    Frequency = nextFrequency;
                }
                else //slide up or down to next frequency
                {
                    if (Frequency < nextFrequency)
                    {
                        Frequency = Clamp(Frequency + frequencyStep, nextFrequency, Frequency);
                    }
                    if (Frequency > nextFrequency)
                    {
                        Frequency = Clamp(Frequency - frequencyStep, Frequency, nextFrequency);
                    }
                }
            }

            buffer[n + offset] = (float)(Amplitude * Math.Sin(2 * Math.PI * time * Frequency));
            try
            {
                time += (double)1 / (double)sampleRate;
            }
            catch
            {
                time = 0;
            }
        }
        return sampleCount;
    }

Solution

  • You are using absolute time to determine the wave function, so when you change the frequency very slightly, the next sample is what it would have been had you started the run at that new frequency.

    I don't know the established best approach, but a simple approach that's probably good enough is to compute the phase (φ = t mod 1/fold) and adjust t to preserve the phase under the new frequency (t = φ/fnew).

    A smoother approach would be to preserve the first derivative. This is more difficult because, unlike for the wave itself, the amplitude of the first derivative varies with frequency, which means that preserving the phase isn't sufficient. In any event, this added complexity is almost certainly overkill, given that you are varying the frequency smoothly.