Спектр сигнала: ДПФ и БПФ

Как по отсчётам узнать, из каких частот состоит сигнал?

Осциллограмма показывает, как сигнал меняется во времени. О звуке часто спрашивают другое: какие частоты в нём есть и насколько сильна каждая. Ответ даёт спектр (spectrum)спектр (spectrum) — Представление сигнала через его частотные составляющие: их частоты, амплитуды и фазы. Для отсчётов спектр вычисляют дискретным преобразованием Фурье.. По отсчётам (sample)отсчёт (sample) — Значение сигнала в один момент дискретизации, одно число. В звукозаписи его также называют сэмплом. его вычисляет дискретное преобразование Фурье, а на практике — быстрый алгоритм того же преобразования. Статья выводит формулу из сравнения сигнала с пробной синусоидой, разбирает разрешение по частоте, утечку и оконные функции, устройство анализатора спектра и заканчивается кодом на C++23 и JUCE. Понадобятся комплексные числа и формула Эйлера.

Что показывает спектр

Спектр отвечает на вопрос «сколько каждой частоты» в сигнале. Для каждой частоты он даёт амплитуду (amplitude)амплитуда (amplitude) — Наибольший модуль отклонения колеблющейся величины от равновесного значения. Для синусоиды обозначается A; единицы совпадают с единицами сигнала. и фазу (phase)фаза (phase) — Положение внутри периода колебания, выраженное углом: полный период соответствует 360°, или 2π радиан. Начальная фаза — её значение в момент t = 0. синусоиды этой частоты, а сумма всех таких синусоид снова даёт сигнал. На графике спектра по горизонтали откладывают частоту, по вертикали — амплитуду или уровень в децибелах. Фазу обычно не рисуют: по ней трудно что-то увидеть, хотя для обратного перехода к сигналу она нужна.

Спектры синусоиды, пилы и белого шумаТри графика друг под другом с общей логарифмической осью частот от 20 Гц до 24 кГц и шкалой уровня от 0 до −100 дБ. Синусоида 1000 Гц: один узкий пик около −6 дБ, по краям уровень быстро падает ниже −100 дБ. Пила 220 Гц: частокол пиков, первый около −10 дБ, следующие ниже и всё гуще к высоким частотам. Белый шум: неровная полоса около −40 дБ от низких до высоких частот без пиков. Синусоида 1000 Гц0−50−100Пила 220 Гц0−50−100Белый шум, 16 кадров0−50−1001001 к10 кЧастота, Гц
Рис. 1. Спектры трёх сигналов с амплитудой 0,5 при 48 кГц: кадр 4096 отсчётов, окно Ханна. По горизонтали частота в логарифмической шкале, по вертикали уровень в дБ относительно синусоиды с амплитудой 1. Синусоида даёт один пик, пила — пики на частотах, кратных 220 Гц, белый шум — неровную полосу по всем частотам. Шум усреднён по 16 кадрам; где на единицу ширины приходится несколько частот, показана наибольшая.

На рисунке 1 три сигнала с одинаковой амплитудой 0,5. Синусоида (sine)синус (sine) — Вертикальная координата точки на единичной окружности под заданным углом от положительного направления горизонтальной оси. Для острого угла прямоугольного треугольника равен отношению противолежащего катета к гипотенузе. 1000 Гц даёт один пик на уровне −6 дБ: 20lg⁡0,5≈−6,0220\lg 0{,}5 \approx -6{,}02. Пик не бесконечно узкий: у него есть ширина и спадающее основание; откуда они берутся, объясняют разделы о разрешении и утечке. Пила 220 Гц даёт пики на 220, 440, 660 Гц и дальше — гармоники с амплитудами, пропорциональными 1/k1/k. При удвоении номера гармоники её амплитуда падает вдвое, на 6 дБ. На логарифмической оси гармоники сгущаются к высоким частотам. Белый шум (white noise)белый шум (white noise) — Шум с постоянной спектральной плотностью мощности в рассматриваемой полосе. Полосы одинаковой ширины в герцах содержат одинаковую ожидаемую мощность. пиков не даёт: его мощность в среднем распределена по всем частотам до fs/2f_s/2 поровну, а неровность графика — случайный разброс, который уменьшается при усреднении.

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

Сравнение с пробной синусоидой

Возьмём кадр из NN отсчётов x[0],x[1],…,x[N−1]x[0], x[1], \ldots, x[N-1] и проверим, есть ли в нём синусоида заданной частоты. Для этого каждый отсчёт умножают на отсчёт пробной синусоиды этой частоты и всё складывают (S. W. Smith, гл. 8, с. 157–159). Если в сигнале есть такая синусоида, произведения почти всё время одного знака, и сумма растёт. Если нет, положительные и отрицательные произведения уравновешивают друг друга.

