기록

[Flutter] OpenAI Text to speech로 챗봇 채팅 음성으로 읽어주기(tts)

avocado8 2024. 9. 11. 20:59

지난번에 플러터 패키지를 이용해 stt를 구현했었다.

https://day4fternoon.tistory.com/126

 

[Flutter] speech_to_text로 음성 인식 구현하기

AI 채팅봇과 대화할 수 있는 채팅 페이지를 만드는데, 타이핑뿐만 아니라 음성으로도 메시지를 입력할 수 있게 해야 한다.speech_to_text 플러터 패키지를 사용해 구현할 수 있다. https://pub.dev/packages

day4fternoon.tistory.com

 

이제 만들 것은 반대로 챗봇 즉 AI의 채팅을 목소리로 읽어주는 기능이다. 대화 감상이 주제이므로 대화하는 느낌을 내기 위해 AI 채팅을 tts로 변환해보도록 하겠다.

 

stt 때는 speech_to_text라는 플러터 패키지로 간단히 구현했었다. tts도 플러터 패키지로 구현할 수 있긴 한데,

https://pub.dev/packages/flutter_tts

 

flutter_tts | Flutter package

A flutter plugin for Text to Speech. This plugin is supported on iOS, macOS, Android, Web, & Windows.

pub.dev

한국어 목소리가 여자하나 남자하나? 여튼 목소리 개수가 너무 한정적이었다.

나는 최소 4개의 목소리가 필요했으므로... 패키지 사용 대신 OpenAI tts API를 사용하기로 했다.

 

https://platform.openai.com/docs/guides/text-to-speech

6개의 목소리를 지원하긴 하는데 솔직히 내가 원하는 느낌은 아니었다... 좀더 귀엽고큐티친근한 목소리가 나왔으면 좋겠는데 일단 이건 해결할 수 있는 부분이 아니니, 목소리 개수가 좀 된다는 것만으로도 만족하고 사용하기로 헀다.

 

1. OpenAI 기본 설정하기

웬만한 오픈AI의 API는 플러터 프로젝트에서 dart_openai를 가지고 사용할 수 있다.

https://pub.dev/packages/dart_openai

 

dart_openai | Dart package

Dart SDK for openAI Apis (GPT-3 & DALL-E), integrate easily the power of OpenAI's state-of-the-art AI models into their Dart applications.

pub.dev

 

OpenAI API를 사용하려면 API 키가 필요하다. 오픈ai에 돈 내면 API키를 준다.

그리고 api키처럼 유출되면 안 되는 값은 환경변수 파일 .env를 만들어 따로 관리해주어야 한다.

플러터 프로젝트에서 .env를 설정하려면 패키지 설치 및 각종 설정이 필요하다. (위 dart_openai 문서에도 초기설정이 잘 나와있다)

 

1) 루트디렉토리에 .env파일을 만들고 API키를 적는다. 

 

2) envied 패키지 설치한다

https://pub.dev/packages/envied

 

envied | Dart package

Explicitly reads environment variables into a dart file from a .env file for more security and faster start up times.

pub.dev

 

얘네 다 설치해야된다

$ flutter pub add envied
$ flutter pub add --dev envied_generator
$ flutter pub add --dev build_runner

 

3) lib/env 폴더를 만들고 안에 env.dart 파일 만든 뒤 내용 작성

빨간줄이 뜰 텐데 일단 놔둬도 된다. env.g.dart 파일이 아직 없어서 생기는 문제이므로...

공식문서에는 없던데 apiKey 선언할 때 자료형도 같이 적어줘야 빌드가 정상적으로 된다.

import 'package:envied/envied.dart';
part 'env.g.dart';

@Envied(path: ".env")
abstract class Env {
  @EnviedField(varName: 'OPEN_AI_API_KEY') // the .env variable.
  static const String apiKey = _Env.apiKey;
}

 

4) 빌드 실행

터미널에

$ dart run build_runner build

입력해서 빌드 실행하면 env폴더에 env.g.dart 파일이 생성되고 빨간줄이 없어진다. 그럼 설정 끝

 

5) .gitignore 설정

api키는 유출되면 안되니까 .gitignore에 .env와 env.g.dart를 추가해주면 진짜 끝

 

 

2. TTS API 사용하기

dart_openai 패키지 공식문서를 쭉 내리다보면 speech를 어떻게 사용하는지 나온다.

참고해서 함수를 만들어주자.

 

봇채팅 위젯에 tts를 위한 재생버튼을 하나 추가해주고 여기 onPress에 할당할 함수를 만들것이다

  Future<void> getSpeech() async {
    OpenAI.apiKey = Env.apiKey;

    final directory = await getApplicationCacheDirectory();
    final speechOutputDir = Directory('${directory.path}/speechOutput');
    if (!speechOutputDir.existsSync()) {
      await speechOutputDir.create(recursive: true);
    }

    File speechFile = await OpenAI.instance.audio.createSpeech(
      model: "tts-1",
      input: message,
      voice: "nova",
      responseFormat: OpenAIAudioSpeechResponseFormat.mp3,
      outputDirectory: speechOutputDir,
    );

    print(speechFile.path);
  }

공식문서 그대로 디렉토리를 지정했더니 OS가 어쩌구 오류가 나서... 존재하지 않는다면 디렉토리를 create해라 하는 부분을 추가했다. 디버그 콘솔에 요청하고 받아와서 어쩌구 저쩌구 잘 됐고 어쩌구 그런 것들이 뜬다면 성공이다.

메시지를 스피치 즉 mp3파일로 변환하고 지정한 디렉토리에 저장한다.

근데 우리가 원하는 건 파일형태가 아니라 내 귀로 들어야 할 것 아닌가

그러기 위해 mp3 재생을 위한 패키지를 찾아봣고 audioplayers라는 패키지가 있다. 이걸 쓰기로 했다.

 

3. audioplayers 패키지 사용해 mp3파일 읽기

https://pub.dev/packages/audioplayers

 

audioplayers | Flutter package

A Flutter plugin to play multiple audio files simultaneously

pub.dev

사용법은 간단하다. 플레이어를 만들고 재생할 파일 경로를 입력해주기만 하면 된다.

  Future<void> getSpeech() async {
    OpenAI.apiKey = Env.apiKey;

    final directory = await getApplicationCacheDirectory();
    final speechOutputDir = Directory('${directory.path}/speechOutput');
    if (!speechOutputDir.existsSync()) {
      await speechOutputDir.create(recursive: true);
    }

    File speechFile = await OpenAI.instance.audio.createSpeech(
      model: "tts-1",
      input: message,
      voice: "nova",
      responseFormat: OpenAIAudioSpeechResponseFormat.mp3,
      outputDirectory: speechOutputDir,
    );

    print(speechFile.path);

    final player = AudioPlayer();
    await player.play(UrlSource('file://${speechFile.path}'));
  }

한가지 알아야 할 점은 파일 경로에 file:// 스킴을 추가해줘야 한다는 것...

그럼 이제 잘 작동한다.

 

결과물

 

귀여운 얼굴에 그렇지 못한 목소리지만... ...

오픈에이아이각성하라. 멋진목소리를만들도록

여튼 이런식으로 잘 작동한다. 굿 👌