오디오 기초 — 파형, 샘플링, 푸리에 변환

파형(Waveform)은 원시 신호이고, 스펙트로그램(Spectrogram)은 그것을 다시 표현한 형태이며, 멜 피처(Mel feature)는 머신러닝(ML)이 다루기 좋은 형태입니다. 현대 음성 인식(ASR)과 음성 합성(TTS) 파이프라인은 모두 이 사다리를 올라가며, 그 첫 칸은 샘플링(sampling)과 푸리에 변환(Fourier transform)을 이해하는 것입니다.

유형: Learn 언어: Python 선수 강의: Phase 1 · 06 (Vectors & Matrices), Phase 1 · 14 (Probability Distributions) 예상 시간: 약 45분

학습 목표

  • 파형(waveform), 샘플링 레이트(sampling rate), 비트 깊이(bit depth)를 구분합니다.
  • 나이퀴스트-섀넌(Nyquist-Shannon) 정리와 앨리어싱(aliasing)이 오디오 품질에 미치는 영향을 설명합니다.
  • 이산 푸리에 변환(DFT), 빠른 푸리에 변환(FFT), 단시간 푸리에 변환(STFT)이 원시 오디오를 주파수 표현(frequency representation)으로 바꾸는 흐름을 이해합니다.
  • 음성 인식(ASR)과 음성 합성(TTS) 모델이 기대하는 샘플링 레이트와 채널 형식을 확인하는 습관을 만듭니다.

문제

마이크는 시간에 따른 압력 신호를 만들어 냅니다. 신경망(neural net)은 텐서(tensor)를 입력으로 받습니다. 그 사이에는 여러 가지 관례(convention)가 쌓여 있으며, 이를 어기는 순간 겉으로는 드러나지 않는 버그가 발생합니다. 모델은 잘 학습되는 것처럼 보이지만 단어 오류율(WER)이 두 배가 되거나, 음성 합성(TTS) 결과물에 잡음(hiss)이 섞이거나, 음성 복제(voice cloning) 시스템이 화자의 목소리 대신 마이크의 특성을 외워 버립니다.

음성 시스템의 버그는 대부분 다음 세 가지 질문 중 하나로 귀결됩니다.

  1. 데이터는 어떤 샘플링 레이트로 녹음되었고, 모델은 어떤 샘플링 레이트를 기대합니까?
  2. 신호에 앨리어싱(aliasing)이 일어났습니까?
  3. 원시 샘플(raw sample) 위에서 작업하고 있습니까, 아니면 주파수 표현(frequency representation) 위에서 작업하고 있습니까?

이 세 가지를 맞추면 Phase 6의 나머지 학습이 다룰 만해집니다. 반대로 어긋난 상태로 두면 Whisper-Large-v4 같은 강력한 모델조차 엉뚱한 결과를 만들어 냅니다.

사전 테스트

2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요

1.오디오 신호를 48 kHz에서 16 kHz로 다운샘플링(downsampling)하기 전에 저역 통과 필터(low-pass filter)를 반드시 적용해야 하는 이유는 무엇인가요?

2.16 kHz 모노 오디오 클립이 5초 길이입니다. 파형(waveform) 배열에는 몇 개의 float 값이 들어 있으며, 이것이 모델 입력에 왜 중요한가요?

0/2 답변 완료

개념

waveform → samples → spectrum continuous pressure microphone output, smooth in time sampled at sr Hz float array, indexed by n / sr DFT magnitude 0 sr/2 energy per frequency bin sample DFT aliasing — energy above Nyquist folds sr/2 (Nyquist) f_true = 7 kHz alias = 3 kHz fix: low-pass before downsample 2026 sample rates 8 kHztelephony — avoid for ASR 16 kHzASR standard (Whisper, Parakeet) 24 kHzmodern TTS (Kokoro, F5, xTTS) 44.1 kHzCD audio, music 48 kHzfilm, pro audio, hi-fi TTS first question before any ML pipeline