Пусть N=64N = 64, а сигнал — косинусоида с амплитудой 1, которая делает в кадре 3 периода: x[n]=cos⁡(2π⋅3n/64)x[n] = \cos(2\pi \cdot 3n/64). Пробный косинус тех же трёх периодов даёт произведения cos⁡2\cos^2, они неотрицательны, а их среднее равно 1/2. Сумма 64 произведений равна 32, то есть N/2N/2. Пробный косинус с пятью периодами даёт сумму ровно 0. Так получается для любых двух разных целых чисел периодов от 1 до N/2−1N/2 - 1: сумма произведения таких косинусоид по кадру равна нулю. Это свойство называют ортогональностью (orthogonality); на нём основано ДПФ (Sundararajan, §3.1).

Если амплитуда сигнала равна AA, сумма равна AN/2AN/2. Значит, амплитуду можно найти, умножив сумму на 2/N2/N.

Одной пробной синусоиды мало. Сдвинем фазу сигнала на четверть периода: x[n]=cos⁡(2π⋅3n/64+π/2)=−sin⁡(2π⋅3n/64)x[n] = \cos(2\pi \cdot 3n/64 + \pi/2) = -\sin(2\pi \cdot 3n/64). Частота та же, а сумма с пробным косинусом теперь равна нулю: произведения синуса на косинус одной частоты тоже уравновешиваются. Поэтому берут две пробы, косинус и синус, и две суммы:

C=∑n=0N−1x[n]cos⁡2πknN,S=∑n=0N−1x[n]sin⁡2πknNC = \sum_{n=0}^{N-1} x[n]\cos\frac{2\pi kn}{N}, \qquad S = \sum_{n=0}^{N-1} x[n]\sin\frac{2\pi kn}{N}
kk
число периодов пробы в кадре, целое.

Для x[n]=Acos⁡(2πkn/N+φ)x[n] = A\cos(2\pi kn/N + \varphi) при целом kk от 1 до N/2−1N/2 - 1 получается C=(AN/2)cos⁡φC = (AN/2)\cos\varphi и S=−(AN/2)sin⁡φS = -(AN/2)\sin\varphi. Фаза делит длину AN/2AN/2 между двумя суммами, как угол делит радиус между проекциями на единичной окружности. Отсюда амплитуда A=(2/N)C2+S2A = (2/N)\sqrt{C^2 + S^2}, а фаза — угол точки (C,−S)(C, -S).

Пару (C,−S)(C, -S) удобно записать одним комплексным числом C−jSC - jS. По формуле Эйлера cos⁡θ−jsin⁡θ=e−jθ\cos\theta - j\sin\theta = e^{-j\theta}, поэтому обе суммы — это одна сумма с комплексной пробой:

C−jS=∑n=0N−1x[n] e−j2πkn/N=AN2 ejφC - jS = \sum_{n=0}^{N-1} x[n]\,e^{-j2\pi kn/N} = \frac{AN}{2}\,e^{j\varphi}

Модуль результата пропорционален амплитуде, аргумент равен фазе. Это комплексная амплитуда синусоиды, умноженная на N/2N/2.

Визуализация 2Пробная синусоида
Сигнал, пробная синусоида, их произведение и сумма Сигнал x[n] = cos(2π·3·n/64 + 0°), 64 отсчётов. Пробная синусоида — косинус с 3 периодами на кадр. Сверху сигнал (точки) и проба (пунктир), посередине их произведение по отсчётам, внизу накопленная сумма; её последнее значение — одна из двух сумм ДПФ для бина 3. Сумма с косинусом C = 32,00, с синусом S = 0,00. |X[3]| = √(C² + S²) = 32,00; амплитуда 2|X|/N = 1,000, фаза arg X = 0°. Частоты совпадают: произведение почти всё время одного знака, и сумма растёт на протяжении всего кадра. −101−101Сигнал x[n] и пробная синусоидаПроизведение x[n] · проба[n]Накопленная сумма произведений−64−320326432,000816243240485664Номер отсчёта n
  • сигнал x[n], точки — отсчёты
  • пробная синусоида
  • произведение
  • накопленная сумма

Сумма с косинусом C = 32,00, с синусом S = 0,00.

|X[3]| = √(C² + S²) = 32,00; амплитуда 2|X|/N = 1,000, фаза arg X = 0°.

