본문 바로가기
Python

[Python] 구글 speech to text api를 사용해 긴 텍스트를 오디오 파일로 변환하고 웹 브라우저에서 재생하기

by teamnova 2023. 8. 25.

안녕하세요. 이번 시간에는 구글 cloud platform 에서 제공하는 text to speech api를 통해 텍스트를 오디오로 변환하는 방법을 알아보려 합니다. 

우선 api를 사용하기 위해 구글 cloud platform에 새 계정을 만든 뒤 json 키를 받아서 인증 환경 변수를 설정해야 하는데요. 아래의 공식 홈페이지 링크에서 제시하는 순서대로 개발 환경에 대한 세팅을 해주세요.

https://cloud.google.com/text-to-speech/docs/before-you-begin?hl=ko 

 

시작하기 전에  |  Cloud Text-to-Speech API  |  Google Cloud

시작하기 전에 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Text-to-Speech는 Google의 인공지능(AI) 기술을 기반으로 한 API입니다. API 호출에서 스크립트 데이

cloud.google.com

 

 

세팅이 완료되면 파이썬이 설치된 개발 환경에 우선 text to speech를 설치해주세요

pip install --upgrade google-cloud-texttospeech

 

이후 아래와 같은 코드를 실행시키면 텍스트가 오디오 파일로 전환됩니다. 공식 홈페이지의 예제 코드의 경우 문장의 길이가 길어지면 에러가 발생하는데요. 아래는 문장 단위로 끊어서 텍스트를 처리하는 로직을 통해 긴 텍스트도 오디오 파일로 변환 가능하게 수정한 코드입니다.

def synthesize_text(text):
    # "texttospeech import"
    from google.cloud import texttospeech        

    client = texttospeech.TextToSpeechClient()

    # 최대 길이를 200으로 지정 (지나치게 길어지면 에러 발생)
    max_length = 200    
    # . 단위로 문장 분리
    words = text.split('. ')
    sentences = []
    current_sentence = ''
    for word in words:
        if len(current_sentence + word) <= max_length:
            current_sentence += word + ' '
        else:
            sentences.append(current_sentence.strip() + '.')
            current_sentence = word + ' '
    if current_sentence:
        sentences.append(current_sentence.strip() + '.')

    

    # 빈 배열 생성
    audio_data = []

    # 문장 개수 단위로 텍스트 변환
    for sentence in sentences:
        input_text = texttospeech.SynthesisInput(text=sentence)

        # 오디오 설정 (예제에서는 한국어, 남성C)
        voice = texttospeech.VoiceSelectionParams(
            language_code="ko-KR",
            name="ko-KR-Neural2-C",
            ssml_gender=texttospeech.SsmlVoiceGender.MALE,
        )

        audio_config = texttospeech.AudioConfig(
            audio_encoding=texttospeech.AudioEncoding.MP3
        )

        response = client.synthesize_speech(
            request={"input": input_text, "voice": voice, "audio_config": audio_config}
        )

        audio_data.append(response.audio_content)
  
    audio_data = b"".join(audio_data)
    
    # audio 폴더 안에 output.mp3라는 이름으로 파일 생성
    with open("audio/output.mp3", "wb") as out:        
        out.write(audio_data)
        print('오디오 파일 생성')


synthesize_text("안녕하세요. 팀노바 티스토리 블로그에 오신 것을 환영합니다.")

 

해당 경로에서 파일을 실행하면 다음과 같이 오디오 파일이 생성됩니다.

 

파일 실행 (파일 이름 : tts.py)

python tts.py

 

오디오 파일 생성

 

하지만 이렇게 단순히 파일을 생성하는 것 뿐만이 아니라 웹페이지에서 생성한 오디오 파일을 재생하는 기능이 필요할 수도 있는데요. 이럴 경우에는 웹서버를 구축한 뒤 생성된 오디오 파일의 경로 url를 활용하여 오디오를 재생할 수 있습니다.

웹서버 구축을 위한 다양한 파이썬 프레임워크가 있지만 여기서는 fastapi를 사용하여 만들어보겠습니다.

 

우선 다음과 같은 명령어를 통해 fastapi를 설치합니다.

pip install fastapi

   

그후 웹서버를 실행시키려는 경로에 main.py 파일과 audio 폴더를 생성해줍니다. audio 폴더는 생성된 오디오 파일을 저장하는 용도이며 이 때 main.py파일과 audio 폴더는 같은 경로에 위치해야 합니다.

 

main.py

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

# fastapi 앱 생성
app = FastAPI()

# 오디오 파일을 넣을 디렉토리 지정
app.mount("/audio", StaticFiles(directory="audio"), name="audio")


@app.get("/")
async def root():
    return {"message": "Hello World!"}

위 코드에서는 우선 app = FastAPI()를 통해 웹 어플리케이션을 생성한 뒤 웹에서 src로 오디오 파일을 가져오기 위해 /audio 경로의 audio 디렉토리를 지정했습니다.

이후 웹 브라우저에서 / 경로로 요청을 보냈을 때 {"message" : "Hello world!"} 가 표시되도록 처리하였습니다.

 

이후 다음 명령어를 통해 웹서버를 실행할 수 있는 uvicorn을 설치하고 웹서버를 실행합니다.

pip install fastapi 'uvicorn[standard]'
uvicorn main:app --reload

main : 모듈명을 의미합니다. # (main.py)
app : FastAPI로부터 생성된 인스턴스를 의미합니다. # app=FastAPI()
--reload : 코드 수정시 새로고침됨을 의미합니다. 

 

