LFO: низкочастотный генератор и модуляция

Как медленное колебание управляет звуком и как получить его на C++ и JUCE?

Опубликовано Дополнено 16 мин чтения

В треках:Программист, этап 5Музыкант, этап 5

Если медленно поворачивать ручку усиления туда и обратно, звук будет то усиливаться, то ослабевать. Низкочастотный генератор (low-frequency oscillator, LFO)низкочастотный генератор (low-frequency oscillator, LFO) — Генератор медленно повторяющегося управляющего сигнала, которым изменяют параметры звука, например усиление, частоту или панораму. Общей строгой верхней границы частоты у LFO нет. создаёт повторяющийся управляющий сигнал, которым можно автоматически менять этот и другие параметры звука. Его выход — последовательность чисел; значение каждого числа задаёт положение параметра в соответствующий момент.

Почему «low»

Осциллятор (oscillator)осциллятор (oscillator) — Генератор повторяющегося колебания. Частота задаёт число повторений за секунду, а форма волны — изменение значения внутри одного периода. — генератор повторяющегося колебания. Частота (frequency)частота (frequency) — Число полных колебаний за секунду. Обозначается f и измеряется в герцах; 1 Гц означает одно колебание в секунду. ff показывает число повторений за секунду, а период (period)период (period) — Время одного полного повторения периодического колебания. Обозначается T, измеряется в секундах и связано с частотой как T = 1/f. T=1/fT = 1/f — время одного повторения. У LFO частота обычно достаточно мала, чтобы изменение параметра воспринималось как отдельное движение: например, 0,5 Гц даёт один цикл за две секунды, а 5 Гц — пять циклов за секунду.

Слово low означает низкую частоту колебания относительно обычных звуковых частот. За 0,1 с синусоида 10 Гц проходит один период, а синусоида 440 Гц — 44 периода. В первом случае удобно управлять медленным изменением звука, во втором — создавать слышимый тон.

Строгой общей границы для LFO нет. Ориентир «ниже примерно 20 Гц» связан с нижней границей слышимых частот, но некоторые генераторы позволяют перейти в звуковой диапазон. Например, LFO синтезатора Operator имеет низкий и высокий частотные режимы (руководство Ableton, Operator). При быстром изменении параметра результат может восприниматься уже как изменение тембра (timbre)тембр (timbre) — Свойство слухового ощущения, по которому различают два звука одинаковой высоты и громкости. Зависит от состава спектра и его изменения во времени..

Низкая частота основного повторения также не гарантирует, что в сигнале нет высоких частот. У прямоугольной и пилообразной волн резкие переходы; при прямом выводе на аудиовыход или резкой модуляции (modulation)модуляция (modulation) — Изменение параметра одного сигнала под действием другого. Например, управляющий сигнал LFO может периодически менять амплитуду звука или частоту генератора. они могут давать слышимые составляющие.

Что можно менять с помощью LFO

Модуляциямодуляция (modulation) — Изменение параметра одного сигнала под действием другого. Например, управляющий сигнал LFO может периодически менять амплитуду звука или частоту генератора. — изменение параметра одного сигнала под действием другого. В звуковом тракте LFO направляют на вход управления: например, его число превращают в коэффициент усиления, затем умножают на него аудиоотсчёт (sample)отсчёт (sample) — Значение сигнала в один момент дискретизации, одно число. В звукозаписи его также называют сэмплом.. Скорость движения задаётся частотой LFO, а размах изменения — глубиной модуляции (modulation depth)глубина модуляции (modulation depth) — Величина изменения управляемого параметра. Её единицы и шкала зависят от параметра: например, глубина может задавать отклонение частоты в герцах или долю ослабления звука..

Таблица 1. Результат зависит от параметра, на который направлен LFO.
ПараметрЧто меняетсяПример
УсилениеАмплитуда сигналаТремоло (tremolo): регулярное усиление и ослабление
Частота звукового генератораВысота тонаВибрато (vibrato): колебание высоты вокруг основной ноты
Частота среза фильтраБаланс частотПериодическое изменение яркости звука
ПанорамаРаспределение между левым и правым каналамиАвтопанорамирование (auto-pan)
Время задержкиЗадержка обрабатываемой копииВ хорусе (chorus) и фленжере (flanger) — движение задержанной копии относительно исходной