Частоты совпадают: произведение почти всё время одного знака, и сумма растёт на протяжении всего кадра.

Сигнал и пробная синусоида
3 пер.
0°
3
Проба:

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

Рис. 2. Сигнал x[n] = cos(2π·3·n/64 + 0°), 64 отсчётов. Пробная синусоида — косинус с 3 периодами на кадр. Сверху сигнал (точки) и проба (пунктир), посередине их произведение по отсчётам, внизу накопленная сумма; её последнее значение — одна из двух сумм ДПФ для бина 3.

В начальном состоянии визуализации 2 сигнал и пробный косинус делают по 3 периода, и накопленная сумма растёт до 32. Сдвиньте фазу сигнала на 90°: сумма с косинусом станет нулём, сумма с синусом — −32, а модуль и амплитуда не изменятся. Поставьте пробную частоту 5: обе суммы обнулятся. Наконец, поставьте частоту сигнала 3,5 периода: ни на одной пробной частоте сумма не обнуляется, при k=3k = 3 и k=4k = 4 оценки амплитуды 0,59 и 0,68 вместо 1. К этому случаю вернёмся в разделе об утечке.

Дискретное преобразование Фурье

Если повторить комплексную сумму для каждого целого числа периодов kk от 0 до N−1N - 1, получится дискретное преобразование Фурье (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):

X[k]=∑n=0N−1x[n] e−j2πkn/N,k=0,1,…,N−1X[k] = \sum_{n=0}^{N-1} x[n]\,e^{-j2\pi kn/N}, \qquad k = 0, 1, \ldots, N-1
x[n]x[n]
отсчёт кадра, n=0,…,N−1n = 0, \ldots, N-1;
NN
длина кадра, отсчётов;
kk
номер частоты: число периодов пробы в кадре;
X[k]X[k]
комплексный результат, в единицах отсчётов, умноженных на число отсчётов.

ДПФ превращает NN отсчётов в NN комплексных чисел. Для каждого kk выполняется NN комплексных умножений, всего N2N^2. Формула (1) не требует, чтобы сигнал был синусоидой: она применима к любому кадру, а результат описывает этот кадр как сумму синусоид с целым числом периодов.

Бины и частоты

Каждое значение kk называют бином (frequency bin)бин (frequency bin) — Одна частотная точка ДПФ с номером k: частота k·fₛ/N, шаг до соседней fₛ/N. У вещественного сигнала независимы бины от 0 до N/2.. Проба бина kk делает kk периодов за кадр, а кадр длится N/fsN/f_s секунд, где fsf_s — частота дискретизации (sample rate)частота дискретизации (sample rate) — Число отсчётов в каждом канале за одну секунду, то же число кадров аудиоданных в секунду. Измеряется в герцах: 44 100 Гц означает 44 100 отсчётов на канал в секунду.. Отсюда частота бина:

fk=kfsNf_k = \frac{k f_s}{N}
fkf_k
частота бина kk, Гц;
fsf_s
частота дискретизации, Гц.

При N=1024N = 1024 и fs=48f_s = 48 кГц бин 1 соответствует 46,875 Гц, бин 21 — 984,4 Гц, бин 512 — 24 кГц. Синусоида 1000 Гц делает в таком кадре 1000/46,875≈21,331000/46{,}875 \approx 21{,}33 периода и попадает между бинами 21 и 22. Шаг между бинами одинаков на всех частотах (Sundararajan, §3.1, с. 75).

Амплитуда и фаза

Пусть x[n]=Acos⁡(2πk0n/N+φ)x[n] = A\cos(2\pi k_0 n/N + \varphi), где k0k_0 — целое от 1 до N/2−1N/2 - 1. По разделу о пробе X[k0]=(AN/2) ejφX[k_0] = (AN/2)\,e^{j\varphi}, а остальные бины от 1 до N/2N/2 равны нулю. Амплитуду и фазу дают модуль и аргумент:

A=2 ∣X[k0]∣N,φ=arg⁡X[k0]A = \frac{2\,|X[k_0]|}{N}, \qquad \varphi = \arg X[k_0]
AA
амплитуда синусоиды, в единицах отсчётов;
φ\varphi
фаза косинусоиды в начале кадра, рад.

Множитель 2/N2/N не действует для двух бинов. Бин 0 — сумма отсчётов, и X[0]/NX[0]/N — их среднее, постоянная составляющая. Бин N/2N/2 — сумма отсчётов с чередующимся знаком, и X[N/2]/NX[N/2]/N — амплитуда последовательности +1,−1,+1,…+1, -1, +1, \ldots. Для этих бинов амплитуда равна ∣X[k]∣/N|X[k]|/N. Нормировку разные книги и библиотеки выбирают по-разному: Bellanger относит множитель 1/N1/N к прямому преобразованию (Bellanger, §2.2), Sundararajan, J. O. Smith и JUCE — к обратному. Перед расчётом уровней нужно проверить соглашение своей библиотеки.

