Осцилляторы синтезатора: формы, расстройка и саб-осциллятор

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

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

В треках:Программист, этап 6

Простой одноголосный синтезатор можно собрать из уже знакомых частей: генераторов периодических волн, шума, независимых уровней и сложения. Здесь собирается первая часть такого синтезатора — источник звука одной ноты: два основных осциллятора (oscillator)осциллятор (oscillator) — Генератор повторяющегося колебания. Частота задаёт число повторений за секунду, а форма волны — изменение значения внутри одного периода., саб-осциллятор (sub-oscillator)саб-осциллятор (sub-oscillator) — Дополнительный генератор голоса, звучащий на октаву или две ниже основной ноты. Обычно даёт синус или прямоугольник. на октаву (octave)октава (octave) — Музыкальный интервал между нотами одного названия в соседних регистрах. Соответствует отношению частот 2:1. ниже, шум и микшер. Огибающая (envelope)огибающая (envelope) — Плавная кривая, которая следует за размахом волны и показывает, как уровень сигнала меняется во времени., фильтр и запись ноты в WAV (WAVE file)WAV-файл (WAVE file) — Звуковой файл со структурой RIFF и типом формы WAVE. Чанк fmt описывает параметры звука, чанк data содержит отсчёты; обычно это несжатый PCM. разбираются отдельно; код на C++20 из этой статьи станет их входом.

Осциллятор и осциллограф

Осцилляторосциллятор (oscillator) — Генератор повторяющегося колебания. Частота задаёт число повторений за секунду, а форма волны — изменение значения внутри одного периода. генерирует повторяющееся колебание. В синтезаторе это код или схема, которая на каждом отсчёте (sample)отсчёт (sample) — Значение сигнала в один момент дискретизации, одно число. В звукозаписи его также называют сэмплом. выдаёт следующее значение волны заданной частоты (frequency)частота (frequency) — Число полных колебаний за секунду. Обозначается f и измеряется в герцах; 1 Гц означает одно колебание в секунду. и формы. Осциллограф (oscilloscope) — прибор или программа, которая рисует уже существующий сигнал во времени.

Голос и его источники

Голос синтезатора (synth voice)голос синтезатора (synth voice) — Набор генераторов и обработки, который звучит одной нотой: источники, микшер, фильтр, усилитель и огибающая. Одноголосный синтезатор играет одну ноту одновременно. — всё, что звучит одной нотой: источники, микшер, фильтр, усилитель и огибающая. Одноголосный (monophonic) синтезатор играет одну ноту одновременно. В аналоговых инструментах блоки называли по способу управления напряжением: VCO (voltage-controlled oscillator) — генератор, VCF — фильтр, VCA — усилитель. Например, у Roland SH-101 генератор, саб-осциллятор и шум сводятся в микшере источников, затем идут фильтр, усилитель и огибающая (Roland, SH-101 PLUG-OUT Owner’s Manual). Цифровой голос повторяет эту схему, хотя напряжения в нём нет.

Цепочка сигнала голосаЦепочка сигнала одного голоса сверху вниз. Осциллятор 1, осциллятор 2, саб-осциллятор и шум сходятся в микшер; дальше идут фильтр нижних частот, усилитель VCA и запись в WAV. Огибающая ADSR справа управляет VCA. Выделена часть, которую разбирает статья: источники и микшер.Выделено: источники и микшерОсц. 1Осц. 2СабШумМикшерФильтр НЧVCAADSRWAV
Рис. 1. Цепочка одного голоса. Четыре источника складываются в микшере, затем сигнал проходит фильтр нижних частот и усилитель VCA, которым управляет огибающая ADSR, и записывается в WAV. Эта статья разбирает выделенную часть: источники и микшер.

Каждый источник выдаёт числа в пределах ±1, а уровень задаётся отдельно, в микшере. Так проще оценить пик суммы: о нём — в разделе о микшере.

Своя фаза у каждого осциллятора

Генератор звукового диапазона устроен так же, как LFO: фазовый аккумулятор (phase accumulator)фазовый аккумулятор (phase accumulator) — Состояние цифрового генератора, хранящее текущую фазу колебания. На каждом временном шаге фаза увеличивается на приращение, заданное частотой генератора и частотой вычисления отсчётов; после полного оборота переносится в исходный диапазон. хранит положение внутри цикла и на каждом отсчёте увеличивает его на постоянный шаг. Здесь фаза (phase)фаза (phase) — Положение внутри периода колебания, выраженное углом: полный период соответствует 360°, или 2π радиан. Начальная фаза — её значение в момент t = 0. хранится в долях периода (period)период (period) — Время одного полного повторения периодического колебания. Обозначается T, измеряется в секундах и связано с частотой как T = 1/f.: t∈[0,1)t\in[0,1), где 0 — начало цикла, 0,5 — середина. Это та же формула приращения, поделённая на 2π2\pi.

Δt=ffs\Delta t = \frac{f}{f_s}
Δt\Delta t
приращение фазы за отсчёт, доли периода;
ff
частота осциллятора, Гц;
fsf_s

