이번에는 배경음악(mr)을 제거하는 방법을 설명해보겠다.

 

우선 앞의 음성 추출 단계를 거친다면 5초 내외의 wav파일이 많이 있을 것이다. 

ex)

이런식으로

이제 이 파일들의 배경음악을 제거해보자.

(참고로 우리가 직접 녹음하거나 한게 아니라 성우분들이 직접 전문 장비로 녹음을 하기 때문에 노이즈는 없다고 봐도 무방하다. 우린 배경음악만 제거하면 된다.)

 

배경음악을 제거하는 프로그램이나 툴이 뭐가있나 찾아보다가 spleeter라는 라이브러리를 찾게 되었다.

spleeter는 간단하게 말하자면 딥러닝 관련 기술로 보컬과 mr을 분리해주는 라이브러리다. 

https://github.com/deezer/spleeter

 

GitHub - deezer/spleeter: Deezer source separation library including pretrained models.

Deezer source separation library including pretrained models. - GitHub - deezer/spleeter: Deezer source separation library including pretrained models.

github.com

# 설치
pip install spleeter

 

# 실행 (터미널에서)
spleeter separate -p spleeter:2stems -o output audio
# output : 출력 값을 넣을 경로
# audio : 보컬과 mr을 분리 할 파일(wav)

 

코드에 2stems라는 것의 뜻은 vocal과 accompaniment separation(mr) 총 2개를 분리하겠다는 뜻이다.

숫자를 4, 5로 바꾸면 더 세세하게 분리가 가능하지만 나는 캐릭터의 목소리와 뒤의 배경음악만 제거하면 되므로 필요가 없다. (4 stems는 drum, bass 추가, 5 stems는 drum, bass, piano 추가)

 

그런데 이 라이브러리의 예제를 보면 터미널에서 명령어를 입력하는 방식으로 사용한다.

문제는 내가 spleeter를 이용해서 분리해야 할 wav파일이 너무 많아서 손으로 다 일일이 칠 수 없다는 것이다!

 

그래서 spleeter를 효과적으로 사용하기 위해서 class를 하나 만들었다.

import os
import wave
import datetime


# filelist -> wav file list (str)
# filenum -> wav file count (int)
# partnum -> ani part num list (int)
# path -> dataset path (str)
# total_time -> wav file total time
class wav_spleeter():
    def __init__(self, path):
        self.path = path
        self.spl_path = path+"\\spleeter_out\\"
        self.filelist = [wav for wav in os.listdir(path) if wav.endswith(".wav")]
        self.filenum = len(self.filelist)
        self.partnum = list(set(int(i.split('_')[0]) for i in self.filelist))
        self.filelist = sorted(self.filelist, key=lambda x: int(x.split('_')[0]))
        self.partnum.sort()
        self.total_time = 0

        os.chdir(self.path)

        for i in self.filelist:
            a = wave.open(i, 'rb')
            self.total_time += a.getnframes() / a.getframerate()

        print(self.partnum)
        print(self.filelist)
        print(f'파일이 {self.filenum}개 있습니다.')
        print(self.total_time)
        print(f'total time : {datetime.timedelta(seconds=self.total_time)}')



    """
    start = 0이면 path안의 모든 wav파일을 spleeter
    start = n이면 n으로 시작하는 wav파일을 spleeter
    start와 end값이 둘 다 입력되면 start~end까지의 wav파일을 spleeter
    """

    def spleeter(self, start, end=0):
        spl_command = 'spleeter separate -p spleeter:2stems -o spleeter_out '
        if start==0:
            for i in self.filelist:
                os.system(spl_command+i)
            print("Done")
        elif end==0 and (start in self.partnum):
            for i in self.filelist:
                if int(i[0])==start:
                    os.system(spl_command+i)
            print("Done")
        elif start!=0 and end!=0:
            if start not in self.partnum or end not in self.partnum:
                print("RangeError!1")
            if start>end:
                print("end must large then start!")
            for i in self.filelist:
                if i[0]>=start and i[0]<=end:
                    os.system(spl_command+i)
            print("Done")
        else:
            print("RangeError!2")


    """
    실행 하면 spleeter함수 실행 후 나온 파일들 중 vocal파일을 
    spleeter_out 디렉토리에 배치한다. 그 후 mr파일과 디렉토리는 삭제
    """

    def spldata_to_path(self):
        if not (os.path.exists(self.spl_path)):
            print("plz execute spleeter def!")
            return 0
        os.chdir(self.spl_path)
        spl_dir = [i for i in os.listdir(os.getcwd()) if os.path.isdir(i)]
        print(spl_dir)
        if len(spl_dir)==0:
            print("spleeter 먼저 실행해 주세요. ")
        for l in spl_dir:
            try:
                os.rename(self.spl_path + l + "\\vocals.wav", self.spl_path + l + "_spl.wav")
            except FileNotFoundError:
                print(l+"폴더에 vocals.wav 파일이 없습니다.")
            except FileExistsError:
                os.remove(self.spl_path + l + "_spl.wav")
                os.rename(self.spl_path + l + "\\vocals.wav", self.spl_path + l + "_spl.wav")

        for l in spl_dir:
            try:
                os.remove(self.spl_path + l + "\\accompaniment.wav")
                os.rmdir(self.spl_path + l)
            except FileNotFoundError:
                pass


	# 폴더 정보를 info.txt에 저장
    def folderinfo(self):
        info = os.popen('dir | sort').read()
        f = open("info.txt", 'w')
        print(info)
        f.write(f'wav file total time : {datetime.timedelta(seconds=self.total_time)}\n\n')
        f.write(f'file_num = {self.filenum}\n\n')
        f.write(info)
        f.close()


