Flutter 音声合成 (flutter_tts)
音声合成パッケージの利用
次は音声合成(text to speech)です。音声認識の時と同様に、使えそうなパッケージをhttps://pub.devから探します。検索すると、複数の候補がありますが、一つに絞らなければなりません。
選択基準は対象とするアプリの性格によって変わります。今回は、機能の豊富さよりも安定性重視で決めたいと思います。そこで、
- ある程度のバージョンアップを重ねているもの、
- 最終更新日から時間が経ちすぎていないもの、
の、2つの観点で決めます。
バージョンが若いと、潜在バグに悩まされる可能性がありますし、最終更新日から何年も経っていると、他のパッケージとの関係で使えなかったりするからです。
結果、flutter_ttsを選択しました。
flutter_ttsの使い方
flutter_ttsの使い方は、こちらにあります。まずは、以下のコマンドでインストールしておきます。
flutter pub add flutter_tts
音声認識に比べて、音声合成の実装はシンプルです。
FlutterTts flutterTts = FlutterTts();
String _speakText = "今日も良い天気です";
Future<void> _speak() async {
await flutterTts.setLanguage("ja-JP");
await flutterTts.setSpeechRate(1.0);
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.speak(_speakText);
}
例えば、ボタンをクリックしたタイミングなどで_speak()を呼び出すと、「今日も良い天気です」と、話します。
おうむ返しアプリケーション
次に、前回の音声認識のサンプルコードと組み合わせて、「おうむ返し」アプリケーションにしてみます。
音声認識のサンプルコードでは、ボタンを押すと、音声認識がスタートし、次にボタンを押すと音声認識がストップします。また、音声認識結果は認識の都度、_lastWordsにセットされ、setState()で画面に反映されていました。
今回は、音声認識が終了したタイミングで、音声合成を走らせるようにします。
改良したコードは、以下の通りです。
import 'package:flutter/material.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart';
import 'package:flutter_tts/flutter_tts.dart'; // ①
import "dart:async"; // ※1
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
FlutterTts flutterTts = FlutterTts(); // ②
SpeechToText _speechToText = SpeechToText();
bool _speechEnabled = false;
String _lastWords = '';
@override
void initState() {
super.initState();
_initSpeech();
_initFlutterTts(); // ③
}
/// This has to happen only once per app
void _initSpeech() async {
_speechEnabled = await _speechToText.initialize();
setState(() {});
}
void _initFlutterTts() async { // ④
await flutterTts.setIosAudioCategory(IosTextToSpeechAudioCategory.playback,
[IosTextToSpeechAudioCategoryOptions.defaultToSpeaker]); // ※ 2
await flutterTts.setLanguage("ja-JP");
await flutterTts.setSpeechRate(0.5);
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
}
/// Each time to start a speech recognition session
void _startListening() async {
await flutterTts.stop(); // ⑤
await _speechToText.listen(onResult: _onSpeechResult);
setState(() {});
}
/// Manually stop the active speech recognition session
/// Note that there are also timeouts that each platform enforces
/// and the SpeechToText plugin supports setting timeouts on the
/// listen method.
void _stopListening() async {
await _speechToText.stop();
await new Future.delayed(new Duration(seconds: 1)); // ※ 3
await _speak(); // ⑥
setState(() {});
}
/// This is the callback that the SpeechToText plugin calls when
/// the platform returns recognized words.
void _onSpeechResult(SpeechRecognitionResult result) {
setState(() {
_lastWords = result.recognizedWords;
});
}
Future<void> _speak() async { // ⑦
//音声合成
if (_lastWords != "") {
await flutterTts.speak(_lastWords);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Speech Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.all(16),
child: Text(
'Recognized words:',
style: TextStyle(fontSize: 20.0),
),
),
Expanded(
child: Container(
padding: EdgeInsets.all(16),
child: Text(
// If listening is active show the recognized words
_speechToText.isListening
? '$_lastWords'
// If listening isn't active but could be tell the user
// how to start it, otherwise indicate that speech
// recognition is not yet ready or not supported on
// the target device
: _speechEnabled
? 'Tap the microphone to start listening...'
: 'Speech not available',
),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed:
// If not yet listening for speech start, otherwise stop
_speechToText.isNotListening ? _startListening : _stopListening,
tooltip: 'Listen',
child: Icon(_speechToText.isNotListening ? Icons.mic_off : Icons.mic),
),
);
}
}
① 〜⑦の部分が音声合成のために追加したコードです。基本的には、これで動作すると思いましたが2箇所ほど修正が必要でした。
- iPhoneの場合ですが、電話のスピーカーから音声が出力されていました。これを修正するために、flutterTtsの初期化関数_initFlutterTts()の中で、オーディオスピーカーをセットしています。 ※ 2
- 音声合成が途中で途切れてしまう問題がありました。タイミングのようでしたので、音声認識終了後にディレイを1秒設けました。 ※ 1,3
これで、音声による「おうむ返しアプリケーション」の完成です。