частота дискретизациичастота дискретизации (sample rate) — Число отсчётов в каждом канале за одну секунду, то же число кадров аудиоданных в секунду. Измеряется в герцах: 44 100 Гц означает 44 100 отсчётов на канал в секунду., Гц.

Для ноты 220 Гц при 48 кГц шаг равен 220/48 000≈0,0045833220/48\,000\approx0{,}0045833, а период занимает 48 000/220≈218,1848\,000/220\approx218{,}18 отсчёта. Дробное число отсчётов в периоде не мешает: после переноса фазы сохраняется избыток, и в среднем частота точная. Отличие от LFO (low-frequency oscillator, LFO)низкочастотный генератор (low-frequency oscillator, LFO) — Генератор медленно повторяющегося управляющего сигнала, которым изменяют параметры звука, например усиление, частоту или панораму. Общей строгой верхней границы частоты у LFO нет. — в диапазоне: частота теперь звуковая, но должна оставаться ниже fs/2f_s/2, иначе шаг больше половины цикла.

Одного аккумулятора на голос мало: если два осциллятора читают одну фазу, у них одна частота; из общего tt можно получить другую форму, но не 220,89 Гц рядом с 220 Гц. Небольшая расстройка (detune)расстройка (detune) — Небольшое отклонение частоты одного генератора от другого или от точного интервала, обычно в центах. При одновременном звучании даёт биения. требует другого шага, то есть отдельного накопления. Отдельное состояние также позволяет задать каждому осциллятору собственную начальную фазу при начале ноты. Поэтому в коде каждый осциллятор — отдельный объект со своей фазой, а частота и форма — его параметры.

Формы в звуковом диапазоне и алиасинг

Формулы форм те же, что в статье об LFO, только записаны через долю периода. Для звука важнее спектр (spectrum)спектр (spectrum) — Представление сигнала через его частотные составляющие: их частоты, амплитуды и фазы. Ограничение спектра задаёт, какие частоты допускаются в сигнале.: у периодической волны с частотой ff есть составляющие на частотах kfkf — гармоники с номерами k=1,2,3,…k = 1, 2, 3, \ldots Их амплитуды (amplitude)амплитуда (amplitude) — Наибольший модуль отклонения колеблющейся величины от равновесного значения. Для синусоиды обозначается A; единицы совпадают с единицами сигнала. для размаха ±1 приведены в таблице; знаки и фазы гармоник опущены. Такие ряды выводит Пакетт для элементарных волн (Puckette, §10.2); у него другая нормировка размаха, поэтому множители отличаются постоянным коэффициентом.

Таблица 1. Формы из фазы t ∈ [0, 1) и их гармоники при размахе ±1.
ФормаЗначениеГармоникиАмплитуда k-й гармоники
Синусsin 2πtтолько k = 11
Треугольник1 − 4|t − 0,5|нечётные8/(π²k²)
Пила2t − 1все2/(πk)
Прямоугольник+1 при t < 0,5, иначе −1нечётные4/(πk)

Выбор формы основного осциллятора задаёт исходный тембр (timbre)тембр (timbre) — Свойство слухового ощущения, по которому различают два звука одинаковой высоты и громкости. Зависит от состава спектра и его изменения во времени.. Пила содержит все гармоники и звучит ярко, прямоугольник — только нечётные, синус (sine)синус (sine) — Вертикальная координата точки на единичной окружности под заданным углом от положительного направления горизонтальной оси. Для острого угла прямоугольного треугольника равен отношению противолежащего катета к гипотенузе. — одну составляющую. Треугольник тоже содержит нечётные гармоники, но их амплитуды убывают как 1/k21/k^2, и звук мягче. Для субтрактивного синтеза, где следом стоит фильтр, обычно берут формы с богатым спектром: фильтру есть что ослаблять.

У идеальной пилы гармоники продолжаются без верхней границы. Если вычислять её значения прямо по формуле, каждый отсчёт совпадёт с отсчётом идеальной непрерывной пилы. Значит, дискретизируются и все гармоники выше fs/2f_s/2, а наложение спектров (aliasing)наложение спектров (aliasing) — Наложение частотных составляющих при дискретизации: разные непрерывные сигналы дают одинаковые отсчёты. Частота выше границы Найквиста может проявиться как более низкая. переносит каждую из них в полосу от 0 до fs/2f_s/2.

