본문 바로가기

Sound

음성데이터 음역대별 데이터 추출 기능

※ 확실한 방법이라고 확신할 수 없으니 참고용으로만 봐주세요. 원하는 기능을 구현하기 위해 ChatGPT를 활용하여 코드를 구현하였습니다.

 

 

1. FFT를 적용하여, 주파수에 따라 음역대를 구분함. 

import numpy as np
import scipy.io.wavfile as wavfile
from scipy.signal import butter, lfilter

# 음성데이터 불러오기
sampling_freq, signal = wavfile.read('sample_audio.wav')

# FFT를 이용해 스펙트럼 그래프 생성
signal_length = len(signal)
fft_size = 2 ** int(np.ceil(np.log2(signal_length)))
signal = np.pad(signal, (0, fft_size - signal_length), 'constant')
fft_signal = np.fft.fft(signal)
magnitude = np.abs(fft_signal)[:int(fft_size/2)]
frequency = np.linspace(0, sampling_freq/2, int(fft_size/2))

# 주파수에 따라 음역대 구분
mid_freq = 440
mid_index = np.argmin(np.abs(frequency - mid_freq))
low_index = np.argmin(np.abs(frequency - mid_freq/2))
high_index = np.argmin(np.abs(frequency - mid_freq*2))

low_magnitude = np.max(magnitude[low_index:mid_index])
mid_magnitude = np.max(magnitude[mid_index-10:mid_index+10])
high_magnitude = np.max(magnitude[mid_index:high_index])

print("Low range magnitude:", low_magnitude)
print("Mid range magnitude:", mid_magnitude)
print("High range magnitude:", high_magnitude)

# bandpass filtering을 위한 필터 생성
nyquist_freq = 0.5 * sampling_freq
low_cutoff = mid_freq/2
high_cutoff = mid_freq
filter_order = 4
b, a = butter(filter_order, [low_cutoff/nyquist_freq, high_cutoff/nyquist_freq], btype='band')

# 필터링 수행
filtered_signal = lfilter(b, a, signal)

# 결과 저장
wavfile.write('filtered_audio.wav', sampling_freq, filtered_signal)

위 코드의 경우 원본 음성데이터와 길이가 맞지 않는 문제점이 있었음. 그리고 wav로 저장시 raw데이터로 저장이 되서 Audacity로 바로 불러올 수 없고 원시데이터(raw data)로 불러와서 인코딩 및 샘플링 주파수도 맞춰줘야 했음.

위 방법으로는 원하는 음역대만 저장하는 기능 구현이 어려울 것으로 판단함.


2. Band -pass filter를 적용한 방법

import librosa
import matplotlib.pyplot as plt
import moviepy.editor as mp
import numpy as np
import scipy.io.wavfile as wavfile
from scipy.signal import butter, lfilter
from scipy import signal
from scipy.io.wavfile import write
import scipy
import wave



class SoundFile:
   def  __init__(self, signal, sr, file_name='test'):
       # https://docs.python.org/3.6/library/wave.html#wave.open
       self.file = wave.open('{}.wav'.format(file_name), 'wb')
       #self.signal = signal
       self.signal = np.ascontiguousarray(signal)

       self.sr = sr

   def write(self):
       # setparams(nchannels, sampwidth, framerate, nframes, comptype, compname)
       self.file.setparams( ( 1, 4, self.sr*2, self.sr*4, 'NONE', 'noncompressed' ) )
       # https://docs.python.org/3.6/library/wave.html#wave.Wave_write.writeframes
       self.file.writeframes( self.signal )
       self.file.close()
       
if __name__ == "__main__":
    audio_data_, samplerate = librosa.load(file_path)
    # signal settings
    # bandpass filtering을 위한 필터 생성
    nyquist_freq = 0.5 * samplerate

    low =low # 최저 주파수
    high = high  # 최고 주파수
    low_ = low / nyquist_freq
    high_ = high / nyquist_freq
    order = 2

    b, a = signal.butter(order, [low_, high_], btype='band')
    filtered_signal = signal.filtfilt(b, a, audio_data_)
    # 필터링 수행
    bw = 50
    bw = bw/nyquist_freq

    #create sound file
    f = SoundFile(filtered_signal, samplerate, file_name='gaussian_test_{}_{}Hz'.format(low,high))
    f.write()

 

Chat-GPT에 원하는 음역대만 추출할 수 있는 기능에 사용할 수 있는 알고리즘에 대해 알려달라고 해서 Band-pass filter를 찾게 되었음. Band-pass filter에 대해 개념이나 사용은 해봤지만, 잘 사용하지 않아서 전혀 떠올리질 못해서 삽질을 했음.

그리고 위 방법으로 적용을 해서 100~500Hz과 같이 원하는 주파수 영역만을 추출함.

 

위 스펙토 그램이 원본 오디오, 아래가 원하는 영역(3000~5000Hz)만 추출한 데이터임.

오디오를 들어봤을 때, 노이즈 때문에 원하는 구간의 특징을 알아내기 힘들었음. 

 

위 데이터에서 노이즈를 제거할 수 있는, Gaussian Fitler와 Wavelet Filter 2가지를 적용해보았고

Wavelet Fitler의 결과는 아래와 같음. 원본과 비교했을 때, 데이터가 없는 구간과 음역대별 특징을 알 수 있지만, 하지만 이 음성데이터도 노이즈가 있어 직접 들어서 특징을 추출하긴 힘들듯함. 다른 알고리즘을 적용하거나 방안이 필요할듯.. 

'Sound' 카테고리의 다른 글

MFCC(Mel Frequency Cepstral Coefficient)  (0) 2023.05.11
음성데이터 샘플링 (Audio Data Sampling)  (2) 2023.05.10
각 주파수별 소리특성  (0) 2023.05.02
음성 주파수 분해  (0) 2023.05.02
Audacity Window 빌드 (python, cmake사용)  (0) 2023.04.28