본문 바로가기

IDEA

보이스 컴포저(Voice Composer)


갑자기 문득 생각이 떠올라서 한번 만들려고 시도를 해봤다.

이름을 어떻게 지어야 할지는 모르겠지만.. 대충 '보이스 컴포저(Voice Composer)'라고 지었다. 해봐야 겠다는 feel 받아서 꼬박 7시간동안 작업..


TTS(Text To Speech)는 우리가 텍스트를 입력하면 사람 음성으로 들려준다. 그런데, TTS엔진을 실행해보면 음성들이 아나운서의 말처럼 감정이 없게 들리거나 감정이 참 일관적이다. 슬픈말을 해도 기쁘게 읽어주고, 화가나도 기쁘게 읽어준다. 물론 느낌표나 물음표, 쉼표를 문장의 중간에 달아주면 끝음을 올려준다든지, 말 중간에 쉬어 말한다든지 하긴하지만, 감정전달에는 별 영향을 주진 못하는 것 같다.

우리는 TV나 영화에서 참 맛갈나게 말하는 배우들 목소리를 인상깊게 듣는다. 그런 목소리를 사실 프로그램적으로 알아서 그 뉘앙스를 음성에 실어 전달하기란 불가능에 가까울것 같다. 무슨 방법으로 그 복잡 미묘한 감정을 목소리에 담아 표현하겠는가 말이다..

그래서, 단순하게 생각해보니, 그냥 텍스트를 입력해서 배우들 목소리를 데이터베이스에서 꺼내다 조합하면 안될려나...

영화 '트랜스포머'에서 본것처럼 말하는 것 대신에 라디오 주파수를 돌려가며 음성을 대신하는 모습이 나오는데, 그런것처럼..


그렇게 한다고 해도 사실 뉘앙스를 잘 전달하지는 못 할 것 같다. 데이터베이스가 완전하지는 않을것이고, 동일한 말이라도 컨텍스트에 따라 오묘한 느낌을 가진 데이터베이스를 만들고 거기서 데이터를 걸러내기도 힘들테니 말이다..


1. 음원 샘플링(유튜브에서 영상 다운로드)

일단 샘플 음성을 하나 Youtube사이트에서 하나 땄다. 사용하고 있는 firefox 브라우저의 확장 플러그인중에 유튜브 영상을 다운로드 할 수 있는 놈을 하나 설치하고, 그걸로 다운로드 받았다.


두가지가 있는데,

(1) Easy Youtube Video Downloader 6.7은 다양한 포맷으로 변화하여 다운로드를 가능하게 해준다.

(2) Download Youtube Videos as MP4 and FLV 1.4.15는 다운로드 포맷이 2가지 뿐이다.

두가지 다 괜찮은데, mp3로 바로 다운로드 받을 수 있게 (1)번을 쓰는게 더 좋은 것 같다.




사용한 음원파일은용량관계상다운로드받은유튜브동영상만링크를걸어둔다.




2. 레이블 제작

받은 음성에서 레이블 편집을 해야한다. 음원데이터에서 자동으로 음성인식을 통해 어떤 말인지 딱딱 잡아낼 수 있으면 좋겠지만, 어디 이게 쉬운일인가.

그리고 현재 사용중인 ubuntu 에서 음원 파일을 편집할 수 있는 프로그램인 audacity 2.0.1 버젼을 설치하였다.

사용한 음원은 한국영화명대사라는 제목의 유튜브동영상을 음원만 추출하여 audacity에서 오픈한 것이고, 하단의 레이블은 직접 음원을 들어가면서 음원의 특정 시간구간마다 레이블을 달아놓은 것이다.




이렇게 레이블을 달아놓고 파일-레이블 내보내기를 선택하면 아래와 같은 포맷으로 text파일로 저장된다. 구간의 시작타임과 끝타임, 레이블이 탭으로 구분되어 저장된다.


작업한 레이블 파일 :

labels.txt


* 작업중에 조금 틀어졌는지 싱크가 조금 밀려 있다. 코드에서 시간 옵셋을 빼주거나 음성파일 앞부분을 약간 지워주면서 땡겨주면 된다.


(명대사엔 왜그리 욕설이 빠지지 않는지...ㅋ)


3. python으로 개발하기 위한 관련 패키지 설치