fa={r,r≤fs/2,fs−r,r>fs/2,r=kf mod fsf_\text{a} = \begin{cases} r, & r \le f_s/2, \\ f_s - r, & r > f_s/2, \end{cases} \qquad r = kf \bmod f_s
faf_\text{a}
частота, на которой окажется гармоника после дискретизации, Гц;
rr
остаток от деления kfkf на fsf_s, Гц.

Остаток от деления убирает целое число fsf_s, а вторая строка зеркально отражает частоты верхней половины. При 48 кГц граница fs/2f_s/2 равна 24 кГц. Таблица сравнивает пилу на низкой ноте A2 (110 Гц) и на высокой A6 (1760 Гц); уровни указаны относительно основной гармоники и следуют из закона 1/k1/k.

Таблица 2. Гармоники простой пилы выше 24 кГц при 48 кГц: куда они отражаются и насколько они слабее основной.
НотаПоследняя гармоника ниже 24 кГцПервая выше 24 кГцПервое отражение ниже 1 кГц
A2, 110 Гцk = 218, 23 980 Гцk = 219: 24 090 → 23 910 Гц, −46,8 дБk = 428: 47 080 → 920 Гц, −52,6 дБ
A6, 1760 Гцk = 13, 22 880 Гцk = 14: 24 640 → 23 360 Гц, −22,9 дБk = 27: 47 520 → 480 Гц, −28,6 дБ

На низкой ноте за границу выходят гармоники с большими номерами: они слабые, а первые отражения ложатся у самой верхней границы слуха. На высокой ноте за границу уходит уже 14-я гармоника. Её отражение 23 360 Гц почти неслышно, но 27-я попадает на 480 Гц — ниже основной частоты — и слабее неё всего на 28,6 дБ. Частота 480 Гц не кратна 1760 Гц, поэтому отражение звучит как посторонний, не связанный с нотой тон. При повышении ноты 48 000−27f48\,000 - 27f уменьшается: этот тон движется вниз, в противоположную сторону. Пакетт оценивает отражения пилы 440 Гц при 44,1 кГц примерно в −32 дБ относительно основной и называет их вполне слышимыми (Puckette, §10.4).

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

Синусу это не нужно: у него одна гармоника. У треугольника нет скачка, только излом, и гармоники убывают как 1/k21/k^2. На A6 его 27-я гармоника слабее основной на 20lg⁡(1/272)≈−57,320\lg(1/27^2)\approx-57{,}3 дБ, вместо −28,6 дБ у пилы; Пакетт для 440 Гц приводит такое же удвоение: около −64 дБ вместо −32 дБ. Излом тоже можно исправлять: вместо поправки к ступеньке используют поправку к излому — интеграл ступеньки с ограниченной полосой (Välimäki, Pekonen, Nam, разд. II). В этой статье треугольник оставлен простым.

PolyBLEP: поправка у скачка

Скачок пилы — ступенька. Её вариант с ограниченной полосой (bandlimited step, BLEP) поднимается плавно, с колебаниями вокруг перехода. Её разность с идеальной ступенькой заметна только рядом со скачком. Если к отсчётам около скачка прибавить эту разность, получится приблизительно ограниченная по полосе волна. Точную разность можно хранить таблицей; PolyBLEP (polynomial bandlimited step)PolyBLEP (polynomial bandlimited step) — Способ ослабить алиасинг генератора со скачками: к отсчётам рядом со скачком прибавляется полиномиальная поправка к ступеньке с ограниченной полосой. заменяет её многочленом. Простейший вариант — интеграл линейной интерполяции: поправка второго порядка меняет только два отсчёта, по одному с каждой стороны скачка. Его предложили Välimäki и Huovilainen; формулы и сравнение с другими вариантами даны в статье Välimäki, Pekonen, Nam (разд. III, табл. I).

p(t)={2x−x2−1,x=t/Δt,t<Δt,x2+2x+1,x=(t−1)/Δt,t>1−Δt,0,иначеp(t) = \begin{cases} 2x - x^2 - 1, & x = t/\Delta t, \quad t < \Delta t, \\ x^2 + 2x + 1, & x = (t - 1)/\Delta t, \quad t > 1 - \Delta t, \\ 0, & \text{иначе} \end{cases}
p(t)p(t)
поправка для скачка вверх на 2 в точке t=0t = 0, без единиц;
xx
расстояние от скачка, выраженное в шагах фазы Δt\Delta t.

Первая строка относится к отсчёту сразу после скачка, вторая — к последнему отсчёту перед ним; оба значения зависят от того, где между отсчётами пришёлся скачок. На краях поправка равна нулю и плавно переходит в обычную форму. У пилы скачок направлен вниз, поэтому поправка вычитается: 2t−1−p(t)2t - 1 - p(t). У прямоугольника два скачка: вверх при t=0t = 0 и вниз при t=0,5t = 0{,}5, поэтому к нему прибавляется p(t)p(t) и вычитается p((t+0,5) mod 1)p\bigl((t + 0{,}5) \bmod 1\bigr).

Пила около скачка без поправки и с PolyBLEPНепрерывная пила дважды падает с плюс единицы до минус единицы. Отсчёты без поправки показаны кольцами, с поправкой — точками. У отсчёта 7 значение уменьшено с 0,95 до 0,43, у отсчёта 8 — увеличено с минус 0,86 до минус 0,78; так же изменены отсчёты 17 и 18. Остальные точки совпадают с кольцами.без поправкис PolyBLEPнепрерывная пила10−10510152025Номер отсчёта n
Рис. 2. Пила с периодом 10,4 отсчёта. Поправка изменила только по одному отсчёту с каждой стороны скачка: 7 и 8 у первого, 17 и 18 у второго; остальные совпадают с простой формулой. Пунктир соединяет исходное и исправленное значение.

Метод требует знать шаг Δt\Delta t и положение скачка — оба следуют из фазы. Условие применимости: шаг должен быть заметно меньше половины периода. При f>fs/4f > f_s/4 поправки двух скачков прямоугольника перекрываются, и метод теряет точность.

PolyBLEP ослабляет алиасинг, но не устраняет его. По спектру визуализации ниже для пилы A6 при 48 кГц отражение на 480 Гц падает с −32,5 дБ ниже −100 дБ, на 2240 Гц — с −32,7 до −85 дБ, на 11 040 Гц — только с −30,6 до −53 дБ. У самой границы fs/2f_s/2 выигрыш ещё меньше: отражение на 23 360 Гц ослабляется лишь на 8 дБ. Поправка слегка ослабляет и разрешённые высокие гармоники: 13-я гармоника A6 (22 880 Гц) становится тише примерно на 7 дБ. По модели маскировки (auditory masking)слуховая маскировка (auditory masking) — Снижение слышимости одного звука из-за присутствия другого. Из-за маскировки восприятие сложного звука нельзя определить только по кривым равной громкости чистых тонов. из той же статьи двухточечная поправка для пилы при 44,1 кГц оставляет отражения ниже порога слышимости (threshold of hearing)порог слышимости (threshold of hearing) — Минимальный уровень звука, который слушатель способен обнаружить в заданных условиях. Зависит от частоты и слушателя; опорные 20 мкПа не задают его для всех частот. примерно до основной частоты 2,1 кГц. Четырёхточечная поправка на основе B-сплайна сохраняет это свойство примерно до 7,8 кГц (Välimäki, Pekonen, Nam, табл. VIII).

Частота ноты

Клавиатура и MIDI передают синтезатору номер ноты — целое число от 0 до 127. Номер 69 соответствует A4 (ля первой октавы, 440 Гц), 60 — C4 (до первой октавы). Каждый шаг номера — полутон, то есть множитель 21/122^{1/12} к частоте в равномерной темперации.

f=440⋅2(m−69)/12f = 440 \cdot 2^{(m - 69)/12}
mm
номер ноты MIDI;
ff
частота ноты при настройке A4 = 440 Гц, Гц.

Для m=57m = 57 (A3) показатель равен −12/12=−1-12/12 = -1, и f=220f = 220 Гц. Для C4 (m=60m = 60) получается 440⋅2−9/12≈261,63440\cdot2^{-9/12} \approx 261{,}63 Гц, для A6 (m=93m = 93) — 440⋅22=1760440\cdot2^{2} = 1760 Гц. Ту же формулу использует juce::MidiMessage::getMidiNoteInHertz с параметром частоты A (исходник JUCE 9.0.3). При другой настройке, например A4 = 442 Гц, меняется только первый множитель.

Интервалы и расстройка

Второй осциллятор обычно звучит на заданном музыкальном интервале (musical interval)музыкальный интервал (musical interval) — Расстояние между высотами двух нот. Интервал называют мелодическим при последовательном звучании нот и гармоническим при одновременном. от первого. В унисоне частоты равны. Тогда результат зависит от разности фаз, как при сложении двух источников одной частоты. Если обе пилы стартуют одновременно с одной фазой, сумма — та же пила с удвоенным уровнем. Если вторую сдвинуть на полпериода, нечётные гармоники компенсируются: (2t−1)+(2((t+0,5) mod 1)−1)(2t - 1) + (2((t + 0{,}5) \bmod 1) - 1) — пила с удвоенной частотой, то есть на октаву выше.

Интервалмузыкальный интервал (musical interval) — Расстояние между высотами двух нот. Интервал называют мелодическим при последовательном звучании нот и гармоническим при одновременном. из nn полутонов умножает частоту на 2n/122^{n/12}. Октаваоктава (octave) — Музыкальный интервал между нотами одного названия в соседних регистрах. Соответствует отношению частот 2:1. — 12 полутонов, множитель 2. Квинта — 7 полутонов, 27/12≈1,4983072^{7/12} \approx 1{,}498307, а не точное отношение 3/2: темперированная квинта ниже чистой примерно на 1,955 цента (cent)цент (cent) — Сотая часть равномерно темперированного полутона; в октаве 1200 центов. Интервал в c центов умножает частоту на 2 в степени c/1200.. От A3 = 220 Гц она даёт 329,63 Гц вместо 330 Гц.

Расстройкарасстройка (detune) — Небольшое отклонение частоты одного генератора от другого или от точного интервала, обычно в центах. При одновременном звучании даёт биения. — небольшое отклонение от точного интервала, обычно в центахцент (cent) — Сотая часть равномерно темперированного полутона; в октаве 1200 центов. Интервал в c центов умножает частоту на 2 в степени c/1200.. Цент — сотая часть полутона (подробнее о центах).

f2=f1⋅2(100n+c)/1200f_2 = f_1 \cdot 2^{(100n + c)/1200}
f1f_1, f2f_2
частоты первого и второго осцилляторов, Гц;
nn
интервал, полутоны: 0 — унисон, 7 — квинта, 12 — октава;
cc
расстройка, центы.

Расстройка на +7 центов от 220 Гц даёт 220⋅27/1200≈220,89220 \cdot 2^{7/1200} \approx 220{,}89 Гц.

Биения

Два тона близких частот попеременно складываются в фазе и в противофазе. Громкость (loudness)громкость (loudness) — Ощущение того, насколько сильным кажется звук. Зависит от звукового давления, частотного состава и условий восприятия. суммы нарастает и спадает с частотой биений (beats)биения (beats) — Периодическое нарастание и спад громкости суммы двух тонов близких частот. Частота биений равна модулю разности частот. ∣f1−f2∣|f_1 - f_2|. Для 220 и 220,89 Гц это 0,89 Гц: примерно одна пульсация за 1,12 с. Такие биениябиения (beats) — Периодическое нарастание и спад громкости суммы двух тонов близких частот. Частота биений равна модулю разности частот. и есть движение, которое слышно в двух слегка расстроенных осцилляторах.

У пилы расходятся все гармоники, и каждая kk-я биёт со своей частотой k∣f1−f2∣k|f_1 - f_2|: 10-я — 8,9 Гц, 20-я — 17,8 Гц, 50-я — 44,6 Гц. Поэтому в звуке двух расстроенных пил движение идёт сразу на разных скоростях: низкие гармоники колеблются медленно, а у высоких изменения быстрые и сливаются в шероховатость (roughness)шероховатость (roughness) — Ощущение дрожащего, грубого характера звука при быстрых изменениях его амплитуды или частоты. Зависит от скорости изменений, глубины и спектра.. Для тона 1 кГц с амплитудной модуляцией (modulation)модуляция (modulation) — Изменение параметра одного сигнала под действием другого. Например, управляющий сигнал LFO может периодически менять амплитуду звука или частоту генератора. пульсация выражена сильнее всего около 4 Гц, а шероховатость начинает нарастать около 15 Гц. Для гармоник сложного звука эти числа служат ориентиром, а не точной границей.

Темперированная квинта тоже даёт биения без расстройки. Третья гармоника A3 (660 Гц) и вторая гармоника квинты (2⋅329,63≈659,262 \cdot 329{,}63 \approx 659{,}26 Гц) расходятся на 0,75 Гц. У октавы при равных частотах вторая гармоника нижнего тона совпадает с основной верхнего, и биений нет, пока нет расстройки.

Саб-осциллятор

Саб-осцилляторсаб-осциллятор (sub-oscillator) — Дополнительный генератор голоса, звучащий на октаву или две ниже основной ноты. Обычно даёт синус или прямоугольник. звучит на октаву ниже ноты: sub_frequency = note_frequency / 2. У SH-101 его можно переключить на одну или две октавы вниз (Roland, SH-101 PLUG-OUT Owner’s Manual). Для начала подойдут две формы: синус добавляет низкую основу без новых гармоник, прямоугольник — нечётные гармоники половинной частоты и более плотный низ.

Есть два способа получить его фазу. Первый — отдельный аккумулятор с половинной частотой: это ещё один объект того же класса. Второй — делить фазу основного осциллятора. При каждом переносе фазы основного осциллятора переключается флаг, и фаза саба равна (t+флаг)/2(t + \text{флаг})/2: на чётных циклах основного осциллятора она растёт от 0 до 0,5, на нечётных — от 0,5 до 1. Саб тогда всегда привязан к первому осциллятору, даже если его частота плавно меняется. Шаг для PolyBLEP саба — Δt/2\Delta t/2.

C++
// Фаза саба из фазы основного: флаг меняется при каждом переносе.
double subPhase (double phase, bool oddCycle) noexcept
{
    return 0.5 * (phase + (oddCycle ? 1.0 : 0.0));
}

В примере ниже используется первый способ: при общем сбросе и постоянных частотах оба способа дают одну и ту же волну. Общий сброс фаз в начале ноты (note on) делает каждую ноту одинаковой с первого отсчёта. Начальная фаза определяет, с какого значения начинается волна и как совпадают скачки саба и основных осцилляторов. Прямоугольник, начатый с фазы 0, сразу выдаёт +1. Без огибающей переход из тишины к +1 даст щелчок; в голосе его сглаживает огибающая VCA. Если фазы не сбрасывать (free-running), каждая нота начнётся с другого сочетания фаз, и её атака будет звучать немного иначе.

Шум как четвёртый источник

Шум (noise signal)шумовой сигнал (noise signal) — Сигнал со случайными или псевдослучайными изменениями; обычный фрагмент не имеет одной повторяющейся периодической формы. не периодичен: у него нет основной частоты и гармоник, поэтому он не зависит от номера ноты. Его добавляют для дыхания, шипения и атаки ударных звуков. Генераторы белого (white noise)белый шум (white noise) — Шум с постоянной спектральной плотностью мощности в рассматриваемой полосе. Полосы одинаковой ширины в герцах содержат одинаковую ожидаемую мощность. и розового (pink noise)розовый шум (pink noise) — Шум, PSD которого приблизительно обратно пропорциональна частоте в рабочей полосе: спад около 3 дБ на октаву и примерно одинаковая мощность октав. шума и их состояние между аудиоблоками разобраны в статье о шуме; здесь они используются без изменений. Оба генератора выдают значения в пределах ±1, поэтому шум в микшере оценивается так же, как остальные источники. У розового шума Voss–McCartney типичные пики заметно ниже 1.

Микшер голоса

Микшер умножает каждый источник на собственный уровень и складывает: y[n]=g1x1[n]+g2x2[n]+g3x3[n]+g4x4[n]y[n] = g_1x_1[n] + g_2x_2[n] + g_3x_3[n] + g_4x_4[n]. Уровни независимы, как усиление отдельных источников; соотношение задаёт тембр, а сумма — пик перед следующей стадией.

∣y[n]∣≤∑i=14∣gi∣⋅peaki|y[n]| \le \sum_{i=1}^{4} |g_i| \cdot \text{peak}_i
gig_i
уровень ii-го источника, без единиц;
peaki\text{peak}_i
наибольший модуль выхода источника; здесь не больше 1.

Это неравенство для модуля суммы: худший случай, когда все пики совпадают по времени и знаку. С расстройкой фазы осцилляторов медленно перебирают все сочетания, поэтому рано или поздно пики совпадут. Для уровней 0,35, 0,35, 0,2 и 0 оценка равна 0,9, то есть −0,92 dBFS (decibels relative to full scale)dBFS (decibels relative to full scale) — Уровень цифрового сигнала в децибелах относительно полной шкалы. Для пиковых значений 0 dBFS соответствует её границе; формат с плавающей точкой допускает значения выше неё. Соглашение об опоре RMS указывают отдельно.: запас уровня (headroom)запас уровня (headroom) — Расстояние от текущего или ожидаемого пикового уровня до предела выбранного участка аудиотракта. Обычно выражается в децибелах. до полной шкалы (full scale)полная шкала (full scale) — Опорная граница уровня выбранного аудиоформата или тракта. Для нормированных аудиоотсчётов обычно принята за ±1; числовой формат с плавающей точкой способен хранить значения за этой границей. около 0,92 дБ. Измеренный пик за первые 2 с — 0,892, почти столько же. Шум с уровнем 0,1 доводит оценку до 1, то есть до 0 dBFS, и запаса не остаётся.

Запас нужен до фильтра и VCA, а не только на выходе. Внутри вычислений с плавающей точкой сумма больше 1 сохраняется. Но если следующая стадия ограничивает сигнал или сохраняет его в формат с полной шкалой, вершины будут срезаны. Распределение усиления по тракту (gain staging)распределение усиления по тракту (gain staging) — Выбор уровней на отдельных этапах аудиотракта с учётом пиков, запаса и допустимого диапазона каждого этапа. требует оценивать пик после каждой стадии: фильтр может изменить форму волны, а VCA с огибающей не выше 1 пик не увеличивает. Простое правило — выбрать уровни так, чтобы ∑∣gi∣≤1\sum |g_i| \le 1, и уменьшать их пропорционально при добавлении источника.

Собрать и послушать

Визуализация считает голос тем же кодом, что приведён ниже, при 48 кГц. Сверху — четыре периода суммы от начала ноты, снизу — её спектр с логарифмической шкалой (logarithmic scale)логарифмическая шкала (logarithmic scale) — Шкала, на которой равные расстояния соответствуют одинаковым отношениям положительных величин. Например, 100, 1000 и 10 000 Гц расположены на равных расстояниях. частот. Полоса от 20 Гц до 5 кГц, где слух особенно чувствителен, разделена на шесть частей с равным отношением частот, и кольцо отмечает самую сильную отражённую составляющую в каждой. Отражения ближе 18 Гц к гармоникам ноты не отмечаются: на таком спектре их не отличить. Звук считается тем же кодом непрерывно. При смене формы или PolyBLEP он затихает и возвращается за 20 мс, уровни меняются плавно за то же время.

Визуализация 3Источники голоса: сумма, спектр и пик
Сумма источников голоса во времени и её спектр Сумма источников одного голоса при 48 кГц. Осциллятор 1 — пила, уровень 0,35; осциллятор 2 — пила, 0,35, унисон и +7 центов; саб-осциллятор — прямоугольник, 0,20; шум выключен; PolyBLEP включён. Сверху четыре периода ноты после общего сброса фаз, снизу спектр первых 16 384 отсчётов с окном Блэкмана — Харриса. Кольца отмечают отражённые составляющие ниже 5 кГц: самую сильную в каждой из шести частей полосы. Звук считается тем же кодом. Пик суммы за первые 2 с: 0,892 (−0,99 dBFS). Худшая оценка Σ|gᵢ|: 0,90 (−0,92 dBFS). Сильнейшая отражённая составляющая ниже 5 кГц: 4 926 Гц, −97,3 дБ — 195-я гармоника (осциллятор 2, 43 074 Гц). PolyBLEP включён. Сумма во времени, без единиц −2−1012 0 18,2 мс Спектр суммы, дБ относительно амплитуды 1 0−20−40−60−80−100201001 к10 к24 к Частота, Гц
  • сумма
  • g₁·осциллятор 1
  • g₂·осциллятор 2
  • g₃·саб
  • g₄·шум
  • полная шкала ±1
  • спектр суммы
  • отражённая составляющая

Нота A3, 220,00 Гц. Осциллятор 2: 220,89 Гц (унисон, +7 центов); биения основных частот — 0,89 Гц. Саб-осциллятор: 110,00 Гц.

Пик суммы за первые 2 с: 0,892 (−0,99 dBFS). Худшая оценка Σ|gᵢ|: 0,90 (−0,92 dBFS).

Сумма помещается в ±1 при любом сочетании фаз.

Сильнейшая отражённая составляющая ниже 5 кГц: 4 926 Гц, −97,3 дБ — 195-я гармоника (осциллятор 2, 43 074 Гц). PolyBLEP включён.

Параметры источников голоса
A3, 220,00 Гц
Осциллятор 1
0,35
Осциллятор 2
0,35
+7 центов
Саб-осциллятор
0,20
Шум
0,00

Громкость. Голос звучит непрерывно, пока его не остановить. Высокие ноты, пила и прямоугольник без PolyBLEP дают резкие высокие составляющие: перед запуском убавьте громкость, особенно в наушниках.

30 %

Остановлен

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

Рис. 3. Сумма источников одного голоса при 48 кГц. Осциллятор 1 — пила, уровень 0,35; осциллятор 2 — пила, 0,35, унисон и +7 центов; саб-осциллятор — прямоугольник, 0,20; шум выключен; PolyBLEP включён. Сверху четыре периода ноты после общего сброса фаз, снизу спектр первых 16 384 отсчётов с окном Блэкмана — Харриса. Кольца отмечают отражённые составляющие ниже 5 кГц: самую сильную в каждой из шести частей полосы. Звук считается тем же кодом.

Пример «A6 без PolyBLEP» показывает слышимые отражения ниже основной; включение PolyBLEP опускает кольца. «Квинта без расстройки» даёт медленные биения 0,75 Гц от темперированной квинты, а «Перегрузка» — сумму выше 0 dBFS и предупреждение. Попробуйте также расстройку 0 и 25 центов в унисоне и сравните движение звука.

Код на C++

Код использует стандартную библиотеку C++20 и повторяет устройство LFO на C++: параметры отдельно от изменяемого состояния, подготовка, сброс и получение одного отсчёта. Первый фрагмент переводит фазу в значение формы и добавляет PolyBLEP.

C++
// Формы из фазы t ∈ [0, 1) и поправка PolyBLEP около скачков.
#include <cassert>
#include <cmath>
#include <numbers>

enum class Waveform { sine, triangle, saw, square };

// Поправка для скачка вверх на 2 в точке t = 0; dt — шаг фазы за отсчёт.
double polyBlep (double t, double dt) noexcept
{
    if (t < dt) // отсчёт сразу после скачка
    {
        t /= dt;
        return t + t - t * t - 1.0;
    }
    if (t > 1.0 - dt) // отсчёт перед скачком
    {
        t = (t - 1.0) / dt;
        return t * t + t + t + 1.0;
    }
    return 0.0;
}

double shapeValue (Waveform shape, double t, double dt, bool antialias) noexcept
{
    switch (shape)
    {
        case Waveform::sine:
            return std::sin (2.0 * std::numbers::pi * t);
        case Waveform::triangle:
            return 1.0 - 4.0 * std::abs (t - 0.5);
        case Waveform::saw: // скачок вниз на 2: поправка вычитается
            return 2.0 * t - 1.0 - (antialias ? polyBlep (t, dt) : 0.0);
        case Waveform::square: // вверх при t = 0, вниз при t = 0,5
        {
            double value = t < 0.5 ? 1.0 : -1.0;
            if (antialias)
                value += polyBlep (t, dt) - polyBlep (std::fmod (t + 0.5, 1.0), dt);
            return value;
        }
    }
    return 0.0;
}

Класс Oscillator

Класс хранит частоту, форму и признак поправки отдельно от фазы. prepare задаёт частоту дискретизации, reset — начальную фазу, setFrequency меняет шаг без сброса фазы, nextSample выдаёт одно значение и переносит фазу с сохранением избытка. Условия: конечная положительная fsf_s и частота от 0 до fs/2f_s/2, не включая границу; проверку пользовательского ввода выполнить до вызова.

C++
// Параметры осциллятора отдельно от состояния — фазы в долях периода.
class Oscillator
{
public:
    void prepare (double newSampleRate) noexcept
    {
        assert (std::isfinite (newSampleRate) && newSampleRate > 0.0);
        sampleRate = newSampleRate;
        setFrequency (frequency);
        reset();
    }

    void reset (double startPhase = 0.0) noexcept
    {
        assert (startPhase >= 0.0 && startPhase < 1.0);
        phase = startPhase;
    }

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

    void setWaveform (Waveform value) noexcept { waveform = value; }
    void setAntialiasing (bool value) noexcept { antialias = value; }

    double nextSample() noexcept
    {
        const double value = shapeValue (waveform, phase, increment, antialias);
        phase += increment;
        if (phase >= 1.0)
            phase -= 1.0;
        return value;
    }

private:
    // Параметры: меняются по нотам и из интерфейса.
    double sampleRate = 48000.0;
    double frequency = 440.0;
    double increment = frequency / sampleRate;
    Waveform waveform = Waveform::saw;
    bool antialias = true;
    // Состояние: меняется на каждом отсчёте.
    double phase = 0.0;
};

Источники и микшер

VoiceSources содержит три осциллятора и шум. Уровни переданы отдельной структурой VoiceLevels, которая также считает худшую оценку пика. Класс WhiteNoise — из статьи о шуме; для розового шума его можно заменить на PinkNoise или на переключаемый источник оттуда же.

C++
// Четыре источника голоса и микшер с независимыми уровнями.
#include <initializer_list>

double noteFrequency (int midiNote) noexcept
{
    return 440.0 * std::exp2 ((midiNote - 69) / 12.0);
}

struct VoiceLevels
{
    double osc1 = 0.35, osc2 = 0.35, sub = 0.2, noise = 0.0;

    // Каждый источник не выходит за ±1, поэтому пик суммы не больше Σ|g|.
    double worstPeak() const noexcept
    {
        return std::abs (osc1) + std::abs (osc2) + std::abs (sub) + std::abs (noise);
    }
};

class VoiceSources
{
public:
    void prepare (double sampleRate) noexcept
    {
        for (auto* osc : { &osc1, &osc2, &sub })
            osc->prepare (sampleRate);
    }

    // Интервал второго осциллятора в полутонах и расстройка в центах.
    void noteOn (int midiNote, int semitones, double cents) noexcept
    {
        const double f = noteFrequency (midiNote);
        osc1.setFrequency (f);
        osc2.setFrequency (f * std::exp2 ((100.0 * semitones + cents) / 1200.0));
        sub.setFrequency (f / 2.0);
        for (auto* osc : { &osc1, &osc2, &sub })
            osc->reset();
    }

    double nextSample (const VoiceLevels& g) noexcept
    {
        return g.osc1 * osc1.nextSample() + g.osc2 * osc2.nextSample()
             + g.sub * sub.nextSample() + g.noise * noise.nextSample();
    }

    Oscillator osc1, osc2, sub; // форма — через setWaveform
    WhiteNoise noise { 2026 };
};

После prepare (48000.0) задайте формы, например voice.sub.setWaveform (Waveform::square), затем вызовите noteOn (57, 0, 7.0): A3, унисон и +7 центов. Каждый вызов nextSample (levels) возвращает следующий отсчёт суммы. Деление (midiNote - 69) / 12.0 выполняется в double, потому что делитель вещественный. Для примера выше levels.worstPeak() равно 0,9. Пример собран clang++ -std=c++20 с заглушкой шума и даёт те же отсчёты, что визуализация.

Голос в аудиоблоке JUCE

juce::dsp::Oscillator подходит для синуса, но для PolyBLEP неудобен: функция формы получает только фазу, а поправке нужен шаг Δt\Delta t и положение скачка. Шаг при этом может меняться внутри блока из-за встроенного сглаживания частоты (исходник Oscillator 9.0.3). Собственный класс хранит шаг рядом с фазой.

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

C++
// Один отсчёт голоса на кадр; буфер перезаписывается.
#include <juce_audio_basics/juce_audio_basics.h>

void renderVoice (VoiceSources& voice, const VoiceLevels& levels,
                  juce::AudioBuffer<float>& output) noexcept
{
    for (int n = 0; n < output.getNumSamples(); ++n)
    {
        const auto sample = static_cast<float> (voice.nextSample (levels));
        for (int channel = 0; channel < output.getNumChannels(); ++channel)
            output.setSample (channel, n, sample);
    }
}

Объект VoiceSources живёт в состоянии процессора; prepare вызывается из prepareToPlay, noteOn — по событию ноты. Смена формы и PolyBLEP между блоками мгновенна, а для плавного перехода во время звучания нужно сглаживание, как в визуализации. Перед записью в целочисленный формат или выводом проверьте пик: о защите выхода — в статье о микшировании.

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

Почему два осциллятора голоса не могут читать одну общую фазу, если нужна расстройка?

Общая фаза растёт с одним шагом, значит частота у обоих одинакова. Расстройка на несколько центов требует другого шага Δt=f/fs\Delta t = f/f_s и, следовательно, отдельного накопления. Отдельное состояние также позволяет задать каждому собственную начальную фазу.

Нота A5 (880 Гц) звучит простой пилой при 48 кГц. Какая гармоника последней помещается ниже 24 кГц и куда отразится следующая?

24 000/880≈27,324\,000/880 \approx 27{,}3, поэтому последняя — 27-я, 23 760 Гц. 28-я гармоника 24 640 Гц отразится в 48 000−24 640=23 36048\,000 - 24\,640 = 23\,360 Гц. Её амплитуда — 1/28 от основной, около −28,9 дБ.

Уровни источников 0,5, 0,5, 0,3 и 0,2. Каков худший пик и как вернуть его в ±1, сохранив соотношение?

0,5+0,5+0,3+0,2=1,50{,}5 + 0{,}5 + 0{,}3 + 0{,}2 = 1{,}5, это около +3,52 dBFS. Если умножить все уровни на 1/1,5≈0,6671/1{,}5 \approx 0{,}667, получатся 0,333, 0,333, 0,2 и 0,133. Сумма модулей станет равна 1, а соотношение источников сохранится.

Источники

Связи

Поиск

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