파형(Waveform). [-1.0, 1.0] 범위의 실수(float)로 이루어진 1차원 배열입니다. 인덱스는 샘플 번호(sample number)이며, 초 단위 시간으로 바꾸려면 샘플링 레이트로 나눕니다(t = n / sr). 16 kHz로 녹음한 10초 클립은 160,000개의 float로 이루어진 배열입니다.

샘플링 레이트(Sampling rate, sr). 1초에 몇 개의 샘플을 측정하는지를 나타냅니다. 2026년 기준으로 자주 쓰이는 값은 다음과 같습니다.

샘플링 레이트용도
8 kHz전화망(telephony)과 구형 VOIP에서 사용합니다. 나이퀴스트 한계가 4 kHz이므로 자음이 많이 손실되어 음성 인식(ASR)에는 피합니다.
16 kHz음성 인식(ASR) 표준입니다. Whisper, Parakeet, SeamlessM4T v2가 모두 16 kHz를 사용합니다.
22.05 kHz오래된 음성 합성(TTS) 보코더(vocoder) 학습에서 자주 쓰입니다.
24 kHzKokoro, F5-TTS, xTTS v2 같은 현대 음성 합성(TTS) 모델에서 쓰입니다.
44.1 kHzCD 오디오와 음악에서 쓰입니다.
48 kHz영화, 프로 오디오, 고음질 음성 합성(VALL-E 2, NaturalSpeech 3)에서 쓰입니다.

나이퀴스트-섀넌 정리(Nyquist-Shannon). 샘플링 레이트 srsr/2까지의 주파수를 모호함 없이 표현할 수 있습니다. 이 경계가 *나이퀴스트 주파수(Nyquist frequency)*입니다. 나이퀴스트 위쪽의 에너지는 낮은 주파수 쪽으로 접혀 들어와 신호를 손상시키며, 이것을 *앨리어싱(aliasing)*이라고 부릅니다. 따라서 다운샘플링(downsampling)하기 전에는 항상 저역 통과 필터(low-pass filter)를 적용해야 합니다.

비트 깊이(Bit depth). 16-bit PCM(부호 있는 int16, 범위 ±32,767)은 범용 교환 포맷입니다. 음악에서는 24-bit, 내부 디지털 신호 처리(DSP)에서는 32-bit float가 자주 쓰입니다. soundfile 같은 라이브러리는 파일을 int16으로 읽더라도 [-1, 1] 범위의 float32 배열로 노출합니다.

푸리에 변환(Fourier Transform). 유한한 신호는 서로 다른 주파수를 가진 정현파(sinusoid)들의 합으로 볼 수 있습니다. 이산 푸리에 변환(Discrete Fourier Transform; DFT)은 N개의 샘플에 대해 주파수 빈(frequency bin)마다 하나씩 모두 N개의 복소수 계수를 계산합니다. bin kk · sr / N Hz에 대응하고, 그 크기(magnitude)는 해당 주파수의 진폭, 각도(angle)는 위상입니다.

빠른 푸리에 변환(FFT). N이 2의 거듭제곱일 때 이산 푸리에 변환(DFT)을 O(N log N)에 계산하는 알고리즘이 빠른 푸리에 변환(Fast Fourier Transform; FFT)입니다. 모든 오디오 라이브러리는 내부적으로 FFT를 사용합니다. 16 kHz 신호에서 1024-샘플 FFT를 쓰면 0~8 kHz 범위에 걸쳐 약 15.6 Hz 해상도의 사용 가능한 주파수 빈 512개를 얻습니다.

프레임 분할과 윈도잉(Framing + window). 클립 전체를 한 번에 FFT하지 않습니다. 보통 25 ms 프레임을 10 ms 홉(hop)으로 잘라 겹치도록 만들고, 각 프레임에 한(Hann) 또는 해밍(Hamming) 윈도우 함수를 곱해 가장자리 불연속을 줄인 뒤 FFT를 수행합니다. 이것이 단시간 푸리에 변환(Short-Time Fourier Transform; STFT)이며, Lesson 02에서 이 지점부터 이어집니다.

