Flutterで行うSTT(Speech To Text)

---
# :book: Flutterの記事を整理し本にしました :book:
https://zenn.dev/kazutxt/books/flutter_practice_introduction

* <font color="red"><b>本稿の記事を含む様々な記事を体系的に整理し本にまとめました</b></font>
* 今後はこちらを最新化するため、<font color="red"><b>最新情報はこちらをご確認ください</b></font>
* 20万文字を超える超大作になっています!!

---

## まとめ記事
* [Flutter系の記事のまとめ](https://qiita.com/kazutxt/items/524acb1c134015682ccf)

# はじめに
スマートフォンの機能を使って、人間が喋った音声データをテキストに変換することができます。

# 事前準備

## パッケージインストール

```yaml:pubspec.yml
speech_to_text: ^3.2.0
```

## iOS

マイクと録音に関する権限を要求します。

```xml:ios/Runner/info.plist
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app requires to speech recognition/<string>
<key>NSMicrophoneUsageDescription</key>
<string>This app requires to add file to your photo library your microphone </string>
```

## Android

```xml:android/app/src/main/AndroidManifest.xml
<queries>
  <intent>
    <action android:name="android.speech.RecognitionService" />
  </intent>
</queries>
```

# 実装
SpeechToTextを使って、喋った内容を文字にします。

```dart:main.dart
import 'package:flutter/material.dart';
import 'package:speech_to_text/speech_recognition_error.dart';
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart' as stt;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String lastWords = "";
  String lastError = '';
  String lastStatus = '';
  stt.SpeechToText speech = stt.SpeechToText();

  Future<void> _speak() async {
    bool available = await speech.initialize(
        onError: errorListener, onStatus: statusListener);
    if (available) {
      speech.listen(onResult: resultListener);
    } else {
      print("The user has denied the use of speech recognition.");
    }
  }

  Future<void> _stop() async {
    speech.stop();
  }

  void resultListener(SpeechRecognitionResult result) {
    setState(() {
      lastWords = '${result.recognizedWords}';
    });
  }

  void errorListener(SpeechRecognitionError error) {
    setState(() {
      lastError = '${error.errorMsg} - ${error.permanent}';
    });
  }

  void statusListener(String status) {
    setState(() {
      lastStatus = '$status';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '変換文字:$lastWords',
              style: Theme.of(context).textTheme.headline4,
            ),
            Text(
              'ステータス : $lastStatus',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton:
          Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        FloatingActionButton(onPressed: _speak, child: Icon(Icons.play_arrow)),
        FloatingActionButton(onPressed: _stop, child: Icon(Icons.stop))
      ]),
    );
  }
}
```

`await speech.initialize`で初期化を行い、`speech.listen`でSTTを開始します。
`resultListener`に変換された文字が順次やってくるため、変数にいれて、格納しています。

# 動作確認

![](https://storage.googleapis.com/zenn-user-upload/gbt0yo5ohydrzq03sv6hkivecfu3 =200x)

再生ボタンで識別開始、停止ボタンで終了します。

Androidの場合は、一定期間音声がないと自動で終了します。