質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Q&A

解決済

1回答

1728閲覧

Whisperで文字起こしをしたいが、soundcardでの音声の処理時にエラーが出る

magui

総合スコア2

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

0グッド

0クリップ

投稿2023/01/14 12:14

前提

tkinterとwhisperとsoundcardを用いて、再生中の動画の文字起こしができるアプリを作成中です。

実現したいこと

・GUIのボタンを押すと、音声を録音しながらリアルタイムにwhisperで文字認識をするようにしたいです。

発生している問題・エラーメッセージ

ボタンを押すとモデルのロードまでは完了しますが、その後の処理(音声の処理部分)でエラーが出ます。

Exception in thread Thread-1 (do_whisper): Traceback (most recent call last): File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1009, in _bootstrap_inner self.run() File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\threading.py", line 946, in run self._target(*self._args, **self._kwargs) File "C:\Users\user\Documents\app\do_whisper.py", line 15, in do_whisper whisper_main() File "C:\Users\user\Documents\app\do_whisper.py", line 60, in whisper_main with sc.get_microphone(id=str(sc.default_speaker().name), include_loopback=True).recorder(samplerate=SAMPLE_RATE, channels=1) as mic: File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\soundcard\mediafoundation.py", line 116, in default_speaker with _DeviceEnumerator() as enum: File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\soundcard\mediafoundation.py", line 216, in __init__ _com.check_error(hr) File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\soundcard\mediafoundation.py", line 98, in check_error raise RuntimeError('Error {}'.format(hex(hresult+2**32))) RuntimeError: Error 0x800401f0

該当のソースコード

do_main.pyを実行します。

do_main.py

Python

1import tkinter as tk 2from do_whisper import * 3import threading 4 5class Application(tk.Frame): 6 def __init__(self, master = None): 7 super().__init__(master) 8 9 self.master.geometry("250x250") 10 self.txt = tk.StringVar(value="test") 11 12 button_top = tk.Button(self.master, text = "TOP", width = 8, command=self.changeLabel) 13 label = tk.Label(self.master, textvariable=self.txt, font=("Ubunt Mono", 20)) 14 15 button_top.pack(side = tk.TOP) 16 label.pack() 17 18 def changeLabel(self): 19 self.txt.set("Start...") 20 t = threading.Thread(target=do_whisper, args=(True,), daemon=True) 21 t.start() 22 23if __name__ == "__main__": 24 root = tk.Tk() 25 app = Application(master = root) 26 app.mainloop()

do_whisper.py

Python

1import whisper 2import soundcard as sc 3import threading 4import queue 5import numpy as np 6import argparse 7import threading 8 9flag = True 10 11def do_whisper(do_flag): 12 global flag 13 if do_flag == True: 14 flag = True 15 whisper_main() 16 else: 17 flag = False 18 19def whisper_main(): 20 SAMPLE_RATE = 16000 21 INTERVAL = 3 22 BUFFER_SIZE = 4096 23 24 parser = argparse.ArgumentParser() 25 parser.add_argument('--model', default='small') 26 args = parser.parse_args() 27 28 print('Loading model...') 29 model = whisper.load_model(args.model) 30 print('Done') 31 32 q = queue.Queue() 33 b = np.ones(100) / 100 34 35 options = whisper.DecodingOptions(fp16=False) 36 37 def recognize(): 38 while True: 39 audio = q.get() 40 if (audio ** 2).max() > 0.001: 41 audio = whisper.pad_or_trim(audio) 42 43 # make log-Mel spectrogram and move to the same device as the model 44 mel = whisper.log_mel_spectrogram(audio).to(model.device) 45 46 # detect the spoken language 47 _, probs = model.detect_language(mel) 48 49 # decode the audio 50 result = whisper.decode(model, mel, options) 51 52 # print the recognized text 53 print(f'{max(probs, key=probs.get)}: {result.text}') 54 55 56 th_recognize = threading.Thread(target=recognize, daemon=True) 57 th_recognize.start() 58 59 # start recording 60 with sc.get_microphone(id=str(sc.default_speaker().name), include_loopback=True).recorder(samplerate=SAMPLE_RATE, channels=1) as mic: 61 audio = np.empty(SAMPLE_RATE * INTERVAL + BUFFER_SIZE, dtype=np.float32) 62 n = 0 63 while True: 64 while n < SAMPLE_RATE * INTERVAL: 65 data = mic.record(BUFFER_SIZE) 66 audio[n:n+len(data)] = data.reshape(-1) 67 n += len(data) 68 69 # find silent periods 70 m = n * 4 // 5 71 vol = np.convolve(audio[m:n] ** 2, b, 'same') 72 m += vol.argmin() 73 q.put(audio[:m]) 74 75 audio_prev = audio 76 audio = np.empty(SAMPLE_RATE * INTERVAL + BUFFER_SIZE, dtype=np.float32) 77 audio[:n-m] = audio_prev[m:n] 78 n = n-m

