Простой одноголосный синтезатор можно собрать из уже знакомых частей: генераторов периодических волн, шума, независимых уровней и сложения. Здесь собирается первая часть такого синтезатора — источник звука одной ноты: два основных осциллятора (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, а уровень задаётся отдельно, в микшере. Так проще оценить пик суммы: о нём — в разделе о микшере.
Своя фаза у каждого осциллятора
Генератор звукового диапазона устроен так же, как LFO: фазовый аккумулятор (phase accumulator)фазовый аккумулятор (phase accumulator) — Состояние цифрового генератора, хранящее текущую фазу колебания. На каждом временном шаге фаза увеличивается на приращение, заданное частотой генератора и частотой вычисления отсчётов; после полного оборота переносится в исходный диапазон. хранит положение внутри цикла и на каждом отсчёте увеличивает его на постоянный шаг. Здесь фаза (phase)фаза (phase) — Положение внутри периода колебания, выраженное углом: полный период соответствует 360°, или 2π радиан. Начальная фаза — её значение в момент t = 0. хранится в долях периода (period)период (period) — Время одного полного повторения периодического колебания. Обозначается T, измеряется в секундах и связано с частотой как T = 1/f.: , где 0 — начало цикла, 0,5 — середина. Это та же формула приращения, поделённая на .
- приращение фазы за отсчёт, доли периода;
- частота осциллятора, Гц;
частота дискретизациичастота дискретизации (sample rate) — Число отсчётов в каждом канале за одну секунду, то же число кадров аудиоданных в секунду. Измеряется в герцах: 44 100 Гц означает 44 100 отсчётов на канал в секунду., Гц.
Для ноты 220 Гц при 48 кГц шаг равен , а период занимает отсчёта. Дробное число отсчётов в периоде не мешает: после переноса фазы сохраняется избыток, и в среднем частота точная. Отличие от LFO (low-frequency oscillator, LFO)низкочастотный генератор (low-frequency oscillator, LFO) — Генератор медленно повторяющегося управляющего сигнала, которым изменяют параметры звука, например усиление, частоту или панораму. Общей строгой верхней границы частоты у LFO нет. — в диапазоне: частота теперь звуковая, но должна оставаться ниже , иначе шаг больше половины цикла.
Одного аккумулятора на голос мало: если два осциллятора читают одну фазу, у них одна частота; из общего можно получить другую форму, но не 220,89 Гц рядом с 220 Гц. Небольшая расстройка (detune)расстройка (detune) — Небольшое отклонение частоты одного генератора от другого или от точного интервала, обычно в центах. При одновременном звучании даёт биения. требует другого шага, то есть отдельного накопления. Отдельное состояние также позволяет задать каждому осциллятору собственную начальную фазу при начале ноты. Поэтому в коде каждый осциллятор — отдельный объект со своей фазой, а частота и форма — его параметры.
Формы в звуковом диапазоне и алиасинг
Формулы форм те же, что в статье об LFO, только записаны через долю периода. Для звука важнее спектр (spectrum)спектр (spectrum) — Представление сигнала через его частотные составляющие: их частоты, амплитуды и фазы. Ограничение спектра задаёт, какие частоты допускаются в сигнале.: у периодической волны с частотой есть составляющие на частотах — гармоники с номерами Их амплитуды (amplitude)амплитуда (amplitude) — Наибольший модуль отклонения колеблющейся величины от равновесного значения. Для синусоиды обозначается A; единицы совпадают с единицами сигнала. для размаха ±1 приведены в таблице; знаки и фазы гармоник опущены. Такие ряды выводит Пакетт для элементарных волн (Puckette, §10.2); у него другая нормировка размаха, поэтому множители отличаются постоянным коэффициентом.
| Форма | Значение | Гармоники | Амплитуда k-й гармоники |
|---|---|---|---|
| Синус | sin 2πt | только k = 1 | 1 |
| Треугольник | 1 − 4|t − 0,5| | нечётные | 8/(π²k²) |
| Пила | 2t − 1 | все | 2/(πk) |
| Прямоугольник | +1 при t < 0,5, иначе −1 | нечётные | 4/(πk) |
Выбор формы основного осциллятора задаёт исходный тембр (timbre)тембр (timbre) — Свойство слухового ощущения, по которому различают два звука одинаковой высоты и громкости. Зависит от состава спектра и его изменения во времени.. Пила содержит все гармоники и звучит ярко, прямоугольник — только нечётные, синус (sine)синус (sine) — Вертикальная координата точки на единичной окружности под заданным углом от положительного направления горизонтальной оси. Для острого угла прямоугольного треугольника равен отношению противолежащего катета к гипотенузе. — одну составляющую. Треугольник тоже содержит нечётные гармоники, но их амплитуды убывают как , и звук мягче. Для субтрактивного синтеза, где следом стоит фильтр, обычно берут формы с богатым спектром: фильтру есть что ослаблять.
У идеальной пилы гармоники продолжаются без верхней границы. Если вычислять её значения прямо по формуле, каждый отсчёт совпадёт с отсчётом идеальной непрерывной пилы. Значит, дискретизируются и все гармоники выше , а наложение спектров (aliasing)наложение спектров (aliasing) — Наложение частотных составляющих при дискретизации: разные непрерывные сигналы дают одинаковые отсчёты. Частота выше границы Найквиста может проявиться как более низкая. переносит каждую из них в полосу от 0 до .
- частота, на которой окажется гармоника после дискретизации, Гц;
- остаток от деления на , Гц.
Остаток от деления убирает целое число , а вторая строка зеркально отражает частоты верхней половины. При 48 кГц граница равна 24 кГц. Таблица сравнивает пилу на низкой ноте A2 (110 Гц) и на высокой A6 (1760 Гц); уровни указаны относительно основной гармоники и следуют из закона .
| Нота | Последняя гармоника ниже 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 Гц, поэтому отражение звучит как посторонний, не связанный с нотой тон. При повышении ноты уменьшается: этот тон движется вниз, в противоположную сторону. Пакетт оценивает отражения пилы 440 Гц при 44,1 кГц примерно в −32 дБ относительно основной и называет их вполне слышимыми (Puckette, §10.4).
Фильтр после осциллятора эту ошибку не исправит: отражения уже лежат в слышимой полосе среди обычных гармоник, и фильтр не отличит их от полезного сигнала (там же). Поэтому алиасинг ослабляют в самом генераторе.
Синусу это не нужно: у него одна гармоника. У треугольника нет скачка, только излом, и гармоники убывают как . На A6 его 27-я гармоника слабее основной на дБ, вместо −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).
- поправка для скачка вверх на 2 в точке , без единиц;
- расстояние от скачка, выраженное в шагах фазы .
Первая строка относится к отсчёту сразу после скачка, вторая — к последнему отсчёту перед ним; оба значения зависят от того, где между отсчётами пришёлся скачок. На краях поправка равна нулю и плавно переходит в обычную форму. У пилы скачок направлен вниз, поэтому поправка вычитается: . У прямоугольника два скачка: вверх при и вниз при , поэтому к нему прибавляется и вычитается .
Метод требует знать шаг и положение скачка — оба следуют из фазы. Условие применимости: шаг должен быть заметно меньше половины периода. При поправки двух скачков прямоугольника перекрываются, и метод теряет точность.
PolyBLEP ослабляет алиасинг, но не устраняет его. По спектру визуализации ниже для пилы A6 при 48 кГц отражение на 480 Гц падает с −32,5 дБ ниже −100 дБ, на 2240 Гц — с −32,7 до −85 дБ, на 11 040 Гц — только с −30,6 до −53 дБ. У самой границы выигрыш ещё меньше: отражение на 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 (до первой октавы). Каждый шаг номера — полутон, то есть множитель к частоте в равномерной темперации.
- номер ноты MIDI;
- частота ноты при настройке A4 = 440 Гц, Гц.
Для (A3) показатель равен , и Гц. Для C4 () получается Гц, для A6 () — Гц. Ту же формулу использует juce::MidiMessage::getMidiNoteInHertz с параметром частоты A (исходник JUCE 9.0.3). При другой настройке, например A4 = 442 Гц, меняется только первый множитель.
Интервалы и расстройка
Второй осциллятор обычно звучит на заданном музыкальном интервале (musical interval)музыкальный интервал (musical interval) — Расстояние между высотами двух нот. Интервал называют мелодическим при последовательном звучании нот и гармоническим при одновременном. от первого. В унисоне частоты равны. Тогда результат зависит от разности фаз, как при сложении двух источников одной частоты. Если обе пилы стартуют одновременно с одной фазой, сумма — та же пила с удвоенным уровнем. Если вторую сдвинуть на полпериода, нечётные гармоники компенсируются: — пила с удвоенной частотой, то есть на октаву выше.
Интервалмузыкальный интервал (musical interval) — Расстояние между высотами двух нот. Интервал называют мелодическим при последовательном звучании нот и гармоническим при одновременном. из полутонов умножает частоту на . Октаваоктава (octave) — Музыкальный интервал между нотами одного названия в соседних регистрах. Соответствует отношению частот 2:1. — 12 полутонов, множитель 2. Квинта — 7 полутонов, , а не точное отношение 3/2: темперированная квинта ниже чистой примерно на 1,955 цента (cent)цент (cent) — Сотая часть равномерно темперированного полутона; в октаве 1200 центов. Интервал в c центов умножает частоту на 2 в степени c/1200.. От A3 = 220 Гц она даёт 329,63 Гц вместо 330 Гц.
Расстройкарасстройка (detune) — Небольшое отклонение частоты одного генератора от другого или от точного интервала, обычно в центах. При одновременном звучании даёт биения. — небольшое отклонение от точного интервала, обычно в центахцент (cent) — Сотая часть равномерно темперированного полутона; в октаве 1200 центов. Интервал в c центов умножает частоту на 2 в степени c/1200.. Цент — сотая часть полутона (подробнее о центах).
- ,
- частоты первого и второго осцилляторов, Гц;
- интервал, полутоны: 0 — унисон, 7 — квинта, 12 — октава;
- расстройка, центы.
Расстройка на +7 центов от 220 Гц даёт Гц.
Биения
Два тона близких частот попеременно складываются в фазе и в противофазе. Громкость (loudness)громкость (loudness) — Ощущение того, насколько сильным кажется звук. Зависит от звукового давления, частотного состава и условий восприятия. суммы нарастает и спадает с частотой биений (beats)биения (beats) — Периодическое нарастание и спад громкости суммы двух тонов близких частот. Частота биений равна модулю разности частот. . Для 220 и 220,89 Гц это 0,89 Гц: примерно одна пульсация за 1,12 с. Такие биениябиения (beats) — Периодическое нарастание и спад громкости суммы двух тонов близких частот. Частота биений равна модулю разности частот. и есть движение, которое слышно в двух слегка расстроенных осцилляторах.
У пилы расходятся все гармоники, и каждая -я биёт со своей частотой : 10-я — 8,9 Гц, 20-я — 17,8 Гц, 50-я — 44,6 Гц. Поэтому в звуке двух расстроенных пил движение идёт сразу на разных скоростях: низкие гармоники колеблются медленно, а у высоких изменения быстрые и сливаются в шероховатость (roughness)шероховатость (roughness) — Ощущение дрожащего, грубого характера звука при быстрых изменениях его амплитуды или частоты. Зависит от скорости изменений, глубины и спектра.. Для тона 1 кГц с амплитудной модуляцией (modulation)модуляция (modulation) — Изменение параметра одного сигнала под действием другого. Например, управляющий сигнал LFO может периодически менять амплитуду звука или частоту генератора. пульсация выражена сильнее всего около 4 Гц, а шероховатость начинает нарастать около 15 Гц. Для гармоник сложного звука эти числа служат ориентиром, а не точной границей.
Темперированная квинта тоже даёт биения без расстройки. Третья гармоника A3 (660 Гц) и вторая гармоника квинты ( Гц) расходятся на 0,75 Гц. У октавы при равных частотах вторая гармоника нижнего тона совпадает с основной верхнего, и биений нет, пока нет расстройки.
Саб-осциллятор
Саб-осцилляторсаб-осциллятор (sub-oscillator) — Дополнительный генератор голоса, звучащий на октаву или две ниже основной ноты. Обычно даёт синус или прямоугольник. звучит на октаву ниже ноты: sub_frequency = note_frequency / 2. У SH-101 его можно переключить на одну или две октавы вниз (Roland, SH-101 PLUG-OUT Owner’s Manual). Для начала подойдут две формы: синус добавляет низкую основу без новых гармоник, прямоугольник — нечётные гармоники половинной частоты и более плотный низ.
Есть два способа получить его фазу. Первый — отдельный аккумулятор с половинной частотой: это ещё один объект того же класса. Второй — делить фазу основного осциллятора. При каждом переносе фазы основного осциллятора переключается флаг, и фаза саба равна : на чётных циклах основного осциллятора она растёт от 0 до 0,5, на нечётных — от 0,5 до 1. Саб тогда всегда привязан к первому осциллятору, даже если его частота плавно меняется. Шаг для PolyBLEP саба — .
// Фаза саба из фазы основного: флаг меняется при каждом переносе.
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.
Микшер голоса
Микшер умножает каждый источник на собственный уровень и складывает: . Уровни независимы, как усиление отдельных источников; соотношение задаёт тембр, а сумма — пик перед следующей стадией.
- уровень -го источника, без единиц;
- наибольший модуль выхода источника; здесь не больше 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 пик не увеличивает. Простое правило — выбрать уровни так, чтобы , и уменьшать их пропорционально при добавлении источника.
Собрать и послушать
Визуализация считает голос тем же кодом, что приведён ниже, при 48 кГц. Сверху — четыре периода суммы от начала ноты, снизу — её спектр с логарифмической шкалой (logarithmic scale)логарифмическая шкала (logarithmic scale) — Шкала, на которой равные расстояния соответствуют одинаковым отношениям положительных величин. Например, 100, 1000 и 10 000 Гц расположены на равных расстояниях. частот. Полоса от 20 Гц до 5 кГц, где слух особенно чувствителен, разделена на шесть частей с равным отношением частот, и кольцо отмечает самую сильную отражённую составляющую в каждой. Отражения ближе 18 Гц к гармоникам ноты не отмечаются: на таком спектре их не отличить. Звук считается тем же кодом непрерывно. При смене формы или PolyBLEP он затихает и возвращается за 20 мс, уровни меняются плавно за то же время.
- сумма
- 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 включён.
Пример «A6 без PolyBLEP» показывает слышимые отражения ниже основной; включение PolyBLEP опускает кольца. «Квинта без расстройки» даёт медленные биения 0,75 Гц от темперированной квинты, а «Перегрузка» — сумму выше 0 dBFS и предупреждение. Попробуйте также расстройку 0 и 25 центов в унисоне и сравните движение звука.
Код на C++
Код использует стандартную библиотеку C++20 и повторяет устройство LFO на C++: параметры отдельно от изменяемого состояния, подготовка, сброс и получение одного отсчёта. Первый фрагмент переводит фазу в значение формы и добавляет PolyBLEP.
// Формы из фазы 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 выдаёт одно значение и переносит фазу с сохранением избытка. Условия: конечная положительная и частота от 0 до , не включая границу; проверку пользовательского ввода выполнить до вызова.
// Параметры осциллятора отдельно от состояния — фазы в долях периода.
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 или на переключаемый источник оттуда же.
// Четыре источника голоса и микшер с независимыми уровнями.
#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 неудобен: функция формы получает только фазу, а поправке нужен шаг и положение скачка. Шаг при этом может меняться внутри блока из-за встроенного сглаживания частоты (исходник Oscillator 9.0.3). Собственный класс хранит шаг рядом с фазой.
Источники заполняют выходной буфер JUCE так же, как шумовой источник: один отсчёт голоса на кадр копируется во все каналы. Нужен модуль juce_audio_basics.
// Один отсчёт голоса на кадр; буфер перезаписывается.
#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 между блоками мгновенна, а для плавного перехода во время звучания нужно сглаживание, как в визуализации. Перед записью в целочисленный формат или выводом проверьте пик: о защите выхода — в статье о микшировании.
Самопроверка
Почему два осциллятора голоса не могут читать одну общую фазу, если нужна расстройка?
Общая фаза растёт с одним шагом, значит частота у обоих одинакова. Расстройка на несколько центов требует другого шага и, следовательно, отдельного накопления. Отдельное состояние также позволяет задать каждому собственную начальную фазу.
Нота A5 (880 Гц) звучит простой пилой при 48 кГц. Какая гармоника последней помещается ниже 24 кГц и куда отразится следующая?
, поэтому последняя — 27-я, 23 760 Гц. 28-я гармоника 24 640 Гц отразится в Гц. Её амплитуда — 1/28 от основной, около −28,9 дБ.
Уровни источников 0,5, 0,5, 0,3 и 0,2. Каков худший пик и как вернуть его в ±1, сохранив соотношение?
, это около +3,52 dBFS. Если умножить все уровни на , получатся 0,333, 0,333, 0,2 и 0,133. Сумма модулей станет равна 1, а соотношение источников сохранится.
Источники
- Välimäki V., Pekonen J., Nam J. Perceptually informed synthesis of bandlimited classical waveforms using integrated polynomial interpolation. в списке литературы → — ступенька с ограниченной полосой и BLEP-остаток (разд. II), полиномиальные поправки и формулы двухточечного варианта (разд. III, табл. I), поправка к излому треугольника, граница слышимости алиасинга (табл. VIII).
- Välimäki V., Huovilainen A. Antialiasing Oscillators in Subtractive Synthesis. в списке литературы → — первое описание двухточечной полиномиальной поправки. Полный текст закрыт; метод проверен по изложению в статье 2012 года.
- Puckette M. The Theory and Technique of Electronic Music. Гл. 10: Fourier series of the elementary waveforms, Predicting and controlling foldover и Transition splicing. в списке литературы → — гармоники пилы, прямоугольника и треугольника, отражения выше частоты Найквиста, вставка ступеньки с ограниченной полосой.
- Roland. SH-101 PLUG-OUT Software Synthesizer Owner’s Manual, 2017 — блоки VCO, микшер источников с саб-осциллятором и шумом, VCF, VCA и огибающая; саб на одну или две октавы ниже.
- JUCE 9.0.3. MidiMessage::getMidiNoteInHertz и Oscillator — частота ноты по номеру MIDI; функция формы получает только фазу.
- Nave C. R. HyperPhysics. Cents и Beats. в списке литературы → — цент и частота биений.
Связи
Связи списком
Ссылается на
- Белый и розовый шум: спектр и генерация
- Дискретизация: отсчёты, период и частота дискретизации
- Как устроен звук: первый обзор
- Математика для аудио
- Микширование сигналов: усиление, фаза и запас уровня
- Огибающая ADSR и VCA: как нота начинается и затихает
- Однополюсный фильтр нижних частот
- Психоакустика: как слух воспринимает звук
- Теория музыки: ноты, ритм и гармония
- LFO: низкочастотный генератор и модуляция
Ссылаются сюда
- Белый и розовый шум: спектр и генерация
- Как устроен звук: первый обзор
- Математика для аудио
- Микширование сигналов: усиление, фаза и запас уровня
- Огибающая ADSR и VCA: как нота начинается и затихает
- Однополюсный фильтр нижних частот
- Первое знакомство с DAW: дорожки, MIDI и звук
- Психоакустика: как слух воспринимает звук
- LFO: низкочастотный генератор и модуляция