この記事はクソアプリ Advent Calendar 2023の
シリーズ1の14日目の記事です。
つぶやきたい。。でも文字打つのがめんどくさい。。
TwitterなどのSNS、ちょっとつぶやくときにいいけど、
キーボードを打つのがめんどくさいときも。。
「ひとりごとをそのままツイートできれば。。」
そんなめんどくさがり屋のためのSNSを作りました!
つくったもの
音声でしか投稿できないSNS
「VoitterX」
トップにはルームの一覧があるので、
好きな部屋に入って、
マイクの権限を許可して話すと、
自動で投稿してくれます!
これでつぶやいたことがそのまま投稿に!!
※最初に匿名認証をしているのですこし待ちが長いです。。
※Chromeじゃないと動かないかも。。
使った技術
構成としてはこんな感じ。
- アプリ部分
- Firebase Hosting
- Flutter Web/CanvasKit
- 音声認識: speech_to_text | Flutter Package
- トースト通知: toastification | Flutter Package
- サーバ部分
- データ部分
Flutter Webを試してみたかったのと、
ルームごとのOGP画像を動的に生成したかったので、
Cloud Functions(2nd)も使うような構成に。
音声認識
音声認識パッケージには、Flutterのspeech_to_textを利用。
Webの場合の中身は、Web Speech APIを利用しているよう。
speech_to_textの使い方
speech_to_textの使い方はこんな感じ。
import 'package:speech_to_text/speech_to_text.dart';
final SpeechToText client = SpeechToText();
String status = "";
String resultWords = "";
// 初期化
await client.initialize(onStatus: (status) {
// ステータスの変化を受け取って保存
status = status;
});
// 音声認識の開始
await client.listen(
onResult: (result) {
// 音声認識の結果を受け取って保存
resultWords = result.recognizedWords;
},
partialResults: true, // 途中経過の結果も受け取るように
localeId: "ja-JP", // ロケールは日本語に設定
);
// 音声認識の停止
await client.stop();
音声認識を継続する
ルームに入ったときは、音声認識を保ってほしい。
speech_to_textのpartialResults: true
をセットすると、
// 音声認識の開始
await client.listen(
partialResults: true, // 途中経過の結果も受け取るように
);
Web Speech APIのcontinuous = true
をセットしてくれる。
const recognition = new SpeechRecognition();
recognition.continuous = true;
また、speech_to_textのステータス(status
)の値と、
対応するWeb Speech APIのイベントはこんな感じ。
// 初期化
await client.initialize(onStatus: (status) {
// ステータスの変化を受け取って保存
if (status == "listening") {
// 入力音声の取得中
// - SpeechRecognition.onStart
// - SpeechRecognition.onSpeechStart
} else if (status == "notListening") {
// 入力音声の取得停止中
// - SpeechRecognition.onEnd
} else if (status == "done") {
// 音声認識が完了(結果あり)
// - SpeechRecognition.onEnd、かつ、結果があるとき
} else if (status == "doneNoResult") {
// 音声認識が完了(結果なし)
// - SpeechRecognition.onEnd、かつ、結果がないとき
}
});
done
やdoneNoResult
のあとは音声認識が完了し、音声入力が停止するので、
ステータスに応じて再度client.listen()
を呼び出したり、
Timer.periodic()
を使ったりして、継続するようにしている。
自動で投稿する
自動投稿部分はこんな感じ。
-
onResult()
で結果を受け取り続け、 - 0.5秒間、結果の変更がなければ自動投稿する
// 音声認識の開始
await client.listen(
onResult: (result) {
// 音声認識の結果を受け取って保存
resultWords = result.recognizedWords;
// Timerをつかって遅延実行する
_createTimer(resultWords);
},
partialResults: true, // 途中経過の結果も受け取るように
localeId: "ja-JP", // ロケールは日本語に設定
);
// 0.5秒間、音声認識の結果を受け取らなければ、
// 自動で投稿する処理
Timer? _timer = null;
Timer _createTimer(String content) {
// すでにTimerがあれば、キャンセル
_timer?.cancel();
// 0.5秒後に音声認識の結果を投稿する
_timer = Timer(const Duration(milliseconds: 500), () async {
try {
if (content == null || content.isEmpty) return;
// TODO: firestoreに追加する処理
} finally {
// 結果(SpeechRecognitionResult)が残り続けるので
// 一旦停止して、再度listenし直すことで結果を空にする
await stopListen();
}
});
}
おわりに
これで手が離せなくても、つぶやけちゃいますね♪
よかったら、ぜひ遊んでみてください〜(*´ω`*)
P.S.
ちょっと長くなってきたので、OGP周りの話は、
Flutter Advent Calendar 2023の16日目の記事で書きます!