Осциллограмма показывает, как сигнал меняется во времени. О звуке часто спрашивают другое: какие частоты в нём есть и насколько сильна каждая. Ответ даёт спектр (spectrum)спектр (spectrum) — Представление сигнала через его частотные составляющие: их частоты, амплитуды и фазы. Для отсчётов спектр вычисляют дискретным преобразованием Фурье.. По отсчётам (sample)отсчёт (sample) — Значение сигнала в один момент дискретизации, одно число. В звукозаписи его также называют сэмплом. его вычисляет дискретное преобразование Фурье, а на практике — быстрый алгоритм того же преобразования. Статья выводит формулу из сравнения сигнала с пробной синусоидой, разбирает разрешение по частоте, утечку и оконные функции, устройство анализатора спектра и заканчивается кодом на C++23 и JUCE. Понадобятся комплексные числа и формула Эйлера.
Что показывает спектр
Спектр отвечает на вопрос «сколько каждой частоты» в сигнале. Для каждой частоты он даёт амплитуду (amplitude)амплитуда (amplitude) — Наибольший модуль отклонения колеблющейся величины от равновесного значения. Для синусоиды обозначается A; единицы совпадают с единицами сигнала. и фазу (phase)фаза (phase) — Положение внутри периода колебания, выраженное углом: полный период соответствует 360°, или 2π радиан. Начальная фаза — её значение в момент t = 0. синусоиды этой частоты, а сумма всех таких синусоид снова даёт сигнал. На графике спектра по горизонтали откладывают частоту, по вертикали — амплитуду или уровень в децибелах. Фазу обычно не рисуют: по ней трудно что-то увидеть, хотя для обратного перехода к сигналу она нужна.
На рисунке 1 три сигнала с одинаковой амплитудой 0,5. Синусоида (sine)синус (sine) — Вертикальная координата точки на единичной окружности под заданным углом от положительного направления горизонтальной оси. Для острого угла прямоугольного треугольника равен отношению противолежащего катета к гипотенузе. 1000 Гц даёт один пик на уровне −6 дБ: . Пик не бесконечно узкий: у него есть ширина и спадающее основание; откуда они берутся, объясняют разделы о разрешении и утечке. Пила 220 Гц даёт пики на 220, 440, 660 Гц и дальше — гармоники с амплитудами, пропорциональными . При удвоении номера гармоники её амплитуда падает вдвое, на 6 дБ. На логарифмической оси гармоники сгущаются к высоким частотам. Белый шум (white noise)белый шум (white noise) — Шум с постоянной спектральной плотностью мощности в рассматриваемой полосе. Полосы одинаковой ширины в герцах содержат одинаковую ожидаемую мощность. пиков не даёт: его мощность в среднем распределена по всем частотам до поровну, а неровность графика — случайный разброс, который уменьшается при усреднении.
Шкалы на рисунке выбраны так, как в большинстве анализаторов: частоты — в логарифмической шкале, где каждая октава занимает одинаковую ширину, уровни — в децибелах. На линейной шкале амплитуд пик синусоиды виден так же, а гармоники пилы выше десятой и весь шум сливаются с нулём.
Сравнение с пробной синусоидой
Возьмём кадр из отсчётов и проверим, есть ли в нём синусоида заданной частоты. Для этого каждый отсчёт умножают на отсчёт пробной синусоиды этой частоты и всё складывают (S. W. Smith, гл. 8, с. 157–159). Если в сигнале есть такая синусоида, произведения почти всё время одного знака, и сумма растёт. Если нет, положительные и отрицательные произведения уравновешивают друг друга.
Пусть , а сигнал — косинусоида с амплитудой 1, которая делает в кадре 3 периода: . Пробный косинус тех же трёх периодов даёт произведения , они неотрицательны, а их среднее равно 1/2. Сумма 64 произведений равна 32, то есть . Пробный косинус с пятью периодами даёт сумму ровно 0. Так получается для любых двух разных целых чисел периодов от 1 до : сумма произведения таких косинусоид по кадру равна нулю. Это свойство называют ортогональностью (orthogonality); на нём основано ДПФ (Sundararajan, §3.1).
Если амплитуда сигнала равна , сумма равна . Значит, амплитуду можно найти, умножив сумму на .
Одной пробной синусоиды мало. Сдвинем фазу сигнала на четверть периода: . Частота та же, а сумма с пробным косинусом теперь равна нулю: произведения синуса на косинус одной частоты тоже уравновешиваются. Поэтому берут две пробы, косинус и синус, и две суммы:
- число периодов пробы в кадре, целое.
Для при целом от 1 до получается и . Фаза делит длину между двумя суммами, как угол делит радиус между проекциями на единичной окружности. Отсюда амплитуда , а фаза — угол точки .
Пару удобно записать одним комплексным числом . По формуле Эйлера , поэтому обе суммы — это одна сумма с комплексной пробой:
Модуль результата пропорционален амплитуде, аргумент равен фазе. Это комплексная амплитуда синусоиды, умноженная на .
- сигнал x[n], точки — отсчёты
- пробная синусоида
- произведение
- накопленная сумма
Сумма с косинусом C = 32,00, с синусом S = 0,00.
|X[3]| = √(C² + S²) = 32,00; амплитуда 2|X|/N = 1,000, фаза arg X = 0°.
Частоты совпадают: произведение почти всё время одного знака, и сумма растёт на протяжении всего кадра.
В начальном состоянии визуализации 2 сигнал и пробный косинус делают по 3 периода, и накопленная сумма растёт до 32. Сдвиньте фазу сигнала на 90°: сумма с косинусом станет нулём, сумма с синусом — −32, а модуль и амплитуда не изменятся. Поставьте пробную частоту 5: обе суммы обнулятся. Наконец, поставьте частоту сигнала 3,5 периода: ни на одной пробной частоте сумма не обнуляется, при и оценки амплитуды 0,59 и 0,68 вместо 1. К этому случаю вернёмся в разделе об утечке.
Дискретное преобразование Фурье
Если повторить комплексную сумму для каждого целого числа периодов от 0 до , получится дискретное преобразование Фурье (discrete Fourier transform, DFT)дискретное преобразование Фурье (discrete Fourier transform, DFT) — Преобразование N отсчётов в N комплексных чисел X[k] = Σ x[n]·e^(−j2πkn/N). Модуль и аргумент X[k] дают амплитуду и фазу составляющей с частотой k·fₛ/N., сокращённо ДПФ (J. O. Smith, DFT Definition; Sundararajan, §3.1, формула 3.3):
- отсчёт кадра, ;
- длина кадра, отсчётов;
- номер частоты: число периодов пробы в кадре;
- комплексный результат, в единицах отсчётов, умноженных на число отсчётов.
ДПФ превращает отсчётов в комплексных чисел. Для каждого выполняется комплексных умножений, всего . Формула (1) не требует, чтобы сигнал был синусоидой: она применима к любому кадру, а результат описывает этот кадр как сумму синусоид с целым числом периодов.
Бины и частоты
Каждое значение называют бином (frequency bin)бин (frequency bin) — Одна частотная точка ДПФ с номером k: частота k·fₛ/N, шаг до соседней fₛ/N. У вещественного сигнала независимы бины от 0 до N/2.. Проба бина делает периодов за кадр, а кадр длится секунд, где — частота дискретизации (sample rate)частота дискретизации (sample rate) — Число отсчётов в каждом канале за одну секунду, то же число кадров аудиоданных в секунду. Измеряется в герцах: 44 100 Гц означает 44 100 отсчётов на канал в секунду.. Отсюда частота бина:
- частота бина , Гц;
- частота дискретизации, Гц.
При и кГц бин 1 соответствует 46,875 Гц, бин 21 — 984,4 Гц, бин 512 — 24 кГц. Синусоида 1000 Гц делает в таком кадре периода и попадает между бинами 21 и 22. Шаг между бинами одинаков на всех частотах (Sundararajan, §3.1, с. 75).
Амплитуда и фаза
Пусть , где — целое от 1 до . По разделу о пробе , а остальные бины от 1 до равны нулю. Амплитуду и фазу дают модуль и аргумент:
- амплитуда синусоиды, в единицах отсчётов;
- фаза косинусоиды в начале кадра, рад.
Множитель не действует для двух бинов. Бин 0 — сумма отсчётов, и — их среднее, постоянная составляющая. Бин — сумма отсчётов с чередующимся знаком, и — амплитуда последовательности . Для этих бинов амплитуда равна . Нормировку разные книги и библиотеки выбирают по-разному: Bellanger относит множитель к прямому преобразованию (Bellanger, §2.2), Sundararajan, J. O. Smith и JUCE — к обратному. Перед расчётом уровней нужно проверить соглашение своей библиотеки.
Таблица 1 — пример из учебника Sundararajan: четыре отсчёта сигнала и их ДПФ (Sundararajan, §3.1, с. 70–72).
| k | X[k] | |X[k]| | arg X[k] | Составляющая |
|---|---|---|---|---|
| 0 | 4 | 4 | 0° | постоянная 4/4 = 1 |
| 1 | 1 + j1,732 | 2 | 60° | 2 · 2/4 = 1: cos(2πn/4 + 60°) |
| 2 | −4 | 4 | 180° | −4/4: −cos(πn), чередование знака |
| 3 | 1 − j1,732 | 2 | −60° | сопряжённая пара бина 1 |
Для сумма с пробой , то есть с числами , равна . Её модуль и аргумент восстанавливают амплитуду 1 и фазу второго слагаемого.
Симметрия и частота Найквиста
В таблице 1 — сопряжённое число к . Это общее свойство: для вещественного сигнала (Sundararajan, §3.1, с. 71). Проба бина — это , вектор, который вращается в обратную сторону: бин соответствует частоте . Вещественная косинусоида — сумма двух векторов с частотами и и половинной длиной (отрицательные частоты). Поэтому каждый из бинов и получает по , и в формуле (3) стоит множитель 2.
Новых сведений бины выше не несут, и анализаторы показывают только бины от 0 до — это чисел (S. W. Smith, гл. 8). Верхний бин соответствует , частоте Найквиста (Nyquist frequency)частота Найквиста (Nyquist frequency) — Половина частоты дискретизации, fₛ/2. В принятой низкочастотной модели точное восстановление по отсчётам требует, чтобы все частоты исходного сигнала были строго ниже этой границы.. Частот выше неё в отсчётах нет: если до дискретизации они были, то при ней уже отразились ниже , и ДПФ покажет их на отражённой частоте.
Разрешение по частоте
Шаг между бинами задаёт разрешение по частоте:
- шаг между бинами, Гц;
- длительность кадра, с.
Шаг по частоте обратен длительности кадра. Bellanger называет это соотношение принципом неопределённости для спектрального анализа: точнее различить частоты можно, только дольше наблюдая сигнал (Bellanger, §2.4.2). Таблица 2 даёт числа для 48 кГц.
| N | Δf, Гц | Кадр, мс |
|---|---|---|
| 256 | 187,5 | 5,3 |
| 512 | 93,75 | 10,7 |
| 1024 | 46,875 | 21,3 |
| 2048 | 23,44 | 42,7 |
| 4096 | 11,72 | 85,3 |
| 8192 | 5,86 | 170,7 |
Ноты E1 и F1 нижнего регистра бас-гитары звучат на частотах 41,20 и 43,65 Гц, между ними 2,45 Гц. Чтобы они попали хотя бы в разные бины, нужен шаг меньше 2,45 Гц, то есть : ближайшая степень двойки — 32 768 отсчётов, 0,68 с. За это время нота может закончиться, а удар барабана в таком кадре не локализовать точнее, чем в пределах всего кадра. Короткий кадр из 256 отсчётов покажет начало удара с точностью 5 мс, но вся полоса от 0 до 187 Гц уложится в один шаг между бинами. Подробная картина по частоте требует длинного кадра, а подробная во времени — короткого.
Можно дополнить кадр нулями до большего . Бинов станет больше, но новые значения лишь интерполируют спектр между прежними: два близких пика, которые сливались, так и останутся слитыми (S. W. Smith, гл. 9, с. 174). Разрешение определяет длительность настоящего сигнала в кадре, а не длина преобразования.
Утечка и окна
Синусоида 1000 Гц делает в кадре из 1024 отсчётов 21,33 периода: кадр обрывается посреди периода. ДПФ считает кадр одним периодом бесконечно повторяющегося сигнала (Sundararajan, §3.1). Конец кадра стыкуется с началом со скачком, и чтобы описать скачок, нужны все частоты. Уровень появляется во всех бинах, а не только у частоты синусоиды. Это утечка спектра (spectral leakage)утечка спектра (spectral leakage) — Появление уровня в бинах вдали от частоты синусоиды, когда в кадре не укладывается целое число её периодов. Причина — обрыв сигнала на краях кадра. (J. O. Smith, Frequencies in the «Cracks»).
Утечка видна и по числам. Синусоида с амплитудой 0,5 дала бы на бине −6,02 дБ. При 1000 Гц бин 21 показывает −7,64 дБ, бин 22 — −13,74 дБ, бин на 10 выше пика — −37,4 дБ, на 50 выше — −51,7 дБ, и до нуля уровень не опускается нигде. Слабый тон 1600 Гц на 40 дБ тише, то есть на уровне −46 дБ, в 13 бинах от сильного тонет в этом основании. Если частота попадает точно посередине между бинами, пик проседает сильнее всего: в коде этой статьи — на 3,82 дБ. Увеличение сужает основание в герцах, но уровни боковых выбросов в децибелах остаются прежними (J. O. Smith).
Окно Ханна
Скачок на стыке можно убрать, если перед ДПФ умножить кадр на функцию, которая плавно сводит его края к нулю. Такой множитель называют оконной функцией (window function)оконная функция (window function) — Множитель, на который поотсчётно умножают кадр перед ДПФ. Сводит края кадра к нулю и уменьшает утечку спектра ценой более широкого пика; пример — окно Ханна., или окном. Без него кадр тоже умножен на окно — прямоугольное, из одних единиц. Одно из самых распространённых плавных окон — окно Ханна (Hann window) (Sundararajan, §5.2.2):
- множитель отсчёта , без единиц, от 0 на краях до 1 в середине.
Так окно Ханна заполняет juce::dsp::WindowingFunction в JUCE (juce_Windowing.cpp). Окно меняет форму пика. Основание пика синусоиды — главный лепесток (main lobe) — у окна Ханна вдвое шире: ±2 бина вместо ±1. Зато боковые лепестки (side lobes) падают: первый — до −31,5 дБ от пика вместо −13 дБ, а дальше они спадают на 18 дБ за октаву (J. O. Smith, Hann Window). Таблица 3 сравнивает оба окна на синусоиде 1000 Гц.
| Окно | Главный лепесток | Первый боковой | Пик, дБ | Через 10 бинов, дБ | Через 50 бинов, дБ | Наибольшая просадка, дБ |
|---|---|---|---|---|---|---|
| прямоугольное | ±1 бин | −13 дБ | −7,64 | −37,4 | −51,7 | 3,82 |
| Ханна | ±2 бина | −31,5 дБ | −6,65 | −76,1 | −118,7 | 1,42 |
S. W. Smith описывает это как обмен: окно уменьшает утечку, то есть высоту «хвостов» пика, ценой разрешения, то есть ширины пика (S. W. Smith, гл. 9, с. 174–175). Прямоугольное окно лучше различает два тона одинаковой силы на соседних частотах. Окно Ханна лучше показывает слабый тон рядом с сильным; его же использует анализатор из урока JUCE.
Окно уменьшает отсчёты: среднее значение окна Ханна около 1/2, поэтому синусоида на бине без поправки покажет уровень на 6 дБ ниже. JUCE по умолчанию (normalise = true) умножает окно на , то есть на . Тогда масштаб из формулы (3) верен для любого окна.
- уровни бинов, точки — отдельные бины
- частота тона
N = 1024: шаг бинов Δf = fs/N = 46,88 Гц, кадр 21,3 мс; бины от 0 до N/2 = 512.
Тон 1 000 Гц = 21,33·Δf, между бинами 21 (984,4 Гц) и 22 (1 031,3 Гц). Пик −6,65 дБ на бине 21; на бине синусоида дала бы −6,02 дБ.
Окно Ханна: главный лепесток ±2 бина, первый боковой −31,5 дБ от пика.
У этого сигнала частота задана заранее.
Громкость. Чистый тон, пила и белый шум звучат резко: перед запуском убавьте громкость, особенно в наушниках.
Остановлен
Параметры и звук работают с включённым JavaScript. Показано начальное состояние.
Визуализация 3 считает спектр начала сигнала тем же способом, что анализатор из раздела о коде: окно с нормировкой, БПФ и масштаб . Звучит тот же сигнал. В начальном состоянии синусоида 1000 Гц попадает между бинами, и пик −6,65 дБ стоит на бине 21. Переключите окно на прямоугольное: основание расширится, через 10 бинов уровень будет −37 дБ вместо −76. Поставьте 1500 Гц — ровно 32 бина при : прямоугольное окно даст одну линию −6,02 дБ, окно Ханна — пик −6,02 дБ и два соседних бина по −12,04 дБ. На сигнале «два тона» слабый тон 1600 Гц с прямоугольным окном виден только при , а с окном Ханна — уже при 512. Выберите белый шум и меняйте : уровень бинов при каждом удвоении падает на 3 дБ, а уровень синусоиды не меняется.
Быстрое преобразование Фурье
По формуле (1) кадр из 1024 отсчётов требует комплексных умножений. Анализатор с перекрытием кадров на половину считает при 48 кГц около 94 кадров в секунду — около 98 миллионов комплексных умножений в секунду на один канал.
Быстрое преобразование Фурье (fast Fourier transform, FFT)быстрое преобразование Фурье (fast Fourier transform, FFT) — Алгоритм, который вычисляет то же ДПФ за число операций порядка N·log₂N вместо N². Распространённые реализации требуют, чтобы N было степенью двойки., БПФ, вычисляет те же с точностью до округления, но за число операций порядка , а не (запись O). Простейший вариант делит кадр на чётные и нечётные отсчёты, вычисляет два ДПФ половинной длины и собирает из них результат, добавляя умножений. Половины делятся дальше, пока не останутся пары отсчётов. Ступеней , на каждой умножений, всего . Bellanger уточняет, что преобразования из двух отсчётов, до которых доходит деление, умножений не требуют, и получает (Bellanger, §2.2.1). Элементарное действие алгоритма, которое из двух чисел делает два новых, называют бабочкой (butterfly) по виду его схемы (S. W. Smith, гл. 12).
| N | ДПФ, N² | БПФ, (N/2)·log₂N | Во сколько раз меньше |
|---|---|---|---|
| 256 | 65 536 | 1024 | 64 |
| 1024 | 1 048 576 | 5120 | 205 |
| 4096 | 16 777 216 | 24 576 | 683 |
У S. W. Smith БПФ на 1024 точки на компьютере 1990-х годов работает в 300 с лишним раз быстрее, чем ДПФ по формуле (S. W. Smith, гл. 12, с. 237). Выигрыш растёт с . Деление пополам требует, чтобы было степенью двойки: juce::dsp::FFT принимает показатель степени order, и размер кадра равен (juce_FFT.h). Для других длин существуют свои алгоритмы, но обычно кадр дополняют нулями до степени двойки (Sundararajan, гл. 10, с. 357).
Анализатор спектра
Уровни и ось частот
Анализатор переводит амплитуду бина в децибелы: . При таком масштабе синусоида с амплитудой 1 на бине показывает 0 дБ, а с амплитудой 0,5 — −6,02 дБ, как на рисунке 1. Опорный уровень выбирают по-разному. В уроке JUCE об анализаторе из уровня вычитают gainToDecibels (fftSize), то есть делят модуль на , а не на , и синусоида полной шкалы там показывает −6 дБ (JUCE, Spectrum analyser tutorial). Анализатор полезно проверить тестовой синусоидой известной амплитуды.
Ось частот у анализаторов обычно логарифмическая, как слух воспринимает высоту, а бины ДПФ идут с равным шагом в герцах. При ниже 200 Гц всего четыре бина — 46,9; 93,8; 140,6 и 187,5 Гц, а выше 10 кГц — почти триста. На логарифмической оси низ спектра выглядит ломаной из нескольких точек, а верх — плотной полосой. Поэтому для низких частот берут большие , а на высоких частотах при рисовании соседние бины объединяют: в визуализации 3 на одну точку экрана выводится наибольший из бинов.
Перекрытие и усреднение
Звук идёт непрерывно, и анализатор берёт кадр за кадром. Окно Ханна почти обнуляет края кадра, поэтому отсчёты у границ кадров почти не учитываются. Чтобы не терять их, кадры перекрывают: новый кадр начинается через отсчётов после предыдущего. Перекрытие на половину и больше — обычный выбор (J. O. Smith, Classic Spectrograms). При и 48 кГц новый спектр появляется каждые 512 отсчётов, 93,75 раза в секунду. Экран обновляется 60 раз в секунду, и между кадрами экрана анализатор показывает последний готовый спектр.
Спектр одного кадра шума неровный: соседние бины отличаются на 5–10 дБ случайным образом. Длинный кадр не помогает: бинов становится больше, а разброс прежний (S. W. Smith, гл. 9, с. 170). Помогает усреднение спектров нескольких кадров: случайные колебания уменьшаются пропорционально корню из числа кадров. В визуализации 3 среднее 16 кадров уменьшает разброс уровней белого шума с 5–6 до 1,1 дБ. Заметен и уровень шума: при каждом удвоении он падает на 3 дБ. Мощность шума та же, но делится между вдвое большим числом бинов, а уровень синусоиды от не зависит. Сравнивать уровень шума и тона на спектре можно только при известном .
Спектрограмма
Если спектры кадров выстроить по времени, получится спектрограмма (spectrogram): время по горизонтали, частота по вертикали, уровень — яркостью (J. O. Smith, Classic Spectrograms). На рисунке 4 один и тот же сигнал разобран кадрами двух размеров.
При кадр длится 5,3 мс: смена нот и щелчок видны точно по времени, но тоны 440 и 494 Гц, между которыми 54 Гц, сливаются в одну широкую полосу — шаг бинов 187,5 Гц. При кадр длится 85 мс, шаг 11,7 Гц, и два тона — отдельные линии. Зато щелчок длиной 20 мс растянут на 0,1 с, а смена нот размыта. Это компромисс из раздела о разрешении.
Обратное ДПФ
Из чисел отсчёты восстанавливаются точно (Sundararajan, §3.1, формула 3.2):
От формулы (1) она отличается знаком в показателе и множителем . Проверим на таблице 1 при : все экспоненты равны 1, и . Обратное преобразование складывает все синусоиды бинов с их амплитудами и фазами.
Пара прямого и обратного ДПФ позволяет обрабатывать звук в частотной области: вычислить спектр кадра, изменить и вернуться к отсчётам. Простое обнуление бинов работает плохо: ДПФ считает кадр периодическим, и изменения переходят через края кадра. Правильная схема опирается на свёртку: умножение спектров соответствует свёртке сигналов. Через БПФ свёртка длинных сигналов выполняется за операций вместо (Sundararajan, §3.3.9, с. 90), так работает свёрточный ревербератор. Свёртку и фильтры разбирает статья о цифровых фильтрах. В JUCE обратное преобразование выполняет performRealOnlyInverseTransform; множитель она применяет сама.
Код на C++
Наивное ДПФ
Функция naiveDft вычисляет формулу (1) двумя вложенными циклами. Для анализатора она слишком медленная, умножений, но годится как эталон: в тестах с ней сравнивают результат БПФ на коротких кадрах. Нужна только стандартная библиотека C++23.
// ДПФ по определению: N² комплексных умножений; эталон для проверки быстрых реализаций.
#include <complex>
#include <numbers>
#include <span>
#include <vector>
std::vector<std::complex<double>> naiveDft (std::span<const float> x)
{
const auto size = x.size();
std::vector<std::complex<double>> spectrum (size);
for (std::size_t k = 0; k < size; ++k)
{
for (std::size_t n = 0; n < size; ++n)
{
// Угол по остатку k·n mod N: при больших N произведение не теряет точность.
const auto turn = static_cast<double> ((k * n) % size) / static_cast<double> (size);
const auto angle = -2.0 * std::numbers::pi * turn;
spectrum[k] += static_cast<double> (x[n]) * std::polar (1.0, angle);
}
}
return spectrum;
}std::polar (1.0, angle) строит — точку единичной окружности (математический справочник). Угол берётся по остатку : проба периодична с периодом , а аргумент синуса и косинуса остаётся меньше . Сумма накапливается в double. Код собран clang++ -std=c++20 -Wall -Wextra -Wconversion без предупреждений. На косинусоиде с тремя периодами в 64 отсчётах он даёт , то есть амплитуду 1, а на отсчётах таблицы 1 — те же .
Анализатор на JUCE
Анализатор делит работу между двумя потоками, как в статье об аудиопотоке. Аудиопоток в processBlock только копирует отсчёты в очередь без блокировок на juce::AbstractFifo. Поток сообщений забирает их, собирает кадр, применяет окно, вычисляет БПФ и уровни.
// Анализатор: аудиопоток кладёт отсчёты в очередь, поток сообщений считает спектр кадра.
#include <juce_dsp/juce_dsp.h>
class SpectrumAnalyzer
{
public:
static constexpr int fftOrder = 10;
static constexpr int fftSize = 1 << fftOrder; // N = 1024
static constexpr int hopSize = fftSize / 2; // кадры перекрываются на 50 %
// Аудиопоток: без блокировок и выделений; если очередь полна, отсчёты теряются.
void pushSamples (const float* samples, int count) noexcept
{
const auto scope = fifo.write (count);
std::copy_n (samples, scope.blockSize1, queue.begin() + scope.startIndex1);
std::copy_n (samples + scope.blockSize1, scope.blockSize2,
queue.begin() + scope.startIndex2);
}
// Поток сообщений: true, если пришло hopSize новых отсчётов и спектр пересчитан.
bool update()
{
if (fifo.getNumReady() < hopSize)
return false;
while (fifo.getNumReady() >= hopSize)
{
std::copy (history.begin() + hopSize, history.end(), history.begin());
const auto scope = fifo.read (hopSize);
auto* tail = history.data() + (fftSize - hopSize);
std::copy_n (queue.begin() + scope.startIndex1, scope.blockSize1, tail);
std::copy_n (queue.begin() + scope.startIndex2, scope.blockSize2,
tail + scope.blockSize1);
}
std::copy (history.begin(), history.end(), fftData.begin());
window.multiplyWithWindowingTable (fftData.data(), fftSize);
fft.performFrequencyOnlyForwardTransform (fftData.data(), true); // |X[k]|, k = 0 … N/2
for (int k = 0; k <= fftSize / 2; ++k)
{
const auto bin = static_cast<std::size_t> (k);
const float scale = (k == 0 || k == fftSize / 2) ? 1.0f / fftSize : 2.0f / fftSize;
levels[bin] = juce::Decibels::gainToDecibels (fftData[bin] * scale, -140.0f);
}
return true;
}
const auto& getLevels() const noexcept { return levels; } // дБ, бины 0 … N/2
private:
juce::dsp::FFT fft { fftOrder };
juce::dsp::WindowingFunction<float> window { fftSize,
juce::dsp::WindowingFunction<float>::hann };
juce::AbstractFifo fifo { 8 * fftSize };
std::array<float, 8 * fftSize> queue {};
std::array<float, fftSize> history {};
std::array<float, 2 * fftSize> fftData {};
std::array<float, fftSize / 2 + 1> levels {};
};pushSamples вызывают в processBlock для одного канала: analyzer.pushSamples (buffer.getReadPointer (0), buffer.getNumSamples()). fifo.write возвращает две области очереди, потому что запись может перейти через конец кольцевого буфера; если места нет, области короче count, и лишние отсчёты пропадают — аудиопоток не ждёт. Очередь на 8 кадров выделена вместе с объектом, в аудиопотоке память не выделяется. update вызывают в потоке сообщений, например из juce::VBlankAttachment при каждом обновлении экрана, и при true перерисовывают график. Цикл сдвигает историю на hopSize и дописывает новые отсчёты, пока они есть, поэтому после паузы в отрисовке спектр считается по последнему кадру.
performFrequencyOnlyForwardTransform требует массив из чисел: первая половина — отсчёты, вторая — рабочее место. С аргументом true она вычисляет только бины от 0 до и записывает их модули в начало массива (juce_FFT.h). Прямое преобразование в JUCE не делит на : это сумма формулы (1). WindowingFunction с окном hann по умолчанию нормирует окно, поэтому масштаб даёт амплитуду синусоиды. Бины 0 и получают масштаб . gainToDecibels с порогом −140 дБ возвращает −140 для нулевого модуля, а не .
Код собран с JUCE 9.0.3 в консольном приложении с флагами предупреждений JUCE и -Wconversion. На синусоиде 1500 Гц с амплитудой 0,5 при 48 кГц он даёт −6,02 дБ на бине 32 и −12,03 дБ на соседних, как визуализация 3 с окном Ханна.
В уроке JUCE об анализаторе спектра кадр копируется в общий массив в аудиопотоке, а готовность отмечает поле bool nextFFTBlockReady, которое таймер читает в потоке сообщений (JUCE, Spectrum analyser tutorial). Обычный bool и общий массив, которые один поток пишет, а другой читает, по правилам C++ — гонка данных, а значит, неопределённое поведение. Очередь AbstractFifo упорядочивает запись и чтение атомарными операциями, а БПФ остаётся в потоке сообщений.
Самопроверка
Частота дискретизации 44,1 кГц, N = 2048. Чему равен шаг между бинами, сколько длится кадр и в какие бины попадёт синусоида 1000 Гц?
Гц, кадр мс. : синусоида лежит между бинами 46 и 47, и пик будет на бине 46.
N = 8, x[n] = cos(2π · 2n/8). Чему равны X[k] и какую амплитуду даст формула (3)?
Сигнал делает 2 периода в кадре: , остальные бины равны нулю. Бин 6 — сопряжённая пара бина 2, частота . Амплитуда , фаза .
Почему при удвоении N уровень бинов белого шума падает на 3 дБ, а уровень синусоиды — нет?
Мощность шума распределена по всем частотам, и при удвоении она делится между вдвое большим числом бинов: мощность на бин уменьшается вдвое, на 3 дБ. Синусоида сосредоточена на одной частоте и попадает в один бин или несколько соседних при любом : её амплитуда по формуле (3) от не зависит.
Сколько комплексных умножений нужно на кадр из 4096 отсчётов по формуле (1) и в БПФ по основанию 2?
по формуле и в БПФ, примерно в 683 раза меньше.
Генератор выдаёт синусоиду с амплитудой 0,5 точно на частоте бина, а анализатор с окном Ханна показывает −12 дБ вместо −6. В чём ошибка?
Окно не нормировано: его сумма около , поэтому модуль бина уменьшился вдвое, на 6,02 дБ. Нужно умножить окно на , в JUCE — оставить normalise = true.
Источники
- Sundararajan D. Digital Signal Processing: An Introduction. §3.1, с. 67–75; §3.3.9, с. 90; §5.2.1–5.2.2, с. 165–171; гл. 10, с. 357–358. в списке литературы → — ДПФ и обратное ДПФ, ортогональность проб, пример из четырёх отсчётов, сопряжённая симметрия, шаг , свёртка через ДПФ, периодограмма и метод Уэлча, окна и таблица уровней боковых лепестков, сложность и длины-степени двойки.
- Bellanger M. Digital Signal Processing: Theory and Practice. §2.2, с. 38–40; §2.4, с. 46–49. в списке литературы → — матричная запись ДПФ с множителем , БПФ с прореживанием по времени и число умножений , ДПФ как банк фильтров, разрешение , утечка между бинами и окна.
- Smith S. W. The Scientist and Engineer’s Guide to Digital Signal Processing. Гл. 8: The Discrete Fourier Transform, с. 157–160, гл. 9: Applications of the DFT, с. 169–175, гл. 12: The Fast Fourier Transform, с. 225–237. в списке литературы → — ДПФ через корреляцию с пробными синусоидами, частот, усреднение спектров кадров, разрешение и дополнение нулями, утечка и окна, бабочка и сравнение скорости ДПФ и БПФ.
- Smith J. O. III. Mathematics of the Discrete Fourier Transform (DFT) with Audio Applications. DFT Definition и Frequencies in the «Cracks». в списке литературы → — определение ДПФ и частоты бинов, утечка для частот между бинами, независимость уровня утечки от .
- Smith J. O. III. Spectral Audio Signal Processing. Hann or Hanning or Raised Cosine и Classic Spectrograms. в списке литературы → — главный и боковые лепестки окна Ханна, спектрограмма как последовательность спектров перекрывающихся кадров.
- JUCE 9.0.3. juce_FFT.h, juce_FFT.cpp, juce_Windowing.cpp — размер ,
performFrequencyOnlyForwardTransformи массив , прямое преобразование без деления на , окно Ханна с делителем и нормировкаnormalise. - JUCE. Tutorial: Visualise the frequencies of a signal in real time — анализатор из урока JUCE: размер кадра, окно Ханна, логарифмическая ось и пересчёт в децибелы.
Связи
Связи списком
Ссылается на
- Белый и розовый шум: спектр и генерация
- Дискретизация: отсчёты, период и частота дискретизации
- Звук в пространстве: панорама, задержка, реверберация и мид-сайд
- Математика для аудио
- Основы C++ для JUCE: сборка, типы, функции и указатели
- Осцилляторы синтезатора: формы, расстройка и саб-осциллятор
- Психоакустика: как слух воспринимает звук
- Цифровые фильтры: импульсная характеристика, КИХ и БИХ
- C++ в аудиопотоке: выделение памяти, общие объекты и инструменты JUCE
- C++ в плагине: классы, конструкторы и время жизни
Ссылаются сюда
- Белый и розовый шум: спектр и генерация
- Дискретизация: отсчёты, период и частота дискретизации
- Дисторшн и сатурация: передаточная кривая и гармоники
- Математика для аудио
- Однополюсный фильтр нижних частот
- Осцилляторы синтезатора: формы, расстройка и саб-осциллятор
- Форматы сжатия звука: AIFF, FLAC и MP3
- Цифровые фильтры: импульсная характеристика, КИХ и БИХ