if __name__ == '__main__':
    path = 'your dataset path'
    wavfile = wav_spleeter(path)
    wavfile.folderinfo()
    wavfile.spleeter(0)
    wavfile.spldata_to_path()

이 class의 함수를 위에서 부터 하나씩 실행하면 dataset경로에 spleeter_out이라는 폴더가 생길 거고, 그 안에는 data들의 mr이 제거된 파일이 있을 것이다. (1_001_spl.wav)

이 코드를 사용하려면 data들이 '데이터 셋 만들기(1)'에서 제시한 대로 이름이 지어져 있어야 원활하게 돌아간다.

뭔가 쓸데없이 복잡해 보이지만 결론만 이야기하면

 

 

<<path에 dataset경로를 입력하고, 함수를 하나씩 실행시킨다. 끝>>

 

 

저기 보면 spleeter라는 함수가 있는데 저게 인자로 0을 주면 경로에 있는 모든 wav파일을 spleeter에 넣고 돌린다.

(wavfile.speeter(0) --> 전부 다 spleeter)

 

인자로 숫자를 주면 앞자리가 그 숫자로 시작하는 wav파일들을 다 spleeter에 넣고 돌린다.

(wavfile.speeter(6) --> 6_001.wav ~ 6_0xx.wav까지 다 spleeter)

 

인자로 숫자 2개를 주면 처음 숫자에서 끝 숫자의 범위가 앞자리인 wav파일들을 다 spleeter에 넣고 돌린다.

(wavfile.speeter(2, 5) --> 2_001.wav ~ 5_0xx.wav까지 다 spleeter)

 

folderinfo함수는 필수는 아니지만 내가 만든 데이터들이 총 길이가 얼마고, 용량은 얼만지 등을 바로 알 수 있으니, 데이터 작업을 하다가 궁금하면 한 번씩 실행시켜 보면 된다,

(사실 dataset에서 ctrl+a누르고 속성 누르면 총 길이를 볼 수 있긴 하다. 근데 이걸 왜 만들었냐면 파일 수가 많이지면 속성 누르면 렉이 엄청 걸린다. 전에 이 렉 때문에 컴퓨터 자기 혼자 꺼진 적이 있어서 화나서 만듦 ㅎㅎ)

 

 

 

사실 spleeter로 mr분리를 했지만 품질이 영 안좋은 파일들도 있을 것이다.

경험상 배경음악이 가사가 있는 음악일 경우, 2명 이상이 동시에 말을 할 때 mr분리가 잘 안 되는 것으로 보인다. 이는 어쩔 수 없으므로 못 쓰겠으면 그 파일은 버리도록 하자. 나는 너무 아까워서 진짜 못 쓰겠는 거 아니면 일단은 냅뒀다. 학습에 악영향을 미친다면 당연히 뺄 거지만.

 

그리고 혹시나 해서 말하는 거지만, 배경음악이 없는 wav파일을 spleeter에 넣고 돌린다고 파일이 깨지거나 이상하게 되지 않으니 안심해도 좋다. 실험해보니 배경음악 없는 wav파일은 그냥 거의 그대로 나오더라.

 

spleeter로 돌리고 나온 wav파일들은 공통점으로 (전부는 아니지만) 소리가 약간 울리는 느낌이 든다. 크게 신경 쓸 정도는 아닌 듯.

 

 

이렇게 해서 우리는 mr이 제거된 wav파일을 얻게 되었다!

이제 wav파일의 음성에 대한 text만 있으면 사전 데이터는 준비 끝이다!

 

다음은 wav파일에서 text를 뽑아내는 작업을 해보겠다.

 

(솔직히 spleeter 사용 전 후 데이터 파일를 올리고 싶은데, 저작권 관련해서 아는게 없어서 무서워서 못올리겠다.. 잘 알아보고 저작권이 허용하는 범위 내에서 한번 올려보겠다!)