【swift】音声入力された文字列のうち最後の単語だけ表示したい

実現したいこと

xcode 15.1
swift 5.8.1
AppleのSpeechフレークワークを用いてます。

音声入力したテキストのうち、最後の単語を画面に表示したいです。

ある程度の誤差はOKと考えてます。

発生している問題・分からないこと

問題というよりは仕様ですが以下2点が難しいと感じてます

1.rsultのStringはすべて繋がっている
例えば、「おはよう」「ありがとう」「さようなら」と音声入力したら、「さようなら」だけ表示したいのですが、result?.bestTranscription.formattedString は、「おはようありがとうさようなら」と繋がって出力されます。

2.タスク処理が複数回実行される
一度の音声入力で3回ほどタスク処理(DispatchQueue.main.async)が走ります

上記の点を考慮し以下のように実装しました

1.バッファと比較して以前の文字かどうかチェックする
2.今回の文字から以前の文字を削除して今回分だけを取り出す

発生している問題

動作させると一見良さそうなんですが、2点不具合確認できました
1.早口
早口で音声入力すると動画のようにおかしくなる
リンク内容

2.フリーズ?
しばらく入力してるとフリーズしたのか結果を返さなくなる
※デバッグにはエラーは出ていない

該当のソースコード

swift

1//2// ViewController.swift3// SpeakingToText4//5// Created by pero on 2023/12/13.6//7 8import UIKit9 10import AVFoundation11import Speech12 13class ViewController: UIViewController {14 15 16 @IBOutlet weak var label: UILabel!17 //音声認識で使用18 //認識言語は日本語設定19 let recognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "ja_JP"))!20 //入力に使うAVAudioEngine21 var audioEngine: AVAudioEngine!22 //バッファの音声を音声認識に使うリクエスト23 var recognitionReq: SFSpeechAudioBufferRecognitionRequest?24 //音声認識タスク25 var recognitionTask: SFSpeechRecognitionTask?26 27 var bufferText = ""28 29 override func viewDidLoad() {30 super.viewDidLoad()31 32 audioEngine = AVAudioEngine()33 self.label.text = ""34 35 do{36 try startLiveTranscription()37 }catch{38 print(error.localizedDescription)39 }40 41 }42 43 //音声認識44 func startLiveTranscription() throws {45 46 // もし前回の音声認識タスクが実行中ならキャンセル47 if let recognitionTask = self.recognitionTask {48 recognitionTask.cancel()49 self.recognitionTask = nil50 }51 52 self.label.text = ""53 54 // 音声認識リクエストの作成55 recognitionReq = SFSpeechAudioBufferRecognitionRequest()56 guard let recognitionReq = recognitionReq else {57 return58 }59 //途中の音声認識の結果も出力するように設定60 recognitionReq.shouldReportPartialResults = true61 62 // audioSessionのインスタンス取得(シングルトン)63 let audioSession = AVAudioSession.sharedInstance()64 //audiosessionの設定、.recordで入力に、.mesurementで測定モードへ、.mixWithOthersは他のアプリのaudiosessionと同時使用可能としている65 try audioSession.setCategory(.record, mode: .measurement, options: .mixWithOthers)66 //audiosessionをアクティブに、.notifyOthersOnDeactivationは他のアプリにAudioSessionを無効にしたことを伝える。67 //セッションが終わると他のアプリはAudioSessionを開始できる68 try audioSession.setActive(true, options: .notifyOthersOnDeactivation)69 70 // マイク入力の設定71 let recordingFormat = audioEngine.inputNode.outputFormat(forBus: 0)72 audioEngine.inputNode.installTap(onBus: 0, bufferSize: 2048, format: recordingFormat) { (buffer, time) in73 //音声認識のバッファーセット74 recognitionReq.append(buffer)75 }76 //audioEngineを開始(入力)77 try audioEngine.start()78 79 //音声認識タスクを処理80 recognitionTask = recognizer.recognitionTask(with: recognitionReq, resultHandler: { (result, error) in81 //停止時はエラーで帰ってくる82 if let error = error {83 print("\(error)")84 85 //システム音を鳴らすなどあればaudiosessionをもとに戻す86 do{87 try audioSession.setCategory(.soloAmbient, mode: .default, options: .mixWithOthers)88 }catch{89 print(error)90 }91 } else {92 DispatchQueue.main.async {93 94 var resultText = result?.bestTranscription.formattedString ?? ""95 print(resultText)96 //self.label.text = resultText97 98 if self.bufferText != resultText {99 let words = resultText.replacingOccurrences(of: self.bufferText, with: "")100 self.label.text = words 101 self.bufferText = resultText 102 }103 104 }105 }106 })107 }108 109}110 111

試したこと・調べたこと

上記の詳細・結果

IOSの音声入力に知見のある方、お力添えいただけないでしょうか?

よろしくお願い致します。

補足

特になし

コメントを投稿

0 コメント