Як створити генератор синусоїди, який може плавно переходити між частотами


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

Це гарантує, що форма вихідної хвилі є безперервною, навіть якщо ви динамічно змінюєте фазу_дельта, наприклад, для зміни частоти, FM та ін.

Для більш плавних змін частоти (портаменто) ви можете перемикати значення фазової дельта між старим значенням та новим значенням протягом відповідної кількості інтервалів вибірки, а не просто змінювати їх миттєво.

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


спасибі, я очікував, що відповідь може включати LUTs. Я думав перейти з LUT, який містить одну форму хвилі на 1 Гц (тобто записи Fs). Чи існує правило, яке регулює оптимальний розмір LUT?

4
Це залежить від різних факторів: який SNR ви шукаєте, чи це чиста синусова хвиля чи більш складна форма хвилі, чи плануєте ви інтерполювати між суміжними записами LUT або просто усікати тощо. Це також залежить від того, чи збираєтесь ви просто мати єдину таблицю квадрантів і обробляти арифметику індексації та інверсію знаків самостійно або мати повну чотири таблиці квадрантів. Особисто я почав би з 1024 балів (NB: 2 ^ N добре підходить за модульною індексацією) чотирьох квадрантних таблиць без інтерполяції, оскільки це дуже просто і має дати хороші результати, наприклад, для 16-бітового "споживчого" аудіо.
Пол Р

1
Гарна відповідь, Пол. Існує також подібне запитання щодо теми, яка була розміщена деякий час назад; більше інформації завжди допомагає.
Джейсон R

4
Інший спосіб дивитися на цей підхід - емуляція генератора, керованого напругою (VCO). Вихідна частота VCO залежить від вхідної напруги (зазвичай це лінійна функція вхідної напруги), але вихідний сигнал має безперервну фазу, навіть якщо вхідна напруга перемикається миттєво. Вихід є де ϕ ( t ) - безперервний
sin(ϕ(t))=sin(0tω0+kx(τ)dτ)
ϕ(t)функція часу, тоді як вихідна частота є похідною фази і дорівнює де ω 0 - частота спокою.
ω0+kx(t)
ω0
Діліп Сарват

1
У мене була така ж проблема, дякую за ідею накопичувача (я використовував прямий розрахунок, який не працював через наближення): jsfiddle.net/sebpiq/p3ND5/12
sebpiq

12

Одним з найкращих способів створення синусоїди є використання складного фазора з рекурсивним оновленням. Тобто

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

Ω=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 потрібно оновити відповідно. Навіть неперервна зміна множника підтримуватиме функцію безперервного осцилятора. Якщо потрібна рампова частота, оновлення можна або розділити на кілька кроків, або ви можете використовувати той самий алгоритм генератора для оновлення самого множника (оскільки це також складний фазор посилення єдності).


дякую за цю відповідь, ймовірно, мені знадобиться трохи часу, щоб зрозуміти достатньо добре, щоб перетворитись на якийсь реальний світовий код, але спробувати це виглядає як цікава альтернатива.
Марк Хіт

2
Я реалізував це рішення у голонагу для довідки: github.com/rmichela/Acoustico/blob/…
Ryan Michela

Це прекрасне рішення, яке, на жаль, працює добре лише за умови постійної бази часу. Якщо ні, то для обчислення правильного складного обертання потрібно обчислити гріх і cos.
Камерон Таклінд

2

З цього сайту :

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

Здається, що це має спрацювати.

(Насправді, якщо вони обидва синхронізовані на осі x при переході, я вважаю, що поступовий перехід не потрібен.)


1
ω00ω10

2

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

Це, по суті, відомий як цифровий керований осцилятор (NCO) або прямий цифровий синтезатор (DDS). Здійснення пошуку в Інтернеті за цими термінами, ймовірно, дасть більше, ніж ви хочете знати з теорії та практики змусити їх працювати добре.

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


Хороша додаткова інформація. Радий бачити тебе тут, Еріку; ми могли б використовувати міністр алгоритмів.
Джейсон R

1

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

Другим варіантом може бути перехід d_phase з частоти однієї частоти на другу протягом декількох зразків. Це очистить наступність 1-ї похідної та забезпечить ковзання.

Третім варіантом може бути використання вікна згладжування, такого як косинус з підвищеною швидкістю, на швидкості синхронізації d_phase.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.