이후 현재 작업하는 서버의 ip로 url로 요청을 보냈을 때 정상적으로 통신이 이루어짐을 알 수 있습니다.

fastapi에 대한 보다 더 자세한 정보는 아래 링크를 참고해주세요.

https://fastapi.tiangolo.com/ko/

 

FastAPI

FastAPI FastAPI 프레임워크, 고성능, 간편한 학습, 빠른 코드 작성, 준비된 프로덕션 문서: https://fastapi.tiangolo.com 소스 코드: https://github.com/tiangolo/fastapi FastAPI는 현대적이고, 빠르며(고성능), 파이썬

fastapi.tiangolo.com

 

 

이제 웹 서버가 구축되었으니 웹 브라우저에서 오디오로 변환할 텍스트와 파일명을 보내면 서버에서 이를 받아 오디오를 생성한 뒤 성공여부를 알려주는 기능을 구현하려 합니다.

 

우선 웹서버를 아래와 같은 코드로 수정합니다. 웹브라우저로부터 받은 text와 filename을 위에서 설명한 synthesize_text 함수에서 받아 오디오 파일을 생성하는 형태로 변형하였습니다.

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel

app = FastAPI()

# cors에러 방지
origins = [
    "http://localhost"     
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 오디오 파일을 넣을 디렉토리 지정
app.mount("/audio", StaticFiles(directory="audio"), name="audio")

# 웹 브라우저로부터 데이터를 받을 class 선언
class request(BaseModel):
    text: str
    filename: str
   

@app.post("/")
async def root(request_data: request):
	
    # 텍스트와 파일 이름 변수에 할당
    text = request_data.text
    filename = request_data.filename

    try:
    	# 텍스트와 파일 이름 활용하여 오디오 파일 생성
        synthesize_text(text, filename)

        return {"success": "yes"}


    except Exception as e:
        return {"success" : "no", "error": e}
    

def synthesize_text(text, filename):
    # "texttospeech import"
    from google.cloud import texttospeech        

    client = texttospeech.TextToSpeechClient()

    # 최대 길이를 200으로 지정 (지나치게 길어지면 에러 발생)
    max_length = 200    
    # . 단위로 문장 분리
    words = text.split('. ')
    sentences = []
    current_sentence = ''
    for word in words:
        if len(current_sentence + word) <= max_length:
            current_sentence += word + ' '
        else:
            sentences.append(current_sentence.strip() + '.')
            current_sentence = word + ' '
    if current_sentence:
        sentences.append(current_sentence.strip() + '.')

    

    # 빈 배열 생성
    audio_data = []

    # 문장 개수 단위로 텍스트 변환
    for sentence in sentences:
        input_text = texttospeech.SynthesisInput(text=sentence)

        # 오디오 설정 (예제에서는 한국어, 남성C)
        voice = texttospeech.VoiceSelectionParams(
            language_code="ko-KR",
            name="ko-KR-Neural2-C",
            ssml_gender=texttospeech.SsmlVoiceGender.MALE,
        )

        audio_config = texttospeech.AudioConfig(
            audio_encoding=texttospeech.AudioEncoding.MP3
        )

        response = client.synthesize_speech(
            request={"input": input_text, "voice": voice, "audio_config": audio_config}
        )

        audio_data.append(response.audio_content)
  
    audio_data = b"".join(audio_data)
    
    # audio 폴더 안에 output.mp3라는 이름으로 파일 생성
    with open(f"audio/{filename}.mp3", "wb") as out:        
        out.write(audio_data)

우선 cors 에러 방지를 위한 코드를 추가한 뒤 (예제에서는 localhost 환경을 사용했기 때문에 http://localhost를 넣었습니다. 만일 다른 서버, 혹은 localhost의 다른 포트를 사용하고 있다면 그에 맞게 문자열을 넣어 주시면 됩니다.)

post 형태의 전송에서는 웹 브라우저로부터 데이터를 받는 별도의 class가 필요하므로 이를 선언합니다.

위와 같은 코드를 통해 오디오 파일 생성이 성공하면 success의 값으로 yes, 실패할 경우 no를 리턴합니다.

 

 

마지막으로 오디오로 변환할 텍스트와 파일 이름을 보내고 결과를 받아 오디오를 재생할 수 있는 html 페이지의 코드입니다.

<!DOCTYPE html>
<html lang="ko">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">              
    </head>    
    
    <body>        
    	<!-- 재생할 오디오 태그 -->
        <audio src='' controls id="myAudio"></audio>       
           
        <script>

            sendData();
			
            // 웹서버로 텍스트와 파일 이름 전송
            async function sendData() {
            
            	// 웹서버에 보낼 객체
                let obj = {
                    text : "안녕하세요. 팀노바 티스토리 블로그에 오신 것을 환영합니다.",
                    filename : "audio_test"
                };
				
                // 웹서버 ip로 post 전송
                const request = fetch("{웹서버ip}", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify(obj),
                })
                // 요청 성공시 json 형태로 저장
                .then((response) => response.json())
                .catch((error) => {
                    // 실패 시 에러 로그 출력
                    console.log(error)
                });

                const check = await request;

				// 만약 성공할 경우 오디오 src 지정
                if (check.success == "yes") {
                    
                    // audio 태그 변수 할당
                    const audio = document.getElementById('myAudio');
                    // 오디오 파일이 생성된 경로를 src로 지정
                    audio.setAttribute('src', '{웹서버ip}/audio/audio_test.mp3')
                }
            }
        </script>
    </body>
</html>

 

코드 실행 영상입니다.