직접 만들기

Step 1: 클립을 읽고 파형 확인

code/main.py는 외부 의존성 없이 데모를 보여주기 위해 표준 라이브러리의 wave 모듈만 사용합니다. 실제 운영 환경에서는 보통 soundfile이나 torchaudio.load를 사용하며, 둘 다 (waveform, sr) 튜플을 반환합니다.

import soundfile as sf
waveform, sr = sf.read("clip.wav", dtype="float32")  # shape (T,), sr=int

Step 2: 정현파를 처음부터 합성

import math

def sine(freq_hz, sr, seconds, amp=0.5):
    n = int(sr * seconds)
    return [amp * math.sin(2 * math.pi * freq_hz * i / sr) for i in range(n)]

16 kHz 샘플링 레이트에서 1초짜리 440 Hz 정현파(콘서트 A음)는 16,000개의 float로 이루어집니다. wave.open(..., "wb")와 16-bit PCM 인코딩으로 WAV 파일로 저장할 수 있습니다.

Step 3: 이산 푸리에 변환(DFT)을 직접 계산

def dft(x):
    N = len(x)
    out = []
    for k in range(N):
        re = sum(x[n] * math.cos(-2 * math.pi * k * n / N) for n in range(N))
        im = sum(x[n] * math.sin(-2 * math.pi * k * n / N) for n in range(N))
        out.append((re, im))
    return out

O(N²)이므로 N=256 정도에서 정답성을 확인하는 데만 적합하고, 실제 오디오에는 쓰지 않습니다. 실전 코드에서는 numpy.fft.rfft 또는 torch.fft.rfft를 호출합니다.

Step 4: 주된 주파수 찾기

크기(magnitude)의 최대값 인덱스 k_stark_star * sr / N으로 주파수에 대응합니다. 440 Hz 정현파에서 실행하면 440 * N / sr 빈에서 정점이 나타나야 합니다.

Step 5: 앨리어싱(aliasing) 시연

10 kHz로 7 kHz 정현파를 샘플링해 봅니다(나이퀴스트는 5 kHz). 7 kHz 톤은 나이퀴스트보다 높은 주파수이므로 10 − 7 = 3 kHz로 접혀 들어옵니다. FFT의 정점은 3 kHz에서 나타나며, 이것이 고전적인 앨리어싱 데모이자 모든 DAC/ADC가 벽돌담(brick-wall) 형태의 저역 통과 필터(low-pass filter)를 함께 탑재하는 이유입니다.

사용해보기

2026년에 실제로 출하(ship)하게 될 스택은 다음과 같습니다.

작업라이브러리이유
WAV/FLAC/OGG 읽기/쓰기soundfile (libsndfile 래퍼)빠르고 안정적이며 float32를 반환합니다.
리샘플링(resample)torchaudio.transforms.Resample 또는 librosa.resample안티 앨리어싱(anti-aliasing)이 올바르게 내장되어 있습니다.
STFT / Meltorchaudio 또는 librosaGPU 친화적이며 PyTorch 생태계와 잘 맞습니다.
실시간 스트리밍(real-time streaming)sounddevice 또는 pyaudio크로스 플랫폼 PortAudio 바인딩입니다.
파일 점검ffprobe 또는 soxiCLI에서 빠르게 샘플링 레이트, 채널, 코덱(codec)을 보고합니다.

판단 기준은 간단합니다. 다른 어떤 것보다 먼저 샘플링 레이트를 맞추세요. Whisper는 16 kHz 모노(mono) float32를 기대합니다. 44.1 kHz 스테레오(stereo)를 그대로 넘기면 마치 모델 버그처럼 보이는 쓰레기 결과가 나옵니다.

산출물 만들기

