안 쓰던 블로그

unity 스크립트에서 mp3 파일 불러오기, 재생하기(Resources.Load<AudioClip>) 본문

유니티/개발

unity 스크립트에서 mp3 파일 불러오기, 재생하기(Resources.Load<AudioClip>)

proqk 2020. 2. 28. 17:14
반응형

빠른 결론->[3차 시도]부터 보면 됨

 

 

어린이 그림 맞추기 게임이니까 문제 텍스트도 읽어주면 좋을 것 같았다

이왕 연습하는 겸 google tts api를 써보려고 했음

 

[1차 시도]

    IEnumerator tts(string text)
    {
        string url = "http://translate.google.com/translate_tts?ie=UTF-8&total=1&idx=0&textlen=32&client=tw-ob&q=" + text + "&tl=ko-KR";
        WWW www = new WWW(url);
        yield return www;
        Debug.Log("url:" + url);
        goAudioSource.clip = www.GetAudioClip(false, false, AudioType.MPEG);
        goAudioSource.Play();
    }

처음에는 이런 방식으로 하려고 했다(검색해서 나오는 글들이 다 이렇게 하길래)

translate.google.com에다가 인자값을 붙인 url을 만들어서

WWW로 리턴해서 받아오고, 결과를 AudioClip에다가 붙이고 Play()

 

근데 이렇게 하면 이제는 안 되는 것 같다

google tts api가 뭔가 바뀌면서 이제는 저 주소를 쓰지 않음

그래서 그런지 주소를 찾지 못한다고 에러를 띄운다

 

 

[2차 시도]

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Net;
using System.IO;
using System.Text;

/*
문제를 읽어준다
1. 문제 텍스트를 구글tts한테 http로 보내서 음성 파일로 만든다
2. 음성 파일을 AudioSource로 불러와서 출력한다
*/
public class TextToSpeech_error : MonoBehaviour
{
    public AudioSource textAudio;
    public string apiURL = "https://texttospeech.googleapis.com/v1/text:synthesize?&key={mykey}";
    public void read(string text)
    {
        try
        {
            string webAddr = apiURL;

            var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
            httpWebRequest.ContentType = "application/json; charset=utf-8";
            httpWebRequest.Method = "POST";

            using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
            {
                string json = "{ \"audioConfig\": {\"audioEncoding\": \"LINEAR16\",\"pitch\": 0,\"speakingRate\": 1},\"input\": {\"text\": \"<REPLACE_TEXT>\"},\"voice\": {\"languageCode\": \"ko-KR\",\"name\": \"ko-KR-Standard-A\"}}\" \"https://texttospeech.googleapis.com/v1/text:synthesize?&key={mykey} \">audio_text.txt";

                json = json.Replace("<REPLACE_TEXT>", text);
                streamWriter.Write(json);
                streamWriter.Flush();
            }

            var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
            using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
            {
                var responseText = streamReader.ReadToEnd();
                Debug.Log("Response:" + responseText);
                byte[] byte64 = Convert.FromBase64String(responseText);
                string base64Decoded = Encoding.UTF8.GetString(byte64);

                byte[] buf = Encoding.UTF8.GetBytes(base64Decoded);

                float[] f = ConvertByteToFloat(buf);

                AudioClip audioClip = AudioClip.Create("testSound", f.Length, 1, 44100, false);
                audioClip.SetData(f, 0);

                textAudio.clip = audioClip;

                textAudio.Play();
   
            }
        }
        catch (WebException ex)
        {
            print(ex.Message);
        }
    }

    private float[] ConvertByteToFloat(byte[] array)
    {
        float[] floatArr = new float[array.Length / 4];
        for (int i = 0; i < floatArr.Length; i++)
        {
            if (BitConverter.IsLittleEndian)
                Array.Reverse(array, i * 4, 4);
            floatArr[i] = BitConverter.ToSingle(array, i * 4) / 0x80000000;
        }
        return floatArr;
    }
}

멘토님한테 여쭤봤더니 api는 보통 json에 담아서 httpWebRequest로 보낸다는 것을 알게 되었다

예시로 주신 코드를 내 프로젝트에 맞게 수정한 게 위의 코드임

 

구글 클라우드 플랫폼에 tts 문서에서 설명해준 대로 json에 원하는 정보를 담고 내 api키를 붙여서 httpWebRequest로 보내기

https://cloud.google.com/text-to-speech/docs/quickstart-protocol

 

빠른 시작: 명령줄 사용  |  Cloud Text-to-Speech API 문서  |  Google Cloud

이 빠른 시작에서는 Cloud Text-to-Speech API를 소개하며, Google Cloud Platform 프로젝트와 승인을 설정한 후 Text-to-Speech API에 요청을 수행하여 텍스트에서 오디오를 만듭니다. Cloud Text-to-Speech API에서 기본 개념에 대해 자세히 알아보려면 Cloud Text-to-Speech API 기본 사항을 읽어 보세요. 시작하기 전에 Google 계정에 로그인합니다.아직 계정이 없으면 새 계정을

