(От первоначального автора кода VST BLT).
На самом деле я переносил генераторы VST BLT на C #, поэтому я искал хорошие синус-генераторы.Вот что я придумал.Перевод на C ++ прост.См. Заметки в конце о накопленных ошибках округления.
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;
}
}
Обратите внимание, что эта конкретная реализация генератора будет дрейфовать со временем, поэтому ее необходимо периодически переинициализировать.В этой конкретной реализации величина синусоиды уменьшается со временем.В первоначальных комментариях в коде STK предлагался генератор с двумя множителями.Фактически существуют два умножителя, которые достаточно стабильны во времени.Но в ретроспективе необходимость синхронизации синусоидальных (фазовых) и синусоидальных (m * фазовых) осцилляторов, вероятно, означает, что в любом случае их необходимо повторно синхронизировать.Ошибки округления между фазой и фазой m * означают, что, даже если осцилляторы были стабильными, они в конечном итоге дрейфовали, создавая значительный риск создания больших всплесков значений вблизи нулей функций BLT.Можно также использовать генератор с одним множителем.
Эти конкретные генераторы, вероятно, следует повторно инициализировать каждые 30–100 циклов (или около того).Моя реализация C # основана на фреймах (то есть вычисляет массив результатов float [] в методе void Tick (int count, float [] result). Осцилляторы повторно синхронизируются в конце каждого вызова Tick. Примерно так:
void Tick(int count, float[] result)
{
for (int i = 0; i < 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);
}
Возможно, отсутствует в коде STK. Возможно, вы захотите исследовать это. Оригинальный код, предоставленный STK, сделал это. Гэри Скавоне немного подправил код, и я думаю, что оптимизация была потеряна.действительно знаю, что реализации STK страдают от дрейфа постоянного тока, который может быть почти полностью устранен при правильной реализации.
Существует своеобразный взлом, который предотвращает дрейф постоянного тока осцилляторов, даже когда происходит качание частоты генераторов.является то, что генераторы должны быть запущены с начальной настройкой фазы dPhase / 2. Это так же, как и для запуска генераторов с нулевым дрейфом постоянного тока, без необходимости выяснять правильное начальное состояние для различных интеграторов в каждом из генераторов BLT.
Странно, еслирегулировка переустанавливается всякий раз, когда изменяется частота генератора, тогда это также предотвращает дикий дрейф постоянного тока на выходе при качании частоты генератора.Всякий раз, когда частота изменяется, вычтите dPhase / 2 из предыдущего значения фазы, пересчитайте dPhase для новой частоты, а затем добавьте dPhase / 2. Я подозреваю, что это может быть формально доказано;но я так не смог.Все, что я знаю, это то, что это просто работает.
Для реализации блока осцилляторы должны фактически инициализироваться следующим образом, вместо переноса регулировки фазы в текущем значении this.phase.
this.sinOsc.Initialize(frequency,phase+dPhase*0.5);
this.mSinOsc.Initialize(frequency*m,(phase+dPhase*0.5)*m);