Для эффекта важны и форма LFO, и способ преобразования его выхода в нужные единицы. Число −1 на выходе LFO может означать крайнее левое положение панорамы (pan)панорама (pan) — Положение слухового образа слева направо в стереополе., но напрямую задать частоту среза −1 Гц нельзя. Значения управления нужно преобразовать в диапазон выбранного параметра; примеры ниже показывают, как это сделать.

Биполярный сигнал (bipolar signal)биполярный сигнал (bipolar signal) — Сигнал, принимающий как положительные, так и отрицательные значения. Нормированный биполярный LFO меняется от −1 до +1 и может управлять отклонением параметра от его центрального значения. принимает значения обоих знаков; здесь он меняется между −1 и +1. Его удобно прибавлять к центральному значению параметра: p[n]=p0+D l[n]p[n] = p_0 + D\,l[n], где p0p_0 — центр, DD — размах в единицах параметра, а l[n]l[n] — значение LFO без единицы измерения. Например, для панорамы со шкалой от −1 (слева) до +1 (справа) можно выбрать p0=0p_0 = 0 и D=0,8D = 0{,}8. Тогда звук движется между −0,8 и +0,8, проходя через центр. Для частоты среза p0=1000p_0 = 1000 Гц и D=500D = 500 Гц дадут движение от 500 до 1500 Гц: отрицательные значения LFO понижают частоту относительно центра, сохраняя её положительной.

Униполярный сигнал (unipolar signal)униполярный сигнал (unipolar signal) — Сигнал, принимающий значения одного знака, включая ноль. Для управления часто используют диапазон от 0 до 1; из биполярного LFO l его получают преобразованием u = (l + 1)/2. принимает значения одного знака. Для диапазона от 0 до 1 преобразуем выход LFO: u[n]=(l[n]+1)/2u[n] = (l[n] + 1)/2. Если l[n]=−1l[n] = -1, то u[n]=0u[n] = 0; если l[n]=0l[n] = 0, то u[n]=0,5u[n] = 0{,}5. Такой сигнал удобно использовать для периодического ослабления звука.

Фаза и скорость её изменения

Фаза (phase)фаза (phase) — Положение внутри периода колебания, выраженное углом: полный период соответствует 360°, или 2π радиан. Начальная фаза — её значение в момент t = 0. описывает положение внутри цикла. Полный оборот — 2π2\pi радиан (radian)радиан (radian) — Единица угла, заданная отношением длины дуги окружности к её радиусу. Дуга длиной в один радиус соответствует 1 рад; полный оборот — 2π рад., или 360°. Можно хранить положение на окружности как угол от 0 до 2π2\pi либо от −π-\pi до π\pi. В обоих случаях после полного оборота начинается следующий цикл.

При постоянной частоте развёрнутая фаза растёт линейно: θ(t)=θ0+2πft\theta(t) = \theta_0 + 2\pi f t, где tt — время в секундах, θ0\theta_0 — начальный угол в радианах. За секунду прибавляется 2πf2\pi f радиан. Чем быстрее растёт фаза, тем больше оборотов происходит за секунду и тем выше частота.

Для вычисления волны достаточно хранить фазу внутри одного оборота. В диапазоне [−π,π)[-\pi,\pi) достижение π\pi означает перенос (phase wrap): из фазы вычитают 2π2\pi. Например, угол 348° составляет примерно 1,933π1{,}933\pi радиан; в диапазоне [−π,π)[-\pi,\pi) это −12°, или примерно −0,067π-0{,}067\pi. Синус (sine)синус (sine) — Вертикальная координата точки на единичной окружности под заданным углом от положительного направления горизонтальной оси. Для острого угла прямоугольного треугольника равен отношению противолежащего катета к гипотенузе. обоих углов одинаков. На следующем обороте вычисления повторяются.

В цифровом генераторе фазовый аккумулятор (phase accumulator)фазовый аккумулятор (phase accumulator) — Состояние цифрового генератора, хранящее текущую фазу колебания. На каждом временном шаге фаза увеличивается на приращение, заданное частотой генератора и частотой вычисления отсчётов; после полного оборота переносится в исходный диапазон. хранит угол между вызовами. На каждом временном шаге он выдаёт текущую фазу и увеличивает её на приращение из формулы (1).