python scikits.audiolab 패키지를 이용하면 쉽게 웨이브파일을 읽고 쓰고 자르고 붙일수가 있다. (다른 방법에 있는지 모르겠지만, 구글링을 해봤을때 이 방법이 가장 먼저 눈에 띄어서 선택하게 되었다.) 이 방법외에 python에서 기본적으로 제공하는 wave를 import해서 프로그램을 짜도 된다. (참고 : http://docs.python.org/2/library/wave.html )


scikits.audiolab 패키지를 다운로드 받고 압축을 해제한다.


> cd /usr/local/lib/python2.7/site-packages      ;패키지를 여기에 저장했다고 한다면..

>tar zxvf scikits.audiolab-0.11.0.tar.gz      ;압축해제

> cd scikits.audiolab-0.11.0       ;디렉토리 위치 이동

> python setup.py install           ;설치


참고 :

(1) 파일 다운로드 : http://pypi.python.org/pypi/scikits.audiolab/

(2) 설치 : http://cournape.github.com/audiolab/

> sudo apt-get install python-numpy      ;numpy package설치

> sudo apt-get install python libsndfile-dev     ;libsndfile 설치

> sudo apt-get install libasound2-dev      ;libasound2 설치


4. python 프로그램 작성

프로그램이 처리해야 될 기능은 사용자의 문자열 입력을 받아, 레벨 텍스트파일에서 동일한 내용을 찾아 그 음원사이의 구간을 알아낸 다음에 그 구간의 음원들을 떼어다가 저장해두고, 마지막에 모두 붙여서 wav파일로 저장하는 기능을 하는 프로그램이 될 것이다.


아래는 파이썬 소스코드이다. 테스트는 '하하하하 예림이 혓바닥이' 라는 텍스트를 입력으로 하였고, 오리지널 파일에서 해당 부분을 분리해내고, 다시 조합하여 연결시키는 것으로 프로그램을 구성하였다.


소스코드 다운로드 :

wavfile_control.py


<소스코드>

#-*- coding:utf-8 -*-
# programmed by Byunghun Hwang <elecun2@gmail.com>

import numpy as np
import scikits.audiolab as audiolab

class labelLoader:
    def __init__(self, filepath):
        self._label_data = []
        self.load(filepath)
       
    def load(self, filepath):
        #read label file
        _label_file = file(filepath,'r')
        _label_lines = _label_file.readlines()
        _label_file.close()
       
        for line in _label_lines:
            self._label_data.append(line.split())
           
        return self._label_data

  
class waveLoader:
    def __init__(self, labels, input_filepath, output_filepath):
        self.input_path = input_filepath
        self.output_path = output_filepath
        self.labels = labels._label_data
        self._freq_sample = 44100
        self._channels = 2
        self._encoding = 'pcm16'
           
       
    def __del__(self):
        pass
       
    def compose(self,text):
        text_list = text.split()
       
        for one in text_list:
            _wav_input_file = audiolab.Sndfile(self.input_path,'r')
            self._freq_sample = _wav_input_file.samplerate
            self._channels = _wav_input_file.channels
            self._encoding = _wav_input_file.encoding
           
            _iencoding = 16
            if self._encoding == "pcm24":
                _iencoding = 24
            elif self._encoding == "pcm16":
                _iencoding = 16
            elif self._encoding == "pcm8":
                _iencoding = 8
               
            #split
            for base in self.labels:
                if base[2].find(one) != -1:
                    print base[0], base[1], base[2]
                   
                    tmp = _wav_input_file.read_frames(self._freq_sample*float(base[0]))
                    data = _wav_input_file.read_frames(self._freq_sample*(float(base[1])-float(base[0])))
                    _outfile = audiolab.Sndfile('/home/elecun/voice_composer/'+one+'.wav', 'w', audiolab.Format('wav'), self._channels, self._freq_sample)
                    _outfile.write_frames(data)
                    _outfile.close()
                   
                    break
               
            _wav_input_file.close()
           
        #join
        wlist = []
        for one in text_list:
            w, fs, enc = audiolab.wavread('/home/elecun/voice_composer/'+one+'.wav')
            print one
            wlist.append(w)
        result = np.vstack(wlist)
        audiolab.wavwrite(result, self.output_path, self._freq_sample, self._encoding)
           
           
if __name__=='__main__':
    print "---Voice Composer---"
   
    _labels = labelLoader('/home/elecun/voice_composer/labels.txt')
    _wave = waveLoader(_labels, '/home/elecun/voice_composer/test.wav', '/home/elecun/voice_composer/out.wav')
    _wave.compose('하하하하 예림이 혓바닥이')
    print "Done."
 


5. 결과

아래가 그 결과 wav 파일의 음성이다. 조합은 잘 된다.

더 발전을 시킬 수 있다면, 빠르게 편집할 수 있는 전용 편집툴을 만들거나, 레이블 비교검색 부분에서 문자열의 특징을 추출하는 알고리즘을 사용하여 distance를 구해서 많은 데이터베이스에서 가장 좋은 결과를 얻을 수 있는 방법을 고려해 볼 수 있을 것 같다. 그리고, 남성과 여성, 연예인 이름 등등의 속성을 가지도록하여 다양한 조건으로 적절한 음성을 조합할 수 있는 기능을 넣어봐도 재미있을듯 하다. 이것 또한 데이터 구축의 문제이고, 기존의 많은 컨텐츠에서 비교 검색하여 결과를 만들어내는데는 빅데이처 처리기술도 필요할 것이다. 이런 의미에서 이런 데이터속에서의 검색과 feature extraction을 통한 적절한 음원 선택 및 조합도 map-reduce로 구현이 될 수 있는 부분일 것 같다.


이런 데이터가 잘 구축되어 좀 웃기기는 하겠지만 TTS로도 활용이 가능할라나? 다양한 조합을 만들어내는데 아무래도 한계는 있을 듯.


결과 음성파일 :