Таблица 1 — пример из учебника Sundararajan: четыре отсчёта сигнала x[n]=1+cos⁡(2πn/4+π/3)−cos⁡(πn)x[n] = 1 + \cos(2\pi n/4 + \pi/3) - \cos(\pi n) и их ДПФ (Sundararajan, §3.1, с. 70–72).

Таблица 1. ДПФ четырёх отсчётов x = 0,5; 1,134; −0,5; 2,866. Амплитуды получены по формуле (3), для бинов 0 и 2 — делением на N = 4.
kX[k]|X[k]|arg X[k]Составляющая
0440°постоянная 4/4 = 1
11 + j1,732260°2 · 2/4 = 1: cos(2πn/4 + 60°)
2−44180°−4/4: −cos(πn), чередование знака
31 − j1,7322−60°сопряжённая пара бина 1

Для k=1k = 1 сумма с пробой e−j2πn/4e^{-j2\pi n/4}, то есть с числами 1,−j,−1,j1, -j, -1, j, равна 0,5−j1,134+0,5+j2,866=1+j1,7320{,}5 - j1{,}134 + 0{,}5 + j2{,}866 = 1 + j1{,}732. Её модуль 1+3=2\sqrt{1 + 3} = 2 и аргумент atan2⁡(1,732; 1)=60°\operatorname{atan2}(1{,}732;\ 1) = 60° восстанавливают амплитуду 1 и фазу π/3\pi/3 второго слагаемого.

Симметрия и частота Найквиста

В таблице 1 X[3]X[3] — сопряжённое число к X[1]X[1]. Это общее свойство: для вещественного сигнала X[N−k]=X∗[k]X[N-k] = X^*[k] (Sundararajan, §3.1, с. 71). Проба бина N−kN-k — это e−j2π(N−k)n/N=e+j2πkn/Ne^{-j2\pi(N-k)n/N} = e^{+j2\pi kn/N}, вектор, который вращается в обратную сторону: бин N−kN-k соответствует частоте −kfs/N-kf_s/N. Вещественная косинусоида — сумма двух векторов с частотами +f+f и −f-f и половинной длиной (отрицательные частоты). Поэтому каждый из бинов kk и N−kN-k получает по AN/2AN/2, и в формуле (3) стоит множитель 2.

Новых сведений бины выше N/2N/2 не несут, и анализаторы показывают только бины от 0 до N/2N/2 — это N/2+1N/2 + 1 чисел (S. W. Smith, гл. 8). Верхний бин N/2N/2 соответствует fs/2f_s/2, частоте Найквиста (Nyquist frequency)частота Найквиста (Nyquist frequency) — Половина частоты дискретизации, fₛ/2. В принятой низкочастотной модели точное восстановление по отсчётам требует, чтобы все частоты исходного сигнала были строго ниже этой границы.. Частот выше неё в отсчётах нет: если до дискретизации они были, то при ней уже отразились ниже fs/2f_s/2, и ДПФ покажет их на отражённой частоте.

Разрешение по частоте

Шаг между бинами задаёт разрешение по частоте:

Δf=fsN=1T,T=Nfs\Delta f = \frac{f_s}{N} = \frac{1}{T}, \qquad T = \frac{N}{f_s}
Δf\Delta f
шаг между бинами, Гц;
TT
длительность кадра, с.

Шаг по частоте обратен длительности кадра. Bellanger называет это соотношение принципом неопределённости для спектрального анализа: точнее различить частоты можно, только дольше наблюдая сигнал (Bellanger, §2.4.2). Таблица 2 даёт числа для 48 кГц.

Таблица 2. Шаг между бинами и длительность кадра при частоте дискретизации 48 кГц.
NΔf, ГцКадр, мс
256187,55,3
51293,7510,7
102446,87521,3
204823,4442,7
409611,7285,3
81925,86170,7

Ноты E1 и F1 нижнего регистра бас-гитары звучат на частотах 41,20 и 43,65 Гц, между ними 2,45 Гц. Чтобы они попали хотя бы в разные бины, нужен шаг меньше 2,45 Гц, то есть N≥48 000/2,45≈19 600N \ge 48\,000/2{,}45 \approx 19\,600: ближайшая степень двойки — 32 768 отсчётов, 0,68 с. За это время нота может закончиться, а удар барабана в таком кадре не локализовать точнее, чем в пределах всего кадра. Короткий кадр из 256 отсчётов покажет начало удара с точностью 5 мс, но вся полоса от 0 до 187 Гц уложится в один шаг между бинами. Подробная картина по частоте требует длинного кадра, а подробная во времени — короткого.

