Как создать генератор синусоидальных колебаний, который может плавно переходить между частотами

27

Я могу написать базовый генератор синусоидальной волны для аудио, но я хочу, чтобы он мог плавно переходить с одной частоты на другую. Если я просто перестану генерировать одну частоту и сразу переключусь на другую, сигнал будет прерывистым, и будет слышен «щелчок».

Мой вопрос заключается в том, что является хорошим алгоритмом для генерации волны, которая начинается, скажем, с 250 Гц, а затем переходит на 300 Гц, без каких-либо щелчков. Если алгоритм включает необязательное время скольжения / портаменто, то тем лучше.

Я могу подумать о нескольких возможных подходах, таких как передискретизация, за которой следует фильтр низких частот, или, возможно, использование волновой таблицы, но я уверен, что это достаточно распространенная проблема, поскольку существует стандартный способ ее решения.

Марк Хит
источник
2
Почему вы просто не использовали линейный частотный переход в течение переходного периода. Например, вам нужно перейти от частоты f0 в момент времени t0 к частоте f1 в момент времени t1, тогда почему бы просто не ввести частоту перехода f (t) = f0 * (1-q) + f1 * q, где q = (t -t0) / (t1-t0), тогда произведите сигнал A (t) = sin (2 * Pi * f (t) * t)?
mbaitoff

Ответы:

24

Один из подходов, который я использовал в прошлом, заключается в поддержании фазового аккумулятора, который используется в качестве индекса в таблице поиска сигналов. Значение дельты фазы добавляется к аккумулятору на каждом интервале выборки:

phase_index += phase_delta

Чтобы изменить частоту, вы изменяете дельту фазы, которая добавляется к фазовому аккумулятору в каждом образце, например

phase_delta = N * f / Fs

где:

phase_delta is the number of LUT samples to increment
freq is the desired output frequency
Fs is the sample rate

Это гарантирует, что выходной сигнал будет непрерывным, даже если вы меняете phase_delta динамически, например, для изменений частоты, FM и т. Д.

Для более плавных изменений частоты (портаменто) вы можете изменять значение phase_delta между его старым значением и новым значением в течение подходящего числа интервалов выборки, а не просто мгновенно изменять его.

Обратите внимание , что phase_indexи у phase_deltaобоих есть целое и дробное компонент, то есть они должны быть с плавающей точкой или фиксированной точкой. Целочисленная часть phase_index (размер таблицы по модулю) используется в качестве индекса в LUT формы сигнала, а дробная часть может дополнительно использоваться для интерполяции между соседними значениями LUT для вывода более высокого качества и / или меньшего размера LUT.

Пол Р
источник
спасибо, я ожидал, что ответ может включать LUT. Я думал о том, чтобы пойти с LUT, который содержит один сигнал на частоте 1 Гц (т.е. записи Fs). Существует ли эмпирическое правило, регулирующее оптимальный размер LUT?
4
Это зависит от различных факторов: какое SNR вы ищете, будь то чистый синусоидальный или более сложный сигнал, планируете ли вы интерполировать между соседними записями LUT или просто усекать и т. Д. Это также зависит от того, собираетесь ли вы просто иметь одну таблицу квадрантов и самостоятельно обрабатывать арифметику индексирования и инверсии знаков, или иметь полную таблицу из четырех квадрантов. Лично я бы начал с таблицы с 1024 точками (NB: 2 ^ N хорошо для индексации по модулю) с четырьмя квадрантами без интерполяции, поскольку это очень просто и должно давать хорошие результаты, например, для 16-битного «потребительского» звука.
Пол Р
1
Хороший ответ, Пол. Есть также похожий вопрос по теме, который был опубликован некоторое время назад; больше информации всегда помогает.
Джейсон Р
4
Другой способ взглянуть на этот подход - эмуляция генератора, управляемого напряжением (ГУН). Выходная частота ГУН зависит от входного напряжения (обычно линейная функция входного напряжения), но выходной сигнал имеет непрерывную фазу, даже если входное напряжение переключается мгновенно. Выходные данные где - это непрерывная функция времени, в то время как выходная частота является производной фазой, и равна где частота покоя.
sin(ϕ(t))=sin(0tω0+kx(τ)dτ)
ϕ(t)
ω0+kx(t)
ω0
Дилип Сарватэ
1
У меня была такая же проблема, спасибо за идею аккумулятора (я использовал прямой расчет, который не работал из-за приближений): jsfiddle.net/sebpiq/p3ND5/12
sebpiq
12

Один из лучших способов создать синусоидальную волну - использовать сложный вектор с рекурсивным обновлением. Т.е.

z[n+1]=z[n]Ω