試したこと

・kivyを使用して同様のアプリを作成した際は問題なく実行できましたが、tkinterだと実行できませんでした。
・do_whisper.pyのwhisper_mainはこちらのサイトのコードをそのまま使っています。
・上記サイトで「メインスレッド以外では動作しない」とあるため、threadingを使用しているのが不味いと予想していますが、kivyでは動作しtkinterでは動作しないという理由が分かりません。また解決方法も見当がつかず、ご教示いただきたいです。

補足情報(FW/ツールのバージョンなど)

Wiondows10
Python 3.10.2

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

kivy のコードが提示されてないので、kivy でうまくいく理由はわかりませんが、
マルチスレッドのプログラムでは、現状で期待通りに動いたとしても、
問題が再現していないだけで後々問題になる可能性があります。

確認のために、
tkinter を使わずに soundcard, wisper モジュールを
別スレッドで利用するコードをそれぞれ試してみてください。

現状の設計上の懸念点

例えば、wisper モジュール等も、
初期化とループ処理が別スレッドになっていて
スレッドを跨ぐリソースのアクセスが、タイミングによっては問題になる可能性があります。
→スレッド内で使うリソースは、基本そのスレッド内で初期化・後始末を完結します。
例外はスレッドセーフな設計で作られた、logging や queue 等の同期プリミティブなオブジェクトのみ
複数のスレッドに跨って扱うことができます。(デッドロック問題には、別途注意)

各ライブラリの、「メインスレッドしか使えない」というのも
厳密には、そのライブラリのリソースを初期化したスレッドと同じスレッド内でしか使えない
ということが多いので、
→ ライブラリの初期化がメインスレッドで行われていると、メインスレッドのみとなる。
一応、別スレッドで初期化を行うことの検討の余地もあります。

問題の再現性が低く、実行タイミング依存になると、デバッグが困難になるので
(利用者から不具合報告をもらっても、開発者側で再現ができない)
設計の段階でスレッドセーフな構成にすることをお勧めします。

個別のモジュールについては詳しく知りませんが、
GUI ライブラリの中にもメインスレッド以外では動作しないものは多いです。
(tkinter は別スレッドでも動きますが、完全なスレッドセーフではありません。稀に問題が報告されています)
メインスレッドでしか動かないライブラリを複数同時に使うには、
マルチプロセスにするか、イベントループを独自に作ることになります。

参考事例:
Pythonでマルチプロセスなオーディオプレイヤを作ってみた
tkinter + soundcard

投稿2023/01/19 05:08

編集2023/01/19 06:10
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

magui

2023/02/04 09:27

遅くなり申し訳ございません。 tkinterを使わずにsoundcardとwisperを別々のスレッドで動かすプログラムを作成したところ、正常に動作しました。 スレッドセーフな設計というものについて考慮していなかったのですが、これについては改めて勉強したいと思います。 ご回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問