Можно дополнить кадр нулями до большего NN. Бинов станет больше, но новые значения лишь интерполируют спектр между прежними: два близких пика, которые сливались, так и останутся слитыми (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 дБ. Увеличение NN сужает основание в герцах, но уровни боковых выбросов в децибелах остаются прежними (J. O. Smith).

Окно Ханна

Скачок на стыке можно убрать, если перед ДПФ умножить кадр на функцию, которая плавно сводит его края к нулю. Такой множитель называют оконной функцией (window function)оконная функция (window function) — Множитель, на который поотсчётно умножают кадр перед ДПФ. Сводит края кадра к нулю и уменьшает утечку спектра ценой более широкого пика; пример — окно Ханна., или окном. Без него кадр тоже умножен на окно — прямоугольное, из одних единиц. Одно из самых распространённых плавных окон — окно Ханна (Hann window) (Sundararajan, §5.2.2):

w[n]=0,5−0,5cos⁡2πnN−1,n=0,…,N−1w[n] = 0{,}5 - 0{,}5\cos\frac{2\pi n}{N-1}, \qquad n = 0, \ldots, N-1
w[n]w[n]
множитель отсчёта nn, без единиц, от 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 Гц.

Таблица 3. Синусоида 1000 Гц с амплитудой 0,5, N = 1024, 48 кГц, масштаб 2/N с нормировкой окна. На бине такая синусоида дала бы −6,02 дБ. Свойства лепестков — по J. O. Smith, уровни бинов посчитаны кодом визуализации 3.
ОкноГлавный лепестокПервый боковойПик, дБЧерез 10 бинов, дБЧерез 50 бинов, дБНаибольшая просадка, дБ
прямоугольное±1 бин−13 дБ−7,64−37,4−51,73,82
Ханна±2 бина−31,5 дБ−6,65−76,1−118,71,42

S. W. Smith описывает это как обмен: окно уменьшает утечку, то есть высоту «хвостов» пика, ценой разрешения, то есть ширины пика (S. W. Smith, гл. 9, с. 174–175). Прямоугольное окно лучше различает два тона одинаковой силы на соседних частотах. Окно Ханна лучше показывает слабый тон рядом с сильным; его же использует анализатор из урока JUCE.

Окно уменьшает отсчёты: среднее значение окна Ханна около 1/2, поэтому синусоида на бине без поправки покажет уровень на 6 дБ ниже. JUCE по умолчанию (normalise = true) умножает окно на N/∑w[n]N/\sum w[n], то есть на 2N/(N−1)2N/(N-1). Тогда масштаб 2/N2/N из формулы (3) верен для любого окна.

Визуализация 3Анализатор спектра
Спектр кадра Спектр начала сигнала при 48 кГц: синусоида 1 000 Гц с амплитудой 0,5. Кадр N = 1024, окно Ханна; по горизонтали частота в логарифмической шкале, по вертикали уровни в дБ относительно синусоиды с амплитудой 1, масштаб 2/N. Бин 0 на логарифмической оси не показан. Звук — та же петля 2 с. 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 дБ от пика. Уровень бина, дБ 0−20−40−60−80−100−12020501002005001 к2 к5 к10 к20 к Частота, Гц
  • уровни бинов, точки — отдельные бины
  • частота тона

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 дБ от пика.

Сигнал и параметры анализа
1 000 Гц
Окно:
Шкала:

Громкость. Чистый тон, пила и белый шум звучат резко: перед запуском убавьте громкость, особенно в наушниках.

30 %

Остановлен

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

Рис. 3. Спектр начала сигнала при 48 кГц: синусоида 1 000 Гц с амплитудой 0,5. Кадр N = 1024, окно Ханна; по горизонтали частота в логарифмической шкале, по вертикали уровни в дБ относительно синусоиды с амплитудой 1, масштаб 2/N. Бин 0 на логарифмической оси не показан. Звук — та же петля 2 с.

Визуализация 3 считает спектр начала сигнала тем же способом, что анализатор из раздела о коде: окно с нормировкой, БПФ и масштаб 2/N2/N. Звучит тот же сигнал. В начальном состоянии синусоида 1000 Гц попадает между бинами, и пик −6,65 дБ стоит на бине 21. Переключите окно на прямоугольное: основание расширится, через 10 бинов уровень будет −37 дБ вместо −76. Поставьте 1500 Гц — ровно 32 бина при N=1024N = 1024: прямоугольное окно даст одну линию −6,02 дБ, окно Ханна — пик −6,02 дБ и два соседних бина по −12,04 дБ. На сигнале «два тона» слабый тон 1600 Гц с прямоугольным окном виден только при N=4096N = 4096, а с окном Ханна — уже при 512. Выберите белый шум и меняйте NN: уровень бинов при каждом удвоении падает на 3 дБ, а уровень синусоиды не меняется.

Быстрое преобразование Фурье

По формуле (1) кадр из 1024 отсчётов требует 10242=1 048 5761024^2 = 1\,048\,576 комплексных умножений. Анализатор с перекрытием кадров на половину считает при 48 кГц около 94 кадров в секунду — около 98 миллионов комплексных умножений в секунду на один канал.

Быстрое преобразование Фурье (fast Fourier transform, FFT)быстрое преобразование Фурье (fast Fourier transform, FFT) — Алгоритм, который вычисляет то же ДПФ за число операций порядка N·log₂N вместо N². Распространённые реализации требуют, чтобы N было степенью двойки., БПФ, вычисляет те же X[k]X[k] с точностью до округления, но за число операций порядка Nlog⁡2NN\log_2 N, а не N2N^2 (запись O). Простейший вариант делит кадр на чётные и нечётные отсчёты, вычисляет два ДПФ половинной длины и собирает из них результат, добавляя N/2N/2 умножений. Половины делятся дальше, пока не останутся пары отсчётов. Ступеней log⁡2N\log_2 N, на каждой N/2N/2 умножений, всего (N/2)log⁡2N(N/2)\log_2 N. Bellanger уточняет, что преобразования из двух отсчётов, до которых доходит деление, умножений не требуют, и получает (N/2)log⁡2(N/2)(N/2)\log_2(N/2) (Bellanger, §2.2.1). Элементарное действие алгоритма, которое из двух чисел делает два новых, называют бабочкой (butterfly) по виду его схемы (S. W. Smith, гл. 12).

Таблица 4. Комплексные умножения на кадр: ДПФ по формуле (1) и БПФ по основанию 2 без учёта тривиальных множителей.
NДПФ, N²БПФ, (N/2)·log₂NВо сколько раз меньше
25665 536102464
10241 048 5765120205
409616 777 21624 576683

У S. W. Smith БПФ на 1024 точки на компьютере 1990-х годов работает в 300 с лишним раз быстрее, чем ДПФ по формуле (S. W. Smith, гл. 12, с. 237). Выигрыш растёт с NN. Деление пополам требует, чтобы NN было степенью двойки: juce::dsp::FFT принимает показатель степени order, и размер кадра равен 2order2^{order} (juce_FFT.h). Для других длин существуют свои алгоритмы, но обычно кадр дополняют нулями до степени двойки (Sundararajan, гл. 10, с. 357).

Анализатор спектра

Уровни и ось частот

Анализатор переводит амплитуду бина в децибелы: Lk=20lg⁡(2∣X[k]∣/N)L_k = 20\lg\bigl(2|X[k]|/N\bigr). При таком масштабе синусоида с амплитудой 1 на бине показывает 0 дБ, а с амплитудой 0,5 — −6,02 дБ, как на рисунке 1. Опорный уровень выбирают по-разному. В уроке JUCE об анализаторе из уровня вычитают gainToDecibels (fftSize), то есть делят модуль на NN, а не на N/2N/2, и синусоида полной шкалы там показывает −6 дБ (JUCE, Spectrum analyser tutorial). Анализатор полезно проверить тестовой синусоидой известной амплитуды.

Ось частот у анализаторов обычно логарифмическая, как слух воспринимает высоту, а бины ДПФ идут с равным шагом в герцах. При N=1024N = 1024 ниже 200 Гц всего четыре бина — 46,9; 93,8; 140,6 и 187,5 Гц, а выше 10 кГц — почти триста. На логарифмической оси низ спектра выглядит ломаной из нескольких точек, а верх — плотной полосой. Поэтому для низких частот берут большие NN, а на высоких частотах при рисовании соседние бины объединяют: в визуализации 3 на одну точку экрана выводится наибольший из бинов.

Перекрытие и усреднение

Звук идёт непрерывно, и анализатор берёт кадр за кадром. Окно Ханна почти обнуляет края кадра, поэтому отсчёты у границ кадров почти не учитываются. Чтобы не терять их, кадры перекрывают: новый кадр начинается через N/2N/2 отсчётов после предыдущего. Перекрытие на половину и больше — обычный выбор (J. O. Smith, Classic Spectrograms). При N=1024N = 1024 и 48 кГц новый спектр появляется каждые 512 отсчётов, 93,75 раза в секунду. Экран обновляется 60 раз в секунду, и между кадрами экрана анализатор показывает последний готовый спектр.

Спектр одного кадра шума неровный: соседние бины отличаются на 5–10 дБ случайным образом. Длинный кадр не помогает: бинов становится больше, а разброс прежний (S. W. Smith, гл. 9, с. 170). Помогает усреднение спектров нескольких кадров: случайные колебания уменьшаются пропорционально корню из числа кадров. В визуализации 3 среднее 16 кадров уменьшает разброс уровней белого шума с 5–6 до 1,1 дБ. Заметен и уровень шума: при каждом удвоении NN он падает на 3 дБ. Мощность шума та же, но делится между вдвое большим числом бинов, а уровень синусоиды от NN не зависит. Сравнивать уровень шума и тона на спектре можно только при известном NN.

Спектрограмма

Если спектры кадров выстроить по времени, получится спектрограмма (spectrogram): время по горизонтали, частота по вертикали, уровень — яркостью (J. O. Smith, Classic Spectrograms). На рисунке 4 один и тот же сигнал разобран кадрами двух размеров.

Спектрограммы одного сигнала при N = 256 и N = 4096Две спектрограммы друг под другом, время от 0 до 2 с по горизонтали, частота от 200 Гц до 6 кГц по вертикали. Верхняя, N = 256: широкая размытая полоса около 440 Гц, во второй части она не делится на два тона; короткий щелчок на 1,3 с — узкая вертикальная черта около 3 кГц; свип — размытая растущая кривая. Нижняя, N = 4096: тонкая линия 440 Гц, во второй части рядом с ней вторая линия 494 Гц; щелчок растянут по времени примерно на 0,1 с; свип — тонкая кривая. N = 256: кадр 5,3 мс, Δf 187,5 Гц2005001 к2 к5 кN = 4096: кадр 85,3 мс, Δf 11,7 Гц2005001 к2 к5 к0,00,51,01,52,0Время, с
Рис. 4. Спектрограммы одного сигнала при 48 кГц, окно Ханна, столбцы через 20 мс. Сигнал: до 0,6 с синусоида 440 Гц, до 1,2 с к ней добавлена 494 Гц, на 1,3 с — щелчок синусоиды 3000 Гц длиной 20 мс, с 1,4 до 2 с — свип от 200 до 6000 Гц. Частота от 200 Гц до 6 кГц в логарифмической шкале; чем темнее, тем выше уровень: пять ступеней через 12 дБ от −12 до −60 дБ.

При N=256N = 256 кадр длится 5,3 мс: смена нот и щелчок видны точно по времени, но тоны 440 и 494 Гц, между которыми 54 Гц, сливаются в одну широкую полосу — шаг бинов 187,5 Гц. При N=4096N = 4096 кадр длится 85 мс, шаг 11,7 Гц, и два тона — отдельные линии. Зато щелчок длиной 20 мс растянут на 0,1 с, а смена нот размыта. Это компромисс из раздела о разрешении.

Обратное ДПФ

Из NN чисел X[k]X[k] отсчёты восстанавливаются точно (Sundararajan, §3.1, формула 3.2):

x[n]=1N∑k=0N−1X[k] e+j2πkn/N,n=0,…,N−1x[n] = \frac{1}{N}\sum_{k=0}^{N-1} X[k]\,e^{+j2\pi kn/N}, \qquad n = 0, \ldots, N-1

От формулы (1) она отличается знаком в показателе и множителем 1/N1/N. Проверим на таблице 1 при n=0n = 0: все экспоненты равны 1, и x[0]=(4+1+j1,732−4+1−j1,732)/4=0,5x[0] = (4 + 1 + j1{,}732 - 4 + 1 - j1{,}732)/4 = 0{,}5. Обратное преобразование складывает все синусоиды бинов с их амплитудами и фазами.

Пара прямого и обратного ДПФ позволяет обрабатывать звук в частотной области: вычислить спектр кадра, изменить X[k]X[k] и вернуться к отсчётам. Простое обнуление бинов работает плохо: ДПФ считает кадр периодическим, и изменения переходят через края кадра. Правильная схема опирается на свёртку: умножение спектров соответствует свёртке сигналов. Через БПФ свёртка длинных сигналов выполняется за O(Nlog⁡N)O(N\log N) операций вместо O(N2)O(N^2) (Sundararajan, §3.3.9, с. 90), так работает свёрточный ревербератор. Свёртку и фильтры разбирает статья о цифровых фильтрах. В JUCE обратное преобразование выполняет performRealOnlyInverseTransform; множитель 1/N1/N она применяет сама.

Код на C++

Наивное ДПФ

Функция naiveDft вычисляет формулу (1) двумя вложенными циклами. Для анализатора она слишком медленная, N2N^2 умножений, но годится как эталон: в тестах с ней сравнивают результат БПФ на коротких кадрах. Нужна только стандартная библиотека C++23.

C++
// ДПФ по определению: 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) строит ej⋅anglee^{j\cdot angle} — точку единичной окружности (математический справочник). Угол берётся по остатку kn mod Nkn \bmod N: проба периодична с периодом NN, а аргумент синуса и косинуса остаётся меньше 2π2\pi. Сумма накапливается в double. Код собран clang++ -std=c++20 -Wall -Wextra -Wconversion без предупреждений. На косинусоиде с тремя периодами в 64 отсчётах он даёт ∣X[3]∣=32|X[3]| = 32, то есть амплитуду 1, а на отсчётах таблицы 1 — те же X[k]X[k].

Анализатор на JUCE

Анализатор делит работу между двумя потоками, как в статье об аудиопотоке. Аудиопоток в processBlock только копирует отсчёты в очередь без блокировок на juce::AbstractFifo. Поток сообщений забирает их, собирает кадр, применяет окно, вычисляет БПФ и уровни.

C++
// Анализатор: аудиопоток кладёт отсчёты в очередь, поток сообщений считает спектр кадра.
#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 требует массив из 2N2N чисел: первая половина — отсчёты, вторая — рабочее место. С аргументом true она вычисляет только бины от 0 до N/2N/2 и записывает их модули ∣X[k]∣|X[k]| в начало массива (juce_FFT.h). Прямое преобразование в JUCE не делит на NN: это сумма формулы (1). WindowingFunction с окном hann по умолчанию нормирует окно, поэтому масштаб 2/N2/N даёт амплитуду синусоиды. Бины 0 и N/2N/2 получают масштаб 1/N1/N. gainToDecibels с порогом −140 дБ возвращает −140 для нулевого модуля, а не −∞-\infty.

Код собран с 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 Гц?

Δf=44 100/2048≈21,53\Delta f = 44\,100/2048 \approx 21{,}53 Гц, кадр 2048/44 100≈46,42048/44\,100 \approx 46{,}4 мс. 1000/21,53≈46,441000/21{,}53 \approx 46{,}44: синусоида лежит между бинами 46 и 47, и пик будет на бине 46.

N = 8, x[n] = cos(2π · 2n/8). Чему равны X[k] и какую амплитуду даст формула (3)?

Сигнал делает 2 периода в кадре: X[2]=X[6]=N/2=4X[2] = X[6] = N/2 = 4, остальные бины равны нулю. Бин 6 — сопряжённая пара бина 2, частота −2fs/8-2f_s/8. Амплитуда 2⋅4/8=12 \cdot 4/8 = 1, фаза arg⁡X[2]=0\arg X[2] = 0.

Почему при удвоении N уровень бинов белого шума падает на 3 дБ, а уровень синусоиды — нет?

Мощность шума распределена по всем частотам, и при удвоении NN она делится между вдвое большим числом бинов: мощность на бин уменьшается вдвое, на 3 дБ. Синусоида сосредоточена на одной частоте и попадает в один бин или несколько соседних при любом NN: её амплитуда по формуле (3) от NN не зависит.

Сколько комплексных умножений нужно на кадр из 4096 отсчётов по формуле (1) и в БПФ по основанию 2?

40962=16 777 2164096^2 = 16\,777\,216 по формуле и (4096/2)⋅log⁡24096=2048⋅12=24 576(4096/2) \cdot \log_2 4096 = 2048 \cdot 12 = 24\,576 в БПФ, примерно в 683 раза меньше.

Генератор выдаёт синусоиду с амплитудой 0,5 точно на частоте бина, а анализатор с окном Ханна показывает −12 дБ вместо −6. В чём ошибка?

Окно не нормировано: его сумма около N/2N/2, поэтому модуль бина уменьшился вдвое, на 6,02 дБ. Нужно умножить окно на N/∑w[n]N/\sum w[n], в JUCE — оставить normalise = true.

Источники

Связи

Связи списком

Ссылается на

Открыть в общем графе

Поиск

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