где z [n] - вектор, , где - угловая частота осциллятора в радианах, а - индекс выборки. И действительная, и мнимая части являются синусоидальными, они сдвинуты по фазе на 90 градусов. Очень удобно, если вам нужны как синус, так и косинус. Для расчета одной выборки требуется только 4 умножения и 4 сложения, и оно НАМНОГО дешевле, чем что-либо, содержащее sin () cos () или справочные таблицы. Потенциальная проблема заключается в том, что амплитуда может дрейфовать со временем из-за проблем с числовой точностью. Однако есть довольно простой способ исправить это. Допустим, что . Мы знаем, что должен иметь единичную величину, т.е. Ω=exp(jω)ωnz[n]z[n]=a+jbz[n]

aa+bb=1

Таким образом, мы можем проверять время от времени, если это все еще так и исправить соответственно. Точная коррекция будет

z[n]=z[n]aa+bb

Это неуклюжий расчет, но так как очень близок к единице, вы можете аппроксимировать с помощью разложения Тейлора около и мы получаемaa+bb1/xx=1

1x3x2

поэтому коррекция упрощается до

z[n]=z[n]3a2b22

Применяя эту простую коррекцию каждые несколько сотен сэмплов, генератор будет стабильно работать вечно.

Для непрерывного изменения частоты множитель W необходимо соответствующим образом обновить. Даже непостоянное изменение множителя будет поддерживать функцию непрерывного генератора. Если требуется линейное изменение частоты, обновление может быть разбито на несколько шагов, или вы можете использовать тот же алгоритм осциллятора для обновления самого множителя (так как это также комплексный вектор с единичным усилением).

Hilmar
источник
спасибо за этот ответ, возможно, мне потребуется некоторое время, чтобы понять достаточно хорошо, чтобы превратить в какой-то реальный код, но это выглядит как интересная альтернатива, которую можно попробовать.
Марк Хит
2
Я реализовал это решение на Голанге для справки: github.com/rmichela/Acoustico/blob/…
Райан
Это прекрасное решение, которое, к сожалению, хорошо работает только при использовании постоянной базы времени. Если нет, вам нужно вычислить грех и cos, чтобы вычислить правильное сложное вращение.
Кэмерон Таклинд
2

С этого сайта :

Чтобы создать плавный переход от одной частоты к другой или от одной амплитуды до другой, неполная синусоида должна быть модифицирована добавленным участком, чтобы результирующая волна после каждой итерации цикла while заканчивалась на оси x.

Похоже, это должно работать.

(На самом деле, если они оба синхронизируются по оси X при переходе, я полагаю, что постепенный переход не требуется.)


источник
1
Это говорит: подождите, пока текущая синусоида на частоте завершит один цикл и достигнет а затем переключится на другую синусоиду на частоте . Это эффективно поддерживает непрерывность фазы и, вероятно, будет в порядке для аудиоприложений, в которых задержка в несколько миллисекунд или микросекунд между желаемым временем переключения (сейчас) и реализованным временем переключения (когда моя синусоида завершает цикл) несущественна. Тем не менее, разница может создать проблемы в других приложениях. Просто помните, что синусоида равна дважды за один цикл, и обязательно выберите правильный! ω00ω10
Дилип Сарвейт
2

Я согласен с предыдущими предложениями по использованию фазового аккумулятора. По сути, управляющий вход представляет собой величину опережения фазы за шаг или за такт (или за прерывание или что-то еще), так что изменение этого значения изменяет частоту без разрыва фазы. Амплитуда волны затем определяется из накопленного значения фазы либо с помощью LUT, либо просто путем вычисления sin (тета) или cos (тета).

По сути, это то, что обычно известно как генератор с числовым управлением (NCO) или прямой цифровой синтезатор (DDS). Выполнение веб-поиска по этим терминам, вероятно, даст больше, чем вы хотите знать о теории и практике, чтобы заставить их работать хорошо.

Добавление дополнительного аккумулятора может позволить плавные переходы между частотами, как вы предложили, если это желательно, также путем контроля скорости изменения значения продвижения фазы. Это иногда называют цифровым дифференциальным анализатором или DDA.

Эрик Якобсен
источник
Хорошая дополнительная информация. Рад видеть тебя здесь, Эрик; мы могли бы использовать министра алгоритмов.
Джейсон Р
1

1-й порядок, вы должны отрегулировать начальную фазу новой синусоиды частоты так, чтобы она была такой же, как и фаза предыдущей синусоиды в точке 1-го перехода. Вычислите первую частоту и используйте ее фазу для второй частоты.

Второй вариант может заключаться в повышении d_phase от частоты одной частоты к другой в течение нескольких выборок. Это очистит непрерывность 1-й производной и обеспечит скольжение.

Третий вариант может заключаться в использовании сглаживающего окна, такого как рельефный косинус, для скорости линейного изменения d_phase.

hotpaw2
источник