outputs/skill-audio-loader.md로 저장합니다. 이 스킬은 오디오 입력이 다운스트림(downstream) 모델의 기대값과 맞는지 점검하고, 맞지 않을 때 안전하게 리샘플링할 수 있도록 도와줍니다.

연습문제

  1. 쉬움. 16 kHz에서 1초짜리 220 Hz + 440 Hz + 880 Hz 혼합 신호를 합성합니다. 이산 푸리에 변환(DFT)을 실행해 예상한 빈에서 세 개의 정점이 나오는지 확인합니다.
  2. 중간. 48 kHz로 3초짜리 본인 음성 WAV를 녹음합니다. torchaudio.transforms.Resample로 안티 앨리어싱을 적용해 16 kHz로 다운샘플링한 결과와, 안티 앨리어싱 없이 단순히 세 샘플마다 하나씩만 취하는 나이브(naive) 데시메이션(decimation)으로 16 kHz로 만든 결과의 FFT를 비교합니다. 앨리어싱이 어디에 나타납니까?
  3. 어려움. Step 3에서 만든 이산 푸리에 변환(DFT)과 math만 사용해 단시간 푸리에 변환(STFT)을 처음부터 구현합니다. 프레임 크기 400, 홉 160, 한(Hann) 윈도우를 사용합니다. matplotlib.pyplot.imshow로 크기(magnitude)를 그리면 이것이 바로 Lesson 02에서 다룰 스펙트로그램입니다.

핵심 용어

용어흔한 설명실제 의미
샘플링 레이트(Sample rate)초당 샘플 수아날로그-디지털 변환기(ADC)가 신호를 측정하는 Hz 단위의 주파수입니다.
나이퀴스트(Nyquist)표현 가능한 최대 주파수sr/2이며, 그 위의 에너지는 아래로 앨리어싱되어 접혀 들어옵니다.
비트 깊이(Bit depth)각 샘플의 해상도int16은 65,536단계, float32[-1, 1] 안에서 24-bit 정밀도를 가집니다.
이산 푸리에 변환(DFT)시퀀스용 푸리에 변환N개의 샘플을 N개의 복소수 주파수 계수로 변환합니다.
빠른 푸리에 변환(FFT)빠른 DFTN이 2의 거듭제곱이어야 하는 O(N log N) 알고리즘입니다.
빈(Bin)주파수 열(column)k · sr / N Hz에 대응하며, 해상도는 sr / N입니다.
단시간 푸리에 변환(STFT)스펙트로그램의 기반시간축을 따라 프레임을 자르고 윈도우를 곱한 뒤 FFT한 결과입니다.
앨리어싱(Aliasing)이상한 주파수 유령나이퀴스트 위쪽 에너지가 낮은 빈으로 거울처럼 접혀 내려오는 현상입니다.

더 읽을거리

실습 코드

이 강의의 실습 코드 1개

main
Code

산출물

이 강의에서 생성된 프롬프트, 스킬, 코드 산출물 1개

audio-loader

Validate a raw audio file against a target model's expectations and resample it safely.

Skill

확인 문제

3문제 · 모두 맞추면 완료 표시가 가능합니다

1.44.1 kHz 스테레오 WAV 파일을 Whisper(16 kHz 모노 float32를 기대)에 그대로 넘겼더니 전사 결과가 무작위 단어처럼 보입니다. 가장 가능성 높은 원인은 무엇인가요?

2.10 kHz로 7 kHz 정현파(sine wave)를 샘플링했더니 FFT 정점이 3 kHz에서 나타납니다. 왜 이런 일이 일어나나요?

3.단시간 푸리에 변환(STFT)에서 각 프레임에 윈도우 함수(예: 한(Hann) 윈도우)를 곱한 뒤 FFT를 수행하는 이유는 무엇인가요?

0/3 답변 완료

추가 문제 풀기

AI가 강의 내용을 바탕으로 새로운 문제를 생성합니다