Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>(From the original author of the VST BLT code).</p> <p>As a matter of fact, I was porting the VST BLT oscillators to C#, so I was googling for good sin oscillators. Here's what I came up with. Translation to C++ is straightforward. See the notes at the end about accuumulated round-off errors.</p> <pre><code>public class FastOscillator { private double b1; private double y1, y2; private double fScale; public void Initialize(int sampleRate) { fScale = AudioMath.TwoPi / sampleRate; } // frequency in Hz. phase in radians. public void Start(float frequency, double phase) { double w = frequency * fScale; b1 = 2.0 * Math.Cos(w); y1 = Math.Sin(phase - w); y2 = Math.Sin(phase - w * 2); } public double Tick() { double y0 = b1 * y1 - y2; y2 = y1; y1 = y0; return y0; } } </code></pre> <p>Note that this particular oscillator implementation will drift over time, so it needs to be re-initialzed periodically. In this particular implementation, the magnitude of the sin wave decays over time. The original comments in the STK code suggested a two-multiply oscillator. There are, in fact, two-multiply oscillators that are reasonably stable over time. But in retrospect, the need to keep the sin(phase), and sin(m*phase) oscillators tightly in synch probably means that they have to be resynched anyway. Round-off errors between phase and m*phase mean that even if the oscillators were stable, they would drift eventually, running a significant risk of producing large spikes in values near the zeros of the BLT functions. May as well use a one-multiply oscillator. </p> <p>These particular oscillators should probably be re-initialized every 30 to 100 cycles (or so). My C# implementation is frame based (i.e. it calculates an float[] array of results in a void Tick(int count, float[] result) method. The oscillators are re-synched at the end of each Tick call. Something like this:</p> <pre><code> void Tick(int count, float[] result) { for (int i = 0; i &lt; count; ++i) { ... result[i] = bltResult; } // re-initialize the oscillators to avoid accumulated drift. this.phase = (this.phase + this.dPhase*count) % AudioMath.TwoPi; this.sinOsc.Initialize(frequency,this.phase); this.mSinOsc.Initialize(frequency*m,this.phase*m); } </code></pre> <p>Probably missing from the STK code. You might want to investigate this. The original code provided to the STK did this. Gary Scavone tweaked the code a bit, and I think the optimization was lost. I do know that the STK implementations suffer from DC drift, which can be almost entirely eliminated when implemented properly. </p> <p>There's a peculiar hack that prevents DC drift of the oscillators, even when sweeping the frequency of the oscillators. The trick is that the oscillators should be started with an initial phase adjustment of dPhase/2. That just so happens to start the oscillators off with zero DC drift, without having to figure out wat the correct initial state for various integrators in each of the BLT oscillators. </p> <p>Strangely, if the adjustment is re-adjusted whenever the frequency of the oscillator changes, then this also prevents wild DC drift of the output when sweeping the frequency of the oscillator. Whenever the frequency changes, subtract dPhase/2 from the previous phase value, recalculate dPhase for the new frequency, and then add dPhase/2.I rather suspect this could be formally proven; but I have not been able to so. All I know is that It Just Works.</p> <p>For a block implementation, the oscillators should actually be initialized as follows, instead of carrying the phase adjustment in the current this.phase value.</p> <pre><code> this.sinOsc.Initialize(frequency,phase+dPhase*0.5); this.mSinOsc.Initialize(frequency*m,(phase+dPhase*0.5)*m); </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload