본문 바로가기

Theory/DataScience

소리나 음원, 음악 데이터에서 주파수 특성 분석 - librosa

오늘은 소리나 음원, 음악 데이터에서 STFT나 Chroma 분석을 수행하는데 편한 librosa를 소개하고, 이를 이용해서 소리 데이터에서 특성을 추출하는 기초에 대한 이야기를 해보려고 합니다. 먼저 STFT에 대해서는 얼마전에 이야기를 했었는데요.

https://pinkwink.kr/1370

 

Python으로 수행하는 주파수 분석 - FFT, STFT

아주 예전에 Python으로 수행하는 FFT라는 주제의 글을 작성한 적이 있습니다. 이번에는 이 글에서 조금 더 나가서 STFT라는 개념도 이야기를 해 보려고 합니다. 시간영역에서의 신호를 분석할 때

pinkwink.kr

위 글에서 FFT와 STFT에 대한 이야기를 했었습니다. 저 글에서는 scipy와 numpy를 사용했는데요. 이번에는 소리 데이터 분석에 유용한 도구인 librosa를 가지고 이야기를 해보도록 하겠습니다.

import librosa.display, librosa
import IPython.display
import warnings
import numpy as np
warnings.simplefilter("ignore")

def sin_wave(amp, freq, time):
    return amp * np.sin(2*np.pi*freq*time)

먼저 필요 모듈들을 import하고 정현파를 만드는 함수 sin_wave를 간단히 만들어 두겠습니다.

일단 도, 미, 솔을 만들어 보겠습니다. 각 주파수는 위 표에 나와있습니다.

sr = 22*1000.
ts = 1/sr

time = np.arange(0, 1, ts)
DO = sin_wave(1, 261.6256, time)
MI = sin_wave(1, 329.6276, time)
SOL = sin_wave(1, 391.9954, time)

그래서 1초로 하고, 샘플링 주파수는 22kHz로 해서 간단하게 도, 미, 솔 파형을 만들어 봤습니다.

import matplotlib.pyplot as plt

plt.figure(figsize=(12,5))
plt.plot(time, DO, label="DO", color='red')
plt.plot(time, MI, label="MI", color='blue')
plt.plot(time, SOL, label="SOL", color='green')
plt.xlim((0, 0.01))
plt.legend(); plt.grid(); plt.show()

위 코드로 0.01초 부분까지만 확인해보면~

이렇게 잘 만들어 졌음을 알 수 있습니다.

IPython 모듈의 diplay.Audio를 Jupyter에서 이용하면 바로 음원을 들어 볼 수 있습니다. 오늘 글에 포함된 소리들은 모두 이 글 하단의 영상에서 들으실 수 있습니다.

sample_concat = np.concatenate((DO, MI, SOL))

그래놓고 1초짜리 도 미 솔을 이어붙여서 3초짜리로 만들었습니다.

import matplotlib.pyplot as plt

def draw_wave(sample_sounds):
    plt.figure(figsize=(12,6))
    librosa.display.waveshow(sample_sounds, alpha=0.2)
    plt.xlabel("Time (s)"); plt.ylabel("Amplitude")
    plt.title("Waveform"); plt.show()

이제 미리 시간축에서 음원의 파형을 그려주는 함수를 하나 만들어 두겠습니다.

도, 미, 솔을 이어 붙은 음원의 파형입니다.

def get_stft(sample_sounds):
    return librosa.stft(sample_sounds)

이제 librosa가 제공하는 stft 결과값을 얻어오는 함수도 간단히 하나 만들구요~ 이 함수의 각종 옵션은

http://librosa.org/doc/main/generated/librosa.stft.html

이렇습니다. 

출처 : 현시점 나와 함께 하고 있는 우리팀 팀원 현진님~

n_fft는 윈도우의 길이, 그리고 hop length는 얼마나 겹칠 것인지를 설정합니다. 디폴트가 잘 지정되어 있어서 그냥 데이터만 주어도 됩니다.

import numpy as np

def draw_stft(sample_sounds, ylim=(None, None)):
    plt.figure(figsize=(12,6))
    librosa.display.specshow(np.abs(get_stft(sample_sounds)), 
                             y_axis='hz', x_axis='s')
    plt.ylim(ylim); plt.grid(); plt.show()

그리고 stft 결과를 받아와서 그려주는 것도 함수로 하나 만들어 두겠습니다.

먼저 도, 미, 솔을 이어 붙인 음원에 대한 STFT의 결과를 그렸습니다. 잘되네요^^

이제 CHROMA 분석을 해보겠습니다. 위 주파수 분석 결과에 12음계를 y라벨로 사용한다고 생각하시면 됩니다.

def get_chroma(sample_sounds, sr):
    return librosa.feature.chroma_stft(S=np.abs(get_stft(sample_sounds)), 
                                       sr=sr)

STFT 분석 결과를 가지고 chroma_stft 함수를 이용하면 chroma 결과를 얻을 수 있습니다. 이 함수를 만들고, 

def draw_chroma(sample_sounds, sr):
    plt.figure(figsize=(12,6))
    librosa.display.specshow(get_chroma(sample_sounds, sr), 
                             y_axis='chroma', x_axis='time')
    plt.grid(); plt.show()

그것을 이용해서 chroma 결과를 그려주는 것도 함수로 만들어 두겠습니다.

아까 만들어둔 도, 미, 솔을 그린 것입니다.

도, 미, 솔 이어 붙인것도 도, 미, 솔 잘 찾아주네요 ㅋㅋ

sum_samples = sum([DO, MI, SOL])

이제 도, 미, 솔을 그냥 더해버리겠습니다.~

STFT 결과도 잘 나오고

Chroma 결과도 잘 나오네요~

화음을 한 번 들어보는 겁니다. 이건 영상에서 확인해주세요~

flute.wav
7.01MB
guitar.wav
7.01MB
piano.wav
7.01MB

위 세 개의 음원은 제가 매우 관심있게 함께 하는 회사의 음악 담당이신 분이 만들어 주신 음원입니다.^^. 이자리를 빌어 감사를 표합니다.~

piano, sr = librosa.core.load('./melody_sample/piano.wav')

일단 피아노 음원을 읽고~

이렇게 생겼군요~

STFT 결과는 저렇습니다~

CHROMA로 분석해서 보면 저렇습니다.

flute, sr = librosa.core.load('./melody_sample/flute.wav')
guitar, sr = librosa.core.load('./melody_sample/guitar.wav')

sum_inst = sum([piano*2, guitar, flute*1.2])

다른 두개도 다 읽어서~ 더해버렸습니다.^^

재미난 결과를 볼 수 있습니다.

어떤가요. 음원이나 소리를 분석할 때 STFT는 꽤 좋고, librosa를 이용하시면 편하게 사용할 수 있습니다. 특히 음원일때는 chroma 분석도 괜찮구요.

반응형