cloud.google.com

근데 생각대로 잘 되지 않고 여전히 주소를 찾지 못한다는 에러를 냈다

 

https://cloud.google.com/text-to-speech/docs/reference/rest/v1/text/synthesize?apix_params=%7B%22resource%22%3A%7B%22audioConfig%22%3A%7B%22audioEncoding%22%3A%22LINEAR16%22%2C%22pitch%22%3A0%2C%22speakingRate%22%3A1%7D%2C%22input%22%3A%7B%22text%22%3A%22%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94%22%7D%2C%22voice%22%3A%7B%22languageCode%22%3A%22ko-KR%22%2C%22name%22%3A%22ko-KR-Standard-A%22%2C%22ssmlGender%22%3A%22NEUTRAL%22%7D%7D%7D

 

Method: text.synthesize  |  Cloud Text-to-Speech API  |  Google Cloud

audioContent string (bytes format) The audio data bytes encoded as specified in the request, including the header for encodings that are wrapped in containers (e.g. MP3, OGG_OPUS). For LINEAR16 audio, we include the WAV header. Note: as with all bytes fiel

cloud.google.com

위의 링크에서는 구글 tts api 문서에서 친절하게 리턴할 값까지 만들어서 주기까지 하는데

분명 하라는 대로 다 한 것 같은데 여전히 주소를 못찾고 리턴값이 없고ㅠ

 

https://github.com/steelejay/LowkeySpeech

 

steelejay/LowkeySpeech

Google Speech API in Unity (C#). Contribute to steelejay/LowkeySpeech development by creating an account on GitHub.

github.com

이 프로젝트는 구글 tts api를 사용한 유니티 샘플인데

유저 목소리를 녹음하라고 하고 녹음이 끝나면 임시 mp3파일로 저장, 읽어준다

이 소스를 참고해서 봤는데 이쪽은 마이크로 들어온 소리를 바로 clip에다가 넣는 방법을 쓰길래 내 상황이라는 뭔가 맞지 않았다

 

마지막까지 삽질을 하다가 결국 최후의 수단

모든 텍스트를 mp3로 만들어서 Resources에 넣고 가져와서 출력하는 방법으로 하기로 결정했다..

 

 

[3차 시도-완성]

모든 텍스트를 하나씩 mp3 파일로 만들어서 불러오는 방법

일단 mp3파일을 전부 만들어야 하는데 나는 49개밖에 없기 때문에 빠른 노가다했다

mp3파일은 파파고에서 뜯었다ㅋㅋ

 

1. 파파고 번역기에서 원하는 문장을 입력

2. 오디오 재생

3. 크롬 기준 f12눌러서 개발자 모드->Network가면 오디오 파일 생성되어 있음

4. 오른쪽 클릭해서 새 탭에서 열고 다운받기

 

참고로 구글 번역기도 된다

바꿔야 할 텍스트가 많다면 스크립트 돌리는 게 좋을텐데 49개밖에 없어서 노가다는 10분이면 됐음

 

그렇게 뜯어낸 mp3 파일들을 전부 프로젝트 폴더/Resources에 새폴더 만들어서 넣어줬다

 

이제 유니티 띄워서 새 스크립트 만들기

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class TextToSpeech : MonoBehaviour
{
    AudioSource audioSource;
    public void read(string text)
    {
        audioSource = gameObject.GetComponent<AudioSource>();

        //텍스트에 ?나 !가 있다면 없애줌(오디오 파일명 일치)
        if (text.IndexOf("!") != -1 || text.IndexOf("?") != -1)
        {
            text = System.Text.RegularExpressions.Regex.Replace(text, @"[!?]", "");
        }

        audioSource.clip = Resources.Load<AudioClip>("Audios/"+text);
        audioSource.Play();
    }
}

동작 흐름은 이렇게 짯다

1. GameManager.cs에서 스테이지 시작할 때 문제 텍스트를 TextToSpeech.read()로 넘김

2. TextToSpeech.cs의 read함수는 텍스트를 받아서 Resources/Audios 폴더에서 같은 이름의 mp3파일을 찾는다

3. 재생한다

 

다만 나는 한 가지 신경 써야 할 것이 있었는데

스테이지 중에 느낌표나 물음표가 들어간 텍스트가 있었음

근데 파일 제목에 느낌표/물음표를 쓸 수 없기 때문에 파일명이 텍스트와 일치하지 않는 경우가 생김

(위의 힘내라!는 힘내라.mp3 라서 둘이 텍스트가 다름)

 

그래서 mp3파일을 찾기 전에 if문으로 text에 !나 ?가 있으면 없애줬다(Regex 정규식 사용함)

 

빈 게임 오브젝트 만들어서 스크립트 붙여주고 오디오 붙여주면 끝

 

 

 

반응형
Comments