28
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

音声でしか投稿できないSNS

Last updated at Posted at 2023-12-13

この記事はクソアプリ Advent Calendar 2023
シリーズ1の14日目の記事です。

つぶやきたい。。でも文字打つのがめんどくさい。。

TwitterなどのSNS、ちょっとつぶやくときにいいけど、
キーボードを打つのがめんどくさいときも。。

「ひとりごとをそのままツイートできれば。。」

そんなめんどくさがり屋のためのSNSを作りました!

つくったもの

音声でしか投稿できないSNS
「VoitterX」

トップにはルームの一覧があるので、
好きな部屋に入って、

マイクの権限を許可して話すと、
自動で投稿してくれます!

これでつぶやいたことがそのまま投稿に!!

※最初に匿名認証をしているのですこし待ちが長いです。。
※Chromeじゃないと動かないかも。。

使った技術

構成としてはこんな感じ。

スクリーンショット 2023-12-12 10.34.39.png

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、かつ、結果がないとき
  }
});

donedoneNoResultのあとは音声認識が完了し、音声入力が停止するので、
ステータスに応じて再度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日目の記事で書きます!

28
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?