본문 바로가기
개발(Development)/Media(오디오&비디오 개발)

오디오 파형 분석(FFT: 고속 푸리에 변환): Time Domain Data to Frequency Data

by 카레유 2021. 11. 26.

이번 글에서는

소리에 대한 내용을 좀더 깊게 다루어 볼 예정이다.

또한 FFT(고속 퓨리에 변환)을 통해 소리를 분석하는 방법도 간단하게 정리한다.

 

▼이 글을 읽기 전에 아래에서 다룬 소리 기초 내용을 알고 있으면 좋다.▼

소리란? 소리의 기초 원리 (feat. 마이크와 스피커의 작동 원리)

소리의 진폭, 주기, 주파수, 파형 (볼륨, 음정, 음색의 결정 원리)

오디오 데이터 추출 및 변환: ADC, 샘플링, 샘플레이트, 채널

 

이 글에서 다루는 내용은 조금 길고 어려울 수 있지만,

최대한 쉽게 정리하려고 노력했다.

 

특히 Javascript Web Audio API 의 AnlayserNode 를 사용하려는 개발자 분들께는 큰 도움이 될 것 같다.

* 글 중간 중간에 사용된 FDI(For Developer's Informaition) 표기는 개발자를 위한 참고 주석입니다.


# 소리의 파형(Wave Form)

1. 소리와 파형

소리는 공기의 밀도(기압)가 진동하며 343 m/s 의 속도로 퍼져나가는 현상이다.

시간에 따라 공기의 밀도가 변화하는 모습을 그래프로 그리면 아래와 같다.

출처: https://cecm.indiana.edu/361/acoustics.html

소리의 밀도가 변화하는 모양을 파형(Wave Form)이라고 한다.

* FDI: 시간(Time)에 따라 변화하는 진폭(Amplitude) 데이터를 Time Domain Data라고 한다.

 

 

2. 기본 파형

파형(Wave Form)은 소리마다 모두 다르다.

대표적인 기본 파형은 아래의 것들이 있다.

(수식은 그냥 참고만 하세요)

 

1) 정현파 (사인파, sine wave): sin (2 π t).

2) 방형파(square wave): saw(t) − saw (t − duty).

3) 삼각파(triangle wave): (t − 2 floor ((t + 1) /2)) (−1)floor ((t + 1) /2).

4) 톱니파(sawtooth wave): 2 (t − floor(t)) − 1.

 

각각을 그래프로 그리면 아래와 같다.

출처: https://en.wikipedia.org/wiki/Waveform

 

그러나 이렇게 깔끔한 모향을 갖는 기본 파형의 소리는

이론적으로만 존재하거나, 인공적으로 만들어낸 경우가 대부분이다.

 

즉, 컴퓨터를 통해 특정 주파수(진동수)로 재생하는 시스템 알림음(삐---) 이나

피리 등으로 특정 음을 연주할 때만 나타나는 파형이다.

 

* FDI: 특정 주파수(진동수)의 소리를 발생시키는 장치를 Oscillator라고 부른다.

* FDI: '규칙적인 진동'을 영어로 Oscillation 이라고 한다.(Vibration은 '불규칙적인 진동'을 의미한다)

 

 

3. 실제 파형

대부분의 소리들은 기본 파형처럼 깔끔한 곡선 형태가 아니다.

아주 복잡한 형태의 밀도의 변화가 주기적으로 반복되는 파형을 갖는다.

 

출처: https://gfycat.com/ko/astonishingrightkingsnake

 

이제부터 이런 복잡한 파형을 분석해보자!

(원리는 어렵지 않다)

 

# 파형의 배합

먼저 주파수가 서로 다른 기본 파형의 소리 여러개를 동시에 재생한다고 생각해보자.

파형의 배합(출처: 직접 그림)

 

여러 파형의 소리들이 동시에 재생되면

각 파형들의 진폭이 합해지고 상쇄되면서 새로운 형태의 파형이 만들어진다.

이 때, 같은 부호의 진폭이 만나면 합해지고 다른 부호의 진폭이 만나면 서로 차감된다.

기본 파형의 배합(출처: 손으로 직접 그림)

즉, 여러개의 기본 파형을 동시에 재생하면

각 파형들이 볼륨(진폭) 비율대로 합쳐진다.

 

 

1. 기음과 배음

- 기음(Fundamental Tone): 여러개의 파형들 중 가장 진폭이 큰 소리를 기음이라고 한다.

- 배음(Over Tone): 기음보다 진폭이 작은 파형들을 모두 배음이라고 한다.

기음, 배음(출처: 직접 그림)

 

2. 볼륨 (크기: Volume, Gain)

같은 부호의 진폭은 더해지고, 다른 부호의 진폭은 차감된다.

여러 파형들의 진폭이 합쳐지고 상쇄되며 만들어진 최종 진폭이 소리의 크기(볼륨)을 결정한다.

볼륨 (출처: 직접 그림)

 

3. 음정 (높낮이: Pitch)

소리의 높낮이는 진폭이 가장 큰 기음의 주파수로 결정된다.

복잡한 파형의 음정(출처: 직접 그림)

여러 파형이 합쳐졌을 때의 최종 파형은

결국 가장 진폭이 큰 기음의 파형과 같은 주기를 갖게 되기 때문이다.

 

참고로 모든 파형의 진폭 크기가 비슷하면 음정 파악이 어렵다.(ex. 북소리, 백색소음, 노이즈 등)

 

 

4. 노이즈 캔슬링(Noise Canceling)

파형은 동일하나, 진폭은 반대인 소리를 동시에 재생하면

서로 완벽히 상쇄되어 최종 진폭이 0이 된다.

결국 아무 소리도 들리지 않는다.

노이즈 캔슬링(출처: 직접 그림)

노이즈 캔슬링은 이런 원리를 이용해

외부의 소음과 반대되는 파형의 소리를 딜레이 없이 재생시켜 소음을 제거하는 기술이다.


지금까지 살펴본 소리 배합의 원리는

복잡한 파형의 소리도 결국 기본 파형 여러 개가 합쳐진 것에 불과하다는 것이다.

 

그렇다면 반대로

복잡한 파형의 소리를 기본 파형 여러 개로 분리할 수 도 있지 않을까?

이것이 바로 오디오 분석의 시작이며, FFT(고속 퓨리에 변환)의 기본 발상이다.

 

그럼 본격적으로 FFT의 원리를 살펴보자.

 

# FFT(Fast Fourier Transform: 고속 푸리에 변환)

 

푸리에 변환은 아이디어는 간단하다.

 

1) 모든 소리는 기본 파형(Sine파 등) 여러 개의 합이다.

2) 따라서 모든 소리는 여러 개의 기본 파형으로 분리할 수 있다.

 

즉, 오디오 분석에서의 푸리에 변환은 소리의 파형을 여러 개의 기본파형으로 변환하는 알고리즘이다.

출처:https://en.wikipedia.org/wiki/Fourier_transform

위의 그림의 움직임을 유심히 살펴보면

퓨리에 변환의 아이디어를 쉽게 이해할 수 있을 것이다.

 

분리된 기본 파형(Sine파)들은 "특정 주파수"에서의 "진폭 값의 변화"를 나타내므로

이를 계산하면 복잡한 파형을 구성하는 각 주파수별 진폭값을 구해낼 수 있다.

FFT의 원리(출처: 직접 그림)

위 그래프는

1)소리의 "시간별 진폭 변화"를 2)“주파수별 Sine파"로 분리하고, 3)"주파수별 진폭값" 들로 변환하는 과정을 보여준다.

 

이제 아래의 그림이 쉽게 이해될 것이다.

 

출처: https://dev.to/trekhleb/playing-with-discrete-fourier-transform-algorithm-in-javascript-53n5

 

이처럼 시간 영역의 진폭값들을 주파수 영역의 진폭값으로 변환하는 것을

이산 푸리에 변환(DFT: Discrete Fourier Transform)이라고 한다.

 

출처: https://en.wikipedia.org/wiki/Fast_Fourier_transform

 

단, DFT는 연산이 오래 걸리기 때문에 

더 빠른 연산이 가능한 고속 푸리에 변환(FFT: Fast Fourier Transform)을 사용한다.

 

이 글에서는 다행히(?) 계산하는 방법은 다루지 않는다.

 

*FDI

시간에 따른 진폭값 데이터를 Time Domain Data라고 하고,

주파수별 진폭값 데이터를 Frequency Domain Data라고 한다.

FFT는 결국 Time Domain Data를 Frequency Domain Data로 변환하는 것이다.

 

* FDI

- FFT는 TimeDomainData를 “대칭적”으로 연산하여 FrequencyDomainData로 변환한다.

- 따라서 변환된 FrequencyDomainData의 갯수는 TimeDomainData 갯수의 절반이 된다.

- 즉 N개의 데이터로 FFT로 돌리면 N/2개로 변환된다.

(아래 그림 참고)

출처: https://en.wikipedia.org/wiki/Fast_Fourier_transform

 

* FDI

- Javascript Web Audio API에서는 

- FFT 분석의 대상이 되는 TimeDomainData(배열)의 length를 fftSize 라고 하고, 

- 분석의 결과가 되는 FrequencyDomainData(배열)의 length를 frequencyBinCount 라고 한다.

- , fftSize = 2 * frequencyBinCount 된다. (FFT 연산의 특성상 결과 데이터는 입력 데이터의 절반으로 줄어들기 때문)


이번 글에서는

1) 소리의 파형이 어떻게 배합되는지를 살펴보고,

2) 복잡한 파형의 소리를 기본 파형으로 분리해 내어

3) 주파수별 진폭값으로 변환하는 FFT에 대해 다루었다.

 

다시 요약하자면

FFT 시간 영역의 데이터를 주파수 영역의 데이터로 빠르게 변환하는 알고리즘이다.

그리고 FFT를 이용하면 복잡한 파형의 소리를 주파수별 진폭값으로 분리해낼 수 있다.

 

그런데 이러한 분석을 위해서는

우리가 듣는 소리를 컴퓨터가 이해할 수 있는 디지털 형태의 바이너리 데이터로 추출/변환하여 처리해야 한다.

이를 샘플링(Sampling)과 ADC(Analog To Digital Conversion)라고 한다.

 

▼이에 대해서는 이전에 작성해둔 아래의 글을 참고하자.▼

오디오 데이터 추출 및 변환: ADC, 샘플링, 샘플레이트, 채널

 

다음 글부터는 본격적인 개발자의 영역이다.

▼Stream, Buffer, File, 그리고 Web Audio API를 다룰 예정이다.▼

스트리밍(Streaming)이란: 스트림(Stream), 버퍼(Buffer) 원리

 

 

 

댓글