Δφ=2πffs\Delta\varphi = \frac{2\pi f}{f_s}
Δφ\Delta\varphi
приращение фазы за один отсчёт, рад;
ff
частота LFO, Гц;
fsf_s
частота вычисления отсчётов LFO, Гц; в примерах она равна частоте дискретизации аудио.

При f=5f = 5 Гц и fs=48 000f_s = 48\,000 Гц приращение равно π/4800≈0,0006545\pi/4800 \approx 0{,}0006545 рад. Один оборот занимает 9600 отсчётов, то есть 0,2 с. При удвоении ff приращение удваивается, а число отсчётов в периоде уменьшается вдвое.

Скорость фазы и частота синусоидыДва примера с общей шкалой времени от 0 до 0,2 секунды. Для 10 герц фаза проходит от минус пи до пи дважды, для 20 герц — четырежды. Под каждой фазой синусоида с тем же числом периодов и амплитудой один. Переносы фазы обозначены пунктиром.10 Гц · T = 0,10 сφ, радπ0−πsin φ10−100,10,220 Гц · T = 0,05 сφ, радπ0−πsin φ10−100,10,2t, с
Рис. 1. За одинаковые 0,2 с генератор 10 Гц делает два оборота, а генератор 20 Гц — четыре. Пунктир показывает перенос записанного угла; синусоида при переносе остаётся непрерывной.

Частота LFO и частота его вычисления — разные величины. Можно вычислять LFO на каждом аудиокадре даже при одном периоде за несколько секунд. Если вычислять его реже, например один раз на блок, в формуле должна стоять фактическая частота обновления управления. Простое удержание одного усиления на весь блок создаёт ступени; для плавной модуляции обычно нужны интерполяция или сглаживание. Дальше LFO вычисляется на каждом кадре.

Формы волн из одной фазы

Пусть φ\varphi — текущая фаза в радианах, −π≤φ<π-\pi \leq \varphi < \pi. Функция (function)функция (function) — Правило, сопоставляющее каждому допустимому значению аргумента ровно одно выходное значение. Запись h(u) обозначает результат функции h при аргументе u. w(φ)w(\varphi) преобразует её в значение LFO. Все формы ниже повторяются при переносе фазы; их нормированные значения лежат между −1 и +1. Начало цикла выбрано в −π-\pi, как у juce::dsp::Oscillator после сброса.

Синусоида

Синусоидальная волна (sine wave) задаётся w(φ)=sin⁡φw(\varphi) = \sin\varphi. Она плавно проходит через ноль и плавно меняет направление в максимуме и минимуме. При выбранной начальной фазе сигнал стартует из нуля вниз; максимум +1 находится при φ=π/2\varphi = \pi/2, минимум −1 — при −π/2-\pi/2. Другая начальная фаза сдвигает волну во времени, сохраняя её форму и частоту.

Треугольник

Треугольная волна (triangle wave) задаётся w(φ)=1−2∣φ∣/πw(\varphi) = 1 - 2|\varphi|/\pi. При −π-\pi она равна −1, при нуле — +1, затем линейно возвращается к −1. На каждой половине периода скорость изменения постоянна; в вершинах направление меняется резко, но разрыва значения нет.

Прямоугольная волна и импульс

Прямоугольная волна (square wave) с равными половинами периода принимает два значения:

w(φ)={−1,−π≤φ<0,+1,0≤φ<π.w(\varphi) = \begin{cases} -1, & -\pi \leq \varphi < 0,\\ +1, & 0 \leq \varphi < \pi. \end{cases}

На каждой половине периода параметр удерживается в одном положении, затем переключается. В точке φ=0\varphi = 0 здесь выбрано +1. Выражение через знак синуса требует отдельного решения в нуле, поэтому в коде удобнее явное сравнение фазы.

Импульсная волна (pulse wave) позволяет менять долю периода с высоким уровнем — коэффициент заполнения (duty cycle). Если q=(φ+π)/(2π)q = (\varphi + \pi)/(2\pi) — положение в цикле от 0 до 1, то можно взять +1 при q<dq < d и −1 иначе, где 0<d<10 < d < 1 — доля высокого уровня. При d=0,5d = 0{,}5 получается прямоугольная волна с равными половинами; относительно формулы (2) она начинается с противоположного уровня. Узкий импульс получается при малом dd.

Пила

Пилообразная волна (sawtooth wave) задаётся w(φ)=φ/πw(\varphi) = \varphi/\pi. Это линейный подъём от −1 к +1 и резкое возвращение к −1 при переносе фазы. Значение +1 — предел перед концом цикла; при диапазоне [−π,π)[-\pi,\pi) оно не достигается. Для нисходящей пилы достаточно поменять знак: −φ/π-\varphi/\pi.

Визуализация 2Четыре формы LFO из одной фазы
Фаза как угол вектора на окружностиОкружность радиусом 1, угол отсчитывается от направления вправо против часовой стрелки; отрицательная фаза — по часовой стрелке. Вектор повёрнут на −45° (−0,785 рад). Высота его конца — значение синусоиды: −0,71.

φ = −45° (−0,785 рад)

  • Синусоидаsin φ = sin(−0,785) ≈ −0,71
    Синусоида на одном цикле фазыСинусоида от −π до π: из нуля уходит вниз до −1 при −π/2, через ноль при 0 поднимается до 1 при π/2 и возвращается к нулю.
  • Треугольник1 − 2|φ|/π = 1 − 2·0,785/π ≈ 0,50
    Треугольник на одном цикле фазыТреугольник от −π до π: линейно растёт от −1 до 1 в нуле и линейно опускается к −1. Разрывов нет.
  • Прямоугольникφ = −0,785 < 0 → −1
    Прямоугольник на одном цикле фазыПрямоугольник от −π до π: −1 на первой половине цикла, в нуле переключается на 1. Пунктир у π — скачок к −1 в начале следующего цикла.
  • Пилаφ/π = −0,785/π ≈ −0,25
    Пила на одном цикле фазыПила от −π до π: линейно растёт от −1 почти до 1. Пунктир у π — скачок к −1 в начале следующего цикла.
  • форма волны w(φ)
  • текущая фаза φ
  • значение в этой фазе
  • скачок к следующему циклу

φ = −45° (−0,785 рад). Первая половина цикла: прямоугольник держит −1, треугольник и пила растут.

Параметры визуализации
−45° (−0,785 рад)

Параметры работают с включённым JavaScript. Рисунок показывает начальное состояние.

Рис. 2. Один цикл LFO от −π до π. Точка на окружности задаёт фазу φ, её можно тянуть мышью или пальцем; вертикальная линия отмечает её на всех графиках, над графиком — расчёт значения. Пунктир у прямоугольника и пилы обозначает скачок к началу следующего цикла; анимация делает оборот за 4 с.

Прямоугольная волна подходит для переключения между двумя положениями, пила — для повторяющегося прохода в одном направлении. Для плавного движения удобны синусоида и треугольник. Резкие формы при управлении усилением могут создавать щелчки; сглаживание переходов меняет их форму и должно быть осознанной частью эффекта.

Реализация на C++ без JUCE

Функция waveform преобразует фазу в значение выбранной волны по приведённым выше формулам. Код использует стандартную библиотеку C++20; WaveShape задаёт форму. На вход подаётся фаза в диапазоне [−π,π)[-\pi,\pi). Начиная с C++20 число π доступно как std::numbers::pi из заголовка <numbers> (математические константы стандартной библиотеки).

C++
// Формы LFO: перевод фазы в нормированное значение.
#include <cassert>
#include <cmath>
#include <numbers>

constexpr double pi = std::numbers::pi;
enum class WaveShape { sine, triangle, square, saw };

double waveform (double phase, WaveShape shape) noexcept
{
    switch (shape)
    {
        case WaveShape::sine:
            return std::sin (phase);
        case WaveShape::triangle:
            return 1.0 - 2.0 * std::abs (phase) / pi;
        case WaveShape::square:
            return phase < 0.0 ? -1.0 : 1.0;
        case WaveShape::saw:
            return phase / pi;
    }
    return 0.0;
}

Следующий класс добавить после функции. prepare задаёт частоту вычисления, setFrequency меняет скорость, reset возвращает начало цикла, nextSample выдаёт одно значение. Условия примера: конечная положительная частота дискретизации (sample rate)частота дискретизации (sample rate) — Число отсчётов в каждом канале за одну секунду, то же число кадров аудиоданных в секунду. Измеряется в герцах: 44 100 Гц означает 44 100 отсчётов на канал в секунду. и конечная частота LFO от 0 до fs/2f_s/2, не включая верхнюю границу. assert помогает обнаружить нарушение при отладке; проверку пользовательского ввода следует выполнить до передачи параметров в класс.

C++
// Фазовый аккумулятор сохраняет положение между аудиоблоками.
class Lfo
{
public:
    void prepare (double newSampleRate) noexcept
    {
        assert (std::isfinite (newSampleRate) && newSampleRate > 0.0);
        sampleRate = newSampleRate;
        setFrequency (frequency);
        reset();
    }

    void setFrequency (double hz) noexcept
    {
        assert (std::isfinite (hz) && hz >= 0.0 && hz < sampleRate / 2.0);
        frequency = hz;
        increment = 2.0 * pi * frequency / sampleRate;
    }

    void reset() noexcept { phase = -pi; }

    double nextSample (WaveShape shape) noexcept
    {
        const double value = waveform (phase, shape);
        phase += increment;
        if (phase >= pi)
            phase -= 2.0 * pi;
        return value;
    }

private:
    double sampleRate = 48000.0;
    double frequency = 5.0;
    double increment = 2.0 * pi * frequency / sampleRate;
    double phase = -pi;
};

Перенос сохраняет избыток угла: если после прибавления получилось π+0,01\pi + 0{,}01, следующая фаза будет −π+0,01-\pi + 0{,}01. Присваивание phase = -pi потеряло бы эту часть и изменило период. Для указанного диапазона частот хватает одного вычитания 2π2\pi.

Один объект хранить в состоянии обработчика. При подготовке вызвать lfo.prepare (sampleRate), затем lfo.setFrequency (5.0). В цикле по кадрам вызов lfo.nextSample (WaveShape::triangle) выдаёт треугольник; замена значения перечисления выбирает синус, прямоугольник или пилу. При нулевой частоте фаза неподвижна, и выход остаётся постоянным. Смена частоты через setFrequency сохраняет текущую фазу.

Границы аудиоблоков не меняют время генератора. Если сбрасывать фазу перед каждым блоком, вместо непрерывного LFO будут повторяться короткие начальные фрагменты. При блоке 512 кадров и 48 кГц каждый такой фрагмент длится всего около 10,67 мс.

Те же формы с juce::dsp::Oscillator

Примеры сверены со стабильной JUCE 9.0.3, актуальной на 2 октября 2026 года. juce::dsp::Oscillator уже содержит фазовый аккумулятор. Ему нужно передать функцию «фаза → значение», задать частоту и подготовить к обработке. Модуль проекта — juce_dsp; он подключается в Projucer либо через цель CMake:

cmake
# MyPlugin — уже созданная цель вашего проекта.
target_link_libraries (MyPlugin PRIVATE juce::juce_dsp)

В исходнике используется #include <juce_dsp/juce_dsp.h> либо общий JuceHeader.h, если проект его создаёт. Функция waveform и перечисление из предыдущего раздела пригодятся и здесь.

C++
// Подготовка синусоидального LFO перед началом обработки.
#include <juce_dsp/juce_dsp.h>

juce::dsp::Oscillator<double> lfo;
lfo.initialise ([] (double phase)
{
    return waveform (phase, WaveShape::sine);
});

const juce::dsp::ProcessSpec spec { 48000.0, 512, 1 };
lfo.prepare (spec);
lfo.setFrequency (5.0, true);

// processSample прибавляет LFO к входу: 0.0 даёт только управляющий сигнал.
const double value = lfo.processSample (0.0);

В этом фрагменте #include размещается в начале исходника, а подготовка и получение значения — внутри функции. Объект lfo в приложении должен жить между вызовами обработки. Для остальных форм в лямбда-функции выбрать WaveShape::triangle, WaveShape::square или WaveShape::saw. В ProcessSpec указаны реальная частота дискретизации, максимальный размер блока и один канал управляющего сигнала; стереофонический звук может использовать это общее управление.

Начальная фаза и вход processSample

После reset() внутренняя фаза JUCE равна нулю. Но функция формы получает её со сдвигом −π-\pi: её первый аргумент равен −π-\pi, дальше он растёт до π\pi и переносится. Поэтому std::sin (phase) начинает с нуля вниз, а треугольник и пила из наших формул — с −1. Синус в вычислениях с плавающей точкой может дать возле нуля небольшую погрешность. Если нужен синус, начинающийся из нуля вверх, можно вернуть -std::sin (phase).

Вызов processSample (input) прибавляет значение генератора к input. Для получения чистого управляющего сигнала передаётся 0. Передача аудиоотсчёта сюда добавила бы LFO к звуку; для тремоло (tremolo)тремоло (tremolo) — Эффект периодического изменения амплитуды аудиосигнала. Скорость пульсации задаёт частота управляющего генератора, а диапазон усиления зависит от глубины и способа её преобразования. требуется отдельно вычислить усиление и умножить на него звук. Это поведение и сдвиг фазы видны в исходнике Oscillator версии 9.0.3.

process() обрабатывает целый блок. Если им заполняют отдельный буфер управления, входные значения должны быть нулевыми, потому что генератор прибавляет выход и при блочной обработке. В примере тремоло используется processSample(): одно значение LFO применяется ко всем каналам текущего кадра.

Подготовка, сброс и таблица значений

prepare() вызвать из prepareToPlay() с частотой дискретизации и максимальным размером блока, полученными от хоста. Создание функции через initialise() выполнить при создании обработчика. Эти операции могут выделять память; их не следует повторять в аудиоколбэке (audio callback)аудиоколбэк (audio callback) — Функция, которую аудиосистема вызывает для передачи очередного блока входных данных и получения выходных. Обработчик должен завершиться до срока, когда нужны данные..

При подготовке setFrequency (5.0, true) сразу устанавливает 5 Гц. При работе обычный setFrequency (newHz) использует встроенное сглаживание частоты: в JUCE 9.0.3 оно занимает 50 мс. Это сглаживание скорости движения фазы; скачки прямоугольной волны и пилы оно не устраняет.

reset() возвращает начало фазы. Его используют, когда требуется перезапуск: например, при новой ноте или остановке воспроизведения. JUCE не отслеживает музыкальный транспорт за осциллятором; нужное событие обрабатывает ваш код. Свободно работающий LFO (free-running LFO) сохраняет движение независимо от новых нот, а LFO с перезапуском (retriggered LFO) начинает каждую ноту из заданного положения. Частота в герцах также сама по себе не синхронизирует начало цикла с долями такта.

Необязательный второй аргумент initialise, например 128, строит таблицу значений (lookup table) и приближает функцию интерполяцией. Здесь он опущен, поэтому вычисляются исходные формулы. У функции со скачком интерполяция между точками таблицы может изменить резкий переход; таблица также не является антиалиасинговой обработкой.

Применение: тремоло

Тремолотремоло (tremolo) — Эффект периодического изменения амплитуды аудиосигнала. Скорость пульсации задаёт частота управляющего генератора, а диапазон усиления зависит от глубины и способа её преобразования. периодически меняет амплитуду (amplitude)амплитуда (amplitude) — Наибольший модуль отклонения колеблющейся величины от равновесного значения. Для синусоиды обозначается A; единицы совпадают с единицами сигнала. звука. LFO задаёт движение усиления, которым умножается аудиоотсчёт. Частота генератора определяет скорость пульсации, а преобразование его выхода — диапазон усиления.

Подробное объяснение вынесено в отдельную статью о тремоло: два варианта формулы усиления, графики глубины модуляции и самостоятельный класс C++/JUCE. Прежняя формула с наибольшим усилением 1 находится в разделе об ослаблении.

Случайные и ступенчатые формы

Sample & hold

Кроме периодических форм в синтезаторах есть случайная. Её даёт выборка с удержанием (sample and hold, S&H)выборка с удержанием (sample and hold, S&H) — Блок, который по сигналу запуска берёт мгновенное значение входа и держит его до следующего запуска. С шумом на входе даёт случайный ступенчатый LFO.: блок берёт мгновенное значение входного сигнала по сигналу запуска и держит его до следующего запуска (Puckette, гл. 3.7). В LFO на вход подают шум, а запуск дают переносы фазы. Пакетт предлагает брать новое значение на спаде пилообразного сигнала запуска, то есть как раз при переносе фазы генератора. В каждом новом цикле выбирается новое случайное значение и держится один период 1/f1/f (Pirkle, «Designing Software Synthesizer Plugins in C++», §8.3.4, с. 113–114). Частота LFO задаёт здесь не повторение формы, а скорость смены значений: при 5 Гц новое значение приходит каждые 0,2 с (рисунок 3, сверху).

В цифровом LFO вместо отсчёта шума достаточно взять новое псевдослучайное число в момент переноса фазы. Время удержания тоже можно делать случайным: тогда случайны и уровень, и длительность ступени (Pirkle, с. 114). Вместо ступеней можно плавно переходить от одного случайного значения к другому за период. Так у LFO синтезатора Analog в Ableton Live два вида шума: один шагает между случайными значениями, другой соединяет их плавными переходами (Ableton, Analog: LFOs).

Класс Lfo из раздела о C++ превращается в случайный, если заменить nextSample и добавить генератор. Новая nextSample не принимает форму волны. Остальные функции остаются прежними; в reset нужно ещё выбрать первое значение: held = next();. Seed задаёт повторяемую последовательность, как в статье о шуме.

C++
// Sample & hold: новое случайное значение при каждом переносе фазы.
#include <random>

// В public класса Lfo вместо прежней nextSample:
double nextSample() noexcept
{
    const double value = held;
    phase += increment;
    if (phase >= pi)
    {
        phase -= 2.0 * pi;
        held = next();
    }
    return value;
}

// В private:
double next() noexcept { return distribution (engine); }

std::minstd_rand engine { 1 };  // seed 1
std::uniform_real_distribution<double> distribution { -1.0, 1.0 };
double held = 0.0;

Ступенчатые формы

Ступенчатую форму получают из обычной двумя способами (Pirkle, §8.3.5, с. 114–116). Первый — квантовать значение: выход LFO округляют до ближайшего из уровней с шагом 2/N2/N, так же как АЦП округляет напряжение (формула (3)).

wN=2N round⁡ ⁣(N (w+1)2)−1w_N = \frac{2}{N}\,\operatorname{round}\!\left(\frac{N\,(w + 1)}{2}\right) - 1
ww
исходное значение LFO от −1 до +1;
NN
число ступеней от −1 до +1, уровней на один больше;
wNw_N
значение после квантования.

При N=4N = 4 уровни равны −1; −0,5; 0; 0,5 и 1. Значение 0,3 даёт 4⋅1,3/2=2,64 \cdot 1{,}3/2 = 2{,}6, после округления 3, и wN=2⋅3/4−1=0,5w_N = 2 \cdot 3/4 - 1 = 0{,}5.

Второй способ — квантовать фазу: цикл делят на NN равных частей и держат значение формы от начала части до начала следующей. Это та же выборка с удержанием, только запуск происходит NN раз за период, а на вход подаётся сама форма. У первого способа ступени одинаковой высоты, но разной длины, у второго — одинаковой длины, но разной высоты (рисунок 3). Прямоугольную волну квантование не меняет: у неё и так два уровня (Pirkle, с. 116).

Случайный и ступенчатые LFOТри графика с общей осью времени от 0 до 1 секунды, значения от −1 до 1. Верхний: сплошная ступенчатая линия из пяти уровней по 0,2 с: 0,42; −0,73; 0,15; 0,88; −0,31; штриховая линия соединяет начала ступеней наклонными отрезками и приходит к 0,55 в конце. Средний: тонкая синусоида начинается в нуле, идёт вниз до −1, поднимается до 1 и возвращается к нулю; сплошная ступенчатая линия следует за ней уровнями −1, −0,5, 0, 0,5 и 1, у вершин ступени длиннее. Нижний: та же синусоида и ступенчатая линия из восьми ступеней по 0,125 с с уровнями 0, −0,71, −1, −0,71, 0, 0,71, 1, 0,71.sample & hold, 5 Гцквантование значения, N = 4квантование фазы, 8 частей10−110−110−100,250,50,751время, свыход LFOплавные переходыисходная синусоида
Рис. 3. Случайный и ступенчатые LFO за 1 с. Сверху: sample & hold на 5 Гц меняет случайное значение каждые 0,2 с; штриховая линия — вариант с плавными переходами между теми же значениями. Значения взяты для примера. В середине: синусоида 1 Гц, квантованная по значению при N = 4: ступени одной высоты, у вершин длиннее. Снизу: та же синусоида с фазой, разделённой на 8 частей: ступени одной длины, высоты разные.

Если ступенчатым LFO управлять высотой тона, высота меняется скачками. Pirkle сравнивает такой режим с арпеджиатором (§8.9.1, с. 118). При подходящей глубине ступени совпадают с интервалами, например с полутонами.

Ограничения простых формул

Эти формулы описывают форму волны и подходят для изучения медленного управления. Они не ограничивают спектр (spectrum)спектр (spectrum) — Представление сигнала через его частотные составляющие: их частоты, амплитуды и фазы. Ограничение спектра задаёт, какие частоты допускаются в сигнале. пилы или прямоугольника: идеальные скачки содержат гармоники (harmonics) — составляющие на частотах, кратных основной, — без конечной верхней границы. При цифровой генерации это связано с наложением спектров. У треугольника гармоники также есть, хотя его значение непрерывно.

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

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

Самопроверка

Чем отличаются синусоида 5 Гц, направленная на усиление, и синусоида 5 Гц, направленная на частоту звукового генератора?

В первом случае меняется амплитуда: получается тремоло. Во втором меняется высота тона (pitch)высота тона (pitch) — Ощущение, по которому звук воспринимают как низкий или высокий. Для чистого тона растёт с частотой; физическая частота и ощущение высоты — разные величины.: получается вибрато (vibrato)вибрато (vibrato) — Периодическое изменение высоты тона вокруг основного значения. В синтезаторе его можно получить, направив LFO на частоту звукового генератора.. Частота LFO задаёт скорость обоих изменений, а их размах задаётся преобразованием в соответствующий параметр.

Какое приращение фазы нужно для 10 Гц при вычислении на каждом кадре аудио 48 кГц?

Δφ=2π⋅10/48 000=π/2400≈0,001309\Delta\varphi = 2\pi \cdot 10 / 48\,000 = \pi/2400 \approx 0{,}001309 рад. Период занимает 4800 кадров и длится 0,1 с.

Почему для управляющего LFO в JUCE вызывается processSample(0.0), а не processSample(audioSample)?

Метод прибавляет выход генератора к аргументу. Нулевой вход даёт отдельное значение управления. Для тремоло из этого значения вычисляется неотрицательное усиление, которым затем умножается аудиоотсчёт.

Источники

  • Ableton. Live 12 Manual, Analog: LFOs — маршруты модуляции, формы, заполнение импульса, частота, перезапуск и фазовый сдвиг; два вида шума: со ступенями и с плавными переходами.
  • Ableton. Live 12 Manual, Operator — низкий и высокий частотные режимы LFO, применение к параметрам синтезатора.
  • JUCE. Oscillator, исходник 9.0.3 — функция формы, подготовка, фазовый сдвиг, прибавление к входу и сглаживание частоты.
  • JUCE. Phase, исходник 9.0.3 — накопление и перенос фазы; advance возвращает фазу до приращения.
  • JUCE. ProcessSpec — частота дискретизации, максимальный размер блока и число каналов при подготовке DSP.
  • JUCE. Introduction to DSP — жизненный цикл обработки, генерация синусоиды и пилы, таблица значений.
  • JUCE. Chorus — модуляция задержки для хоруса и фленжера.
  • Pirkle W. C. Designing Software Synthesizer Plugins in C++: With Audio DSP. §8.3.4–8.3.5, с. 113–116; §8.9.1, с. 118. в списке литературы → — случайный sample & hold с постоянным и случайным временем удержания, ступенчатые формы через квантование фазы и значения, ступенчатый LFO как арпеджиатор. Использован только алгоритм, код написан заново.
  • Puckette M. The Theory and Technique of Electronic Music. Гл. 3.7: Audio signals as control. в списке литературы → — выборка с удержанием по сигналу запуска, запуск на спаде пилы.
  • Ableton. Live 12 Manual, Drift: LFO Section — формы Sample & Hold и Wander: выборка с удержанием и плавный переход между значениями на частоте LFO.

Связи

Поиск

Ищем по названиям, тексту статей и английским терминам. Например: «частота дискретизации», «Nyquist», «аналоговый сигнал».