STFT и спектрограмма: спектр во времени

Решаем проблему обычного ДПФ: оно показывает «средний» спектр, а нам нужно видеть, как спектр меняется во времени.

STFT (кратковременное преобразование Фурье) — нарезка сигнала на короткие кадры и ДПФ каждого кадра; результат — спектрограмма, картина «частота × время».

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

Идея: режем на кадры

Сигнал делят на короткие кадры (например, по 8–1024 отсчёта), каждый умножают на окно и считают ДПФ. Получается набор спектров — по одному на каждый момент времени. Возьмём сигнал, у которого в первой половине низкий тон, во второй — высокий, и проанализируем по кадрам.

import math, cmath

def dft(x):
    N = len(x)
    return [sum(x[n] * cmath.exp(-2j * math.pi * k * n / N) for n in range(N))
            for k in range(N)]

def peak_bin(frame):
    X = dft(frame); N = len(frame)
    mags = [abs(X[k]) for k in range(N // 2 + 1)]
    return mags.index(max(mags))

fs = 8.0
frame1 = [math.sin(2 * math.pi * 1 * n / fs) for n in range(8)]   # низкий тон
frame2 = [math.sin(2 * math.pi * 3 * n / fs) for n in range(8)]   # высокий тон

print("Кадр 1: пик на бине", peak_bin(frame1), "->", peak_bin(frame1) * fs / 8, "Гц")
print("Кадр 2: пик на бине", peak_bin(frame2), "->", peak_bin(frame2) * fs / 8, "Гц")

Вывод:

Кадр 1: пик на бине 1 -> 1.0 Гц
Кадр 2: пик на бине 3 -> 3.0 Гц

Первый кадр показал 1 Гц, второй — 3 Гц. Мы увидели, что частота выросла со временем — то, чего обычное ДПФ всего сигнала не показало бы.

Спектрограмма: рисуем частоту во времени

Если выложить спектры кадров в строки (время по вертикали, частота по горизонтали) и закрасить ячейки по амплитуде, получится спектрограмма. Нарисуем её ASCII-символами.

import math, cmath

def dft(x):
    N = len(x)
    return [sum(x[n] * cmath.exp(-2j * math.pi * k * n / N) for n in range(N))
            for k in range(N)]
def mag(frame):
    X = dft(frame); N = len(frame)
    return [round(abs(X[k]) / (N / 2), 2) for k in range(N // 2 + 1)]

fs = 8.0
frames = [
    [math.sin(2 * math.pi * 1 * n / fs) for n in range(8)],
    [math.sin(2 * math.pi * 2 * n / fs) for n in range(8)],
    [math.sin(2 * math.pi * 3 * n / fs) for n in range(8)],
]
print("частоты:  0  1  2  3  4 Гц")
for i, fr in enumerate(frames):
    row = "".join("#" if v > 0.5 else ("." if v > 0.1 else " ") for v in mag(fr))
    print(f"кадр {i}: |{row}|")

Вывод:

частоты:  0  1  2  3  4 Гц
кадр 0: | #   |
кадр 1: |  #  |
кадр 2: |   # |

Решётка «#» ползёт слева направо — это чирп, тон с растущей частотой. Спектрограмма наглядно показала движение частоты во времени; так выглядят свистки, сирены, восходящие глиссандо.

Кадр, перекрытие и компромисс

Длина кадра задаёт фундаментальный компромисс между разрешением по времени и по частоте. Короткий кадр — хорошо видно когда сменилась частота (точность по времени), но плохо какая именно (грубое разрешение по частоте fs/N). Длинный кадр — наоборот. Это проявление принципа неопределённости в DSP: нельзя одновременно точно знать и время, и частоту события. Чтобы сгладить швы между кадрами, их берут с перекрытием (обычно 50%) и оконной функцией.

Как работает под капотом

STFT — это мост между DSP и аудио-AI. Спектрограмма — это, по сути, «изображение» звука: по нему распознавалки речи и музыки работают как с картинкой, подавая её в нейросеть. Популярный вариант — мел-спектрограмма, где частотная ось искажена под восприятие человеческого уха (мел-шкала), что делает признаки ближе к тому, как слышим мы. На спектрограмме видны форманты гласных, гармоники нот, шумовые согласные — поэтому она основной инструмент анализа речи. Связь с курсом «Распознавание речи»: там спектрограмма — вход модели, а здесь мы видим, как она устроена изнутри.

Частые ошибки

  • Брать слишком длинный кадр для быстрых событий. Короткий щелчок «размажется» по времени и потеряет локализацию.
  • Анализировать кадры без окна и перекрытия. На стыках появятся артефакты, спектрограмма «зарябит».
  • Ждать одновременно идеального разрешения по времени и частоте. Это запрещено принципом неопределённости — всегда компромисс.

Итог

  • STFT режет сигнал на кадры и считает ДПФ каждого — спектр становится функцией времени.
  • Спектрограмма — картина «частота × время», по сути «изображение» звука.
  • Длина кадра — компромисс между разрешением по времени и по частоте.
  • Спектрограмма (и мел-вариант) — основной вход для распознавания речи и музыки.
Проверьте себя
1. Какую проблему обычного ДПФ решает STFT?
AДПФ слишком медленное
BДПФ даёт один спектр на весь сигнал и не показывает, как частоты меняются во времени
CДПФ не работает с шумом
DДПФ требует степени двойки
2. Что показывает спектрограмма?
AТолько средний спектр
BКартину «частота × время» — как меняются частоты
CФорму сигнала во времени
DУровень квантования
3. В чём компромисс при выборе длины кадра STFT?
AМежду скоростью и памятью
BМежду разрешением по времени и разрешением по частоте
CМежду амплитудой и фазой
DКомпромисса нет