パソコンのマイクに話した内容を文字にするアプリを作成しました。
SpeechRecognition の使い方、pyaudio のインストール方法、マルチスレッドの使い方、ウィジェット変数のトレースなどを説明します。
アプリの画面や機能などはこちらの記事で確認できます。
目次
- ◆SpeechRecognition で音声認識
- ◆連続音声入力のためのマルチスレッド
- ◆ScrolledText ウィジェットで結果表示
- ◆ウィジェット変数の監視
- ◆SpeechRecognition ライブラリ
- ◆SpeechRecognition パッケージ
- ◆concurrent.futures モジュール
- ◆Tkinterの使い方
- ◆ScrolledText ウィジェット
- ◆pyaudioのインストール
- ◆ソースの取得
- ◆さいごに
- ◆参考
音声認識の Python 向けパッケージには複数ありますが、SpeechRecognition を使用することにしました。 1
◆SpeechRecognition で音声認識
SpeechRecognition の使い方は非常に簡単です。
音声ファイルを使用するならメソッドを一つ動かすだけです。
ここでは、マイクから入力した音声を認識します。
そのため、2つのフェーズがあり、それぞれ該当するメソッドがあります。
それぞれのメソッドは単に実行するだけです。
- マイクから入力された音声を音声データにする(音声入力)
- Microphone オブジェクトを作成
- Recognizer クラスの
listen()
メソッドを実行
引数に Microphone オブジェクトを指定 - ※ Microphone オブジェクトを使う時は with 文と合わせて使うと PyAudioを通したマイクの open と後処理を実施してくれます
- 音声データを解析して文字にする(音声認識)
Recognizer クラスのrecognize_google()
メソッドを実行
(Google Speech Recognition 用)
▽音声入力するコード
import speech_recognition as sr self.r = sr.Recognizer() self.mic = sr.Microphone() def listen_voice(self, i): with self.mic as source: # マイク入力 audio = self.r.listen(source)
※実際のソースコードからデバッグ用コードなどを除いて表示しています。
▽音声データを認識するコード
def recognize_voice(self, audio, i) -> str: """ 音声認識(Google Speech Recognition) Args: audio: 音声データ int: 処理順 Returns: str: 認識した文字列 """ text = "" # recognize speech using Google Speech Recognition try: text = self.r.recognize_google(audio, language='ja-JP') self.my_frame.insert_msg(f"\n{i}===>{text}") except sr.UnknownValueError: self.my_frame.insert_msg("❓") except sr.RequestError as e: self.my_frame.insert_msg("❓") else: if text == "ストップ": self.do_break = True # 終了フラグをオン return text
基本的には、recognize_google()
メソッドを呼び出すだけです。
他はエラー処理と終了のためのフラグ設定です。
※ SpeechRecognition パッケージについては「◆SpeechRecognition パッケージ」節を参照してください。
◆連続音声入力のためのマルチスレッド
音声認識では、先ほども触れたように2つのフェーズがあります。
- マイクから入力された音声を音声データにする(音声入力)
- 音声データを解析して文字にする(音声認識)
音声入力では、音声の切れ目を検出して音声データを作ります。
音声認識では、その音声データを受けて音声解析をはじめます。
音声認識には時間が掛かります。
音声認識に音声データを渡したらすぐに次の音声入力を始める必要があります。
そうしないと連続した会話に対応できません。
そのため、音声認識はスレッドにして呼び出します。
また、音声認識は音声データの長さによって解析時間が異なります。
したがって、前の音声認識のスレッドの終了を待たずに次の音声認識のスレッドを起動します。
音声認識は I/O バウンドなタスクなのでマルチスレッド(マルチプロセスではなく)で対応します。
マルチスレッド用のライブラリには、threading もありますが、ここでは concurrent.futures の ThreadPoolExecutor を使用します。
理由は、音声認識スレッドの結果を認識を開始した順に受け取れるようにするためです。
※ concurrent.futures モジュールについては、「concurrent.futures モジュール ⤵」節を参照
※ ソースでは、threading モジュールのスレッドで音声入力を動かしていますが、画面(tkinter)が止まらないようにスレッドにしています。
【処理】音声入力の結果を持って音声認識を起動します
ThreadPoolExecutor
オブジェクトを作成- 以下を繰り返す
submit()
メソッドで音声認識メソッドをスケジュールsubmit()
メソッドの戻り値Futrue
オブジェクトをself.futures
に追加
※スレッドの終了を待たずに処理できます
- すべての音声入力が終わったら
self.futures
の要素を追加した順に取り出し、認識した文字列をファイルに出力
認識した文字列の取得は、Futrue
オブジェクトのresult()
メソッドで行います
※このメソッドは音声認識のスレッドの終了を待ちます shutdown()
メソッドで終了処理
※ ThreadPoolExecutor オブジェクトは、with 文と一緒に使うと後処理が必要なく便利ですが、起動したスレッドの終了を内部的に待つ( shutdown(wait=true)
を呼んだように)ため、ここでは使用していません。
▽スレッドプールで音声データを認識するコードを呼び出す
def __init__(self, my_frame) -> None: ...中略 self.futures = [] # スレッドプールの作成 self.pool = ThreadPoolExecutor(thread_name_prefix="Rec Thread") def recognize_voice_thread_pool(self, audio, i, event=None): """ スレッドプールで音声認識メソッドをスケジュール 作成された Futureオブジェクトをself.futuresに追加する Args: audio: 音声データ int: 処理順 """ future = self.pool.submit(self.recognize_voice, audio, i) self.futures.append(future)
▽認識した文字列をファイルに出力
# 同じファイルに追記する # 順番通り取得できるか self.insert_msg("▽結果") with open("音声入力.txt", "a", encoding="utf_8_sig") as f: f.write(f"\n◆{datetime.now().strftime('%Y年%m月%d日 %H:%M:%S')}\n") for future in self.vr.futures[:-1]: # 最後の「ストップ」を除く s = future.result() logger.debug("get future result") if not s:continue # 空文字は認識できなかった時なので出力しない # print(f"{s}") self.insert_msg(f"{s}") f.write(s + "\n") self.insert_msg("△", "\n\n")
▽スレッドプールの後処理
def delete_window(self): """ ウィンドウの☓が押された時の処理 """ self.vr.pool.shutdown() # ThreadPoolExcutorの終了処理 self.master.destroy() # 自分でウィンドウを削除する
◆ScrolledText ウィジェットで結果表示
音声認識できたテキストを表示するために ScrolledText ウィジェットを使います。
表示するテキストが複数行に渡るための選択です。
ScrolledText ウィジェットは、Text ウィジェットにスクロールバーが付いたものです。
基本的な機能は Text ウィジェットと同じです。
Text ウィジェットは、エディタなどでテキストを編集する部分を実現するウィジェットと考えていただければ良いです。
今回は編集機能などは使用せず、テキストを追加して表示するだけのために使用しています。
Text ウィジェットにテキストを追加するには、insert()
メソッドを使います。
その時に、どこに追加するのかをインデックスで指定します。
今回は常に末尾に追加するので tk.END
を使います。
ところですが、tk.INSERT
を使用しています。
テキストを追加すると tk.INSERT
の位置も動くのでこちらを使いました。
tk.INSERT
は、テキストカーソルの位置を示します。
更新:2022-12-28
self.txt.insert(tk.END, msg1)
※ ScrolledText ウィジェットの仕様は「◆ScrolledText ウィジェット」節で簡単に説明しています。
▽インポートとコンストラクタ
from tkinter import scrolledtext as tk_scrolledtext self.txt = tk_scrolledtext.ScrolledText(master, font=self.font4txt)
▽ScrolledTextウィジェットの作成(フォントの指定を含む)
self.v_font = tk.IntVar(master, args.font) self.v_font.trace_add("write", self.change_font_size) # v_fontが変更された時の処理を登録 self.c_font = tk.Entry(self.f_top, textvariable=self.v_font, width=3, justify=tk.RIGHT , validate="key", vcmd=(self.register(self.entry_validate), "%S")) self.font4txt = font.Font(size=self.v_font.get()) self.txt = tk_scrolledtext.ScrolledText(master, font=self.font4txt) self.txt.pack(fill=tk.BOTH, expand=True)
▽ScrolledText ウィジェットへメッセージの挿入
def insert_msg(self, msg:str, end:str="\n"): """ ScrolledText ウィジェットへメッセージの挿入 Args: str: 挿入する文字列 str: 終端文字 """ msg1 = msg + end self.txt.insert(tk.INSERT, msg1) self.txt.see(tk.INSERT) # self.txt.update_idletasks() # see()メソッドを実行すると必要ない
◆ウィジェット変数の監視
今回、しきい値とフォントサイズを変更できるように画面に Entry ウィジェットを作成しました。
Entry ウィジェットの数値を変更したら、対応する処理を実行します。
その方法としてウィジェット変数の監視を選択しました。
その理由は次のようなことです。
つまり、Entry ウィジェットにイベントを割り当ててしまうと検証結果が False でも動いてしまいます。
検証の結果、ウィジェット変数が変更されたらイベントが発生するようにしました。
▽ウィジェット変数のトレース設定
self.v_threshold.trace_add("write", self.mic_init) # v_thresholdが変更された時の処理を登録 self.v_font.trace_add("write", self.change_font_size) # v_fontが変更された時の処理を登録
トレース設定にはウィジェット変数オブジェクトの trace_add()
メソッドを使用します。
引数に変更のモードと実行する関数(コールバック関数)を指定します。
関数には3つの引数が定義されている必要があるので注意が必要です。
コールバック関数内で引数の値を明示的に使っていないので、定義だけしてあります。
▽コールバック関数の定義部分
def mic_init(self, var=None, index=None, mode=None): def change_font_size(self, var, index, mode):
◎メソッド
【trace_add】コールバック関数の登録
- 【構文】
trace_add(mode, callback)
- 引数
【trace_remove】コールバック関数の登録を解除
- 【構文】
trace_remove(mode, cbname)
【trace_info】登録されているコールバック関数名を返す
- 【構文】
trace_info()
◆SpeechRecognition ライブラリ
SpeechRecognition は、音声認識を実行するための Python ライブラリです。
特徴は、複数の認識エンジンをサポートしていることです。
認識エンジンは、オンラインまたはオフラインで動作するものがあります。
◇対応している音声認識エンジン/API
SpeechRecognition では以下の音声認識エンジン/API に対応しています。
それぞれ特徴があります。
他のサイトでの評価結果 2 などを基に、ここでは「Google Speech Recognition」を使用します。
エンジン | サン プル |
日本語 | 無料 | オフ ライン |
備考 |
---|---|---|---|---|---|
CMU Sphinx | ◎ | ✖ | ◎ | ◎ | |
Google Speech Recognition | ◎ | ◎ | ◎ | ✖ | |
Google Cloud Speech API | ◎ | ◎ | ✖ | ✖ | |
Wit.ai | ◎ | ◎ | ◎ | ✖ | 人工知能API |
Microsoft Bing Voice Recognition | ◎ | ◎ | ✖ | ✖ | |
Houndify API | ◎ | ✖ | ✖ | ✖ | 音楽認識検索 |
IBM Speech to Text | ◎ | ◎ | ✖ | ✖ | |
Snowboy Hotword Detection | ✖ | ✖ | ◎ | ◎ | ホットワード検出 開発元は2020/12/31終了Githubは残る Win非対応 |
※サンプル:サンプルコードが提供されているかどうか 3
※ホットワード検出:発話された時にデバイスをアクティブにすることを目的とした特別な単語またはフレーズの検出
◇依存関係
- PyAudio 0.2.11+ (マイク入力のために必要)
◇インストール
pip install pyaudio
※pyaudio のインストールに失敗する場合は「◆pyaudioのインストール ⤵」節を参照pip install SpeechRecognition
インポート:
import speech_recognition as sr
◆SpeechRecognition パッケージ
◇Recognizerクラス
音声認識のためのクラスです。
◎メソッド
【listen】 オーディオソースからの音を録音しオーディオデータを返します
- 【構文】
listen(source: AudioSource, timeout: Union[float, None] = None, phrase_time_limit: Union[float, None] = None, snowboy_configuration: Union[Tuple[str, Iterable[str]], None] = None) -> AudioData
- よく使う使い方(サンプル)
【構文】listen(source)
- 主な引数
source
:オーディオソースオブジェクト(マイクなど)timeout
:フレーズの開始を待機してから、WaitTimeoutError
例外をスローするまでの最大秒数
Noneの場合、待機タイムアウトなしphrase_time_limit
:フレーズを継続できる最大秒数
結果として得られる音声は、制限時間にカットオフされたフレーズ
Noneの場合、フレーズの時間制限はない
- 戻り値:オーディオソースからの単一のフレーズを記録した
AudioData
オブジェクト - 記録開始:オーディオの大きさが
energy_threshold
属性値を超える(ユーザーが話し始めた)までは待機
※energy_threshold
属性値のデフォルトは、300
ただしdynamic_energy_threshold(デフォルトは有効)が有効なら自動調節 - 記録終了:
pause_threshold
秒(デフォルトは0.8秒)の無音と判断する状態が発生するか、
オーディオ入力がなくなると終了
【adjust_for_ambient_noise】 外部雑音に合わせてしきい値を調整します
- 【構文】
adjust_for_ambient_noise(source: AudioSource, duration: float = 1) -> None
オーディオソースを通して周囲のノイズを考慮した しきい値を動的に調整
本メソッドは音声のない期間に実行する
音声が検出されると早期に停止 - 主な引数
source
:オーディオソースオブジェクト(マイクなど)duration
:しきい値の動的調整時間の最大値(秒)(デフォルト1秒) 少なくとも0.5以上が望ましい。
【recognize_google】 Google Speech Recognition を使用して音声認識します
- 【構文】
recognize_google(self, audio_data, key=None, language="en-US", show_all=False)
- よく使う使い方(サンプル)
【構文】recognize_google(audio, language='ja-JP'))
- 主な引数
audio_data
:オーディオデータlanguage
:日本語はja-JP
◎属性
energy_threshold
:デフォルト 300
無音と音声を区別する音の大きさのレベルのしきい値
このしきい値を下回る値は無音と見なされます
このしきい値を超える値は音声と見なされます
動的しきい値(dynamic_energy_threshold
を参照)が有効になっている場合、自動調整します
適切な開始値は、自動調整を早く終わらせます
静かな部屋での一般的な値は 0〜100 です
話し声がしているような環境での一般的な値は 150〜3500 です
無音とみなして欲しいのに音を拾ってしまう場合、より高い値に調整してみてくださいpause_threshold
:デフォルト 0.8 秒
この長さ分の無音状態が続くとフレーズの終わりと判断しますdynamic_energy_threshold
:デフォルト True
energy_threshold
を、リスニング中に自動調整するかどうかを表すフラグ
周囲のノイズレベルが予測できない状況の場合に使うと良いです
◇Microphoneクラス
PC 上の物理マイクを表すクラスです。
◎コンストラクタ
- 【構文】
Microphone(device_index: Union[int, None] = None, sample_rate: int = 16000, chunk_size: int = 1024) -> Microphone
with文の使用が可能。使用した場合、PyAudioを通してマイクのopenと後処理を実施 - 【使い方】
with Microphone() as source: # open the microphone and start recording pass # do things here - ``source`` is the Microphone # instance created above # the microphone is automatically released at this point
◆concurrent.futures モジュール
concurrent.futures モジュールは、非同期に実行できる呼び出し可能オブジェクトの高水準のインターフェースを提供します。
使用にはインポートが必要です。
from concurrent.futures import ThreadPoolExecutor
◇クラス
- ThreadPoolExecutor
- スレッドを使って並列タスクを実行
- ネットワークアクセスなど CPU に負荷がかからない(I/O バウンドな)処理の並列実行向き
- ProcessPoolExecutor
- プロセスを使って並列タスクを実行
- CPU に負荷がかかる計算(CPU バウンドな)処理などの並列実行向き
- Future
- カプセル化された呼び出し可能オブジェクトの非同期実行状態
submit() メソッドで生成される - 呼び出し可能オブジェクトのキャンセルや実行結果の取得が可能
- カプセル化された呼び出し可能オブジェクトの非同期実行状態
◇メソッド
以下は、ThreadPoolExecutor クラス、ProcessPoolExecutor クラスで使えるメソッドです。
【submit】関数の実行をスケジュールします
- 【構文】
submit(fn, *args, **kwargs)
- 引数
fn
:fn(*args, **kwargs)
をスケジュール
- 戻り値
- Future オブジェクト
【shuttdown】 Executor(ThreadPoolExecutor オブジェクトなど)の終了処理
- 【構文】
shuttdown(wait=True, *, cancel_futures=False)
- 引数
wait
:待ち方
wait の値に関係なく、すべての未完了の Future の実行が完了するまで Pythonプログラム全体は終了しない- True:すべての未完了の Future の実行が完了して Executor に 関連付けられたリソースが解放されたら、このメソッドが返る
- False:このメソッドはすぐに返る
すべての未完了の Future の実行が完了したときに、Executorに関連付けられたリソースが解放される
- with 文
with 文を使用すると、このメソッドを明示的に呼ばなくてすむ
with 文は Executor をシャットダウンする (wait を True にセットして Executor.shutdown() が呼ばれたかのように待つ)
◆Tkinterの使い方
Tkinter(ティーキンター)は Python が標準で提供している GUI パッケージです。
他のプログラムの開発ツールで提供されているようなルック&フィールな GUI ツールではありません。
従って、画面を構成するには、すべてコードを書く必要があります。
Tkinter を使用するには import が必要です。
import tkinter as tk
慣例的に tk と別名を付けるようです。
◇Tkinterの使用ウィジェット
Tkinter がサポートする GUI の部品をウィジェット (widget) と呼びます。
ここでは次のウィジェットを使用しています。
◇使用ウィジェット
ウィジェットは基本的にインスタンスを作成し、表示テキストや表示方法などを指定して使用します。
今回使用しているウィジェットです。
パッケージ | ウィジェット名 | 用途 | 見た目 |
---|---|---|---|
tkinter | Frame フレーム |
ウィジェットの受け皿 | |
tkinter | Label ラベル |
文字の表示 | |
tkinter | Button ボタン |
押すと処理が動くボタン | |
tkinter | Checkbutton チェックボックス |
チェックでオン/オフを設定 | |
tkinter | Entry エントリー |
テキストを入力するボックス | |
tkinter | ScrolledText スクロールバー付テキスト |
テキストの編集 |
◇画面構成 - フレーム構造
プログラムを作るにあたって、どのような画面にするか決めなくてはなりません。画面構成を考えるということですね。
画面を構成するというのは、GUI 部品(ウィジェット)を画面に配置することです。
ウィジェットは設定したテキストに合わせてサイズが決まるので、そのまま使用しても画面構成上問題ないものと、広げないと見栄えが良くないものが出てきます。
ウィジェットを広げる場合、他のウィジェットとの位置関係で思ったように配置できないことがあります。
そのようなときに Frame ウィジェットを使用して区分けをして希望する位置に配置します。
引き出しの整理に使うトレイのようなイメージでしょうか。
今回の画面構成です。
- f_top (frame)
- b_start (Button)
- l_threshold (Label)
- e_threshold (Entry)
- c_adjust (Checkbutton)
- l_font (Label)
- c_font (Entry)
- txt (ScrolledText)
※「f_top」などはプログラムで使用した変数名です。()内はクラス名です。
入力をする部分と出力をする部分で大きくウィジェットを分けています。
結果を表示する部分は余った領域をすべて使用して、できるだけ大きく表示させています。
◇ウィジェットの配置 - pack
▶説明は別記事を参照してください
📖 ◇ウィジェットの配置 - pack - SQLクライアントアプリの作り方(Tkinterで表)【Python】 🔗
◇動作をウィジェットに割り当てる - バインド
▶説明は別記事を参照してください
📖 □動作をウィジェットに割り当てる - Excel viewerアプリの作り方(Tkinterでタブと表)【Python】 🔗
◆ScrolledText ウィジェット
Text ウィジェットを継承してスクロールバーが付加されたウィジェットです。
ここでは、使用している insert()
メソッドに関する仕様を説明します。
使用するには tkinter とは別にインポートが必要です。
from tkinter import scrolledtext as tk_scrolledtext
◇インデックス
インデックスとは、Text ウィジェットに挿入したコンテンツの中の位置を指します。
インデックスを指定して挿入や削除する位置を指定します。
【主なインデックス】
行.桁
:行(1から数えて)、列(0から数えて)行.end
:指定された行の末尾の改行の直前の位置tk.INSERT
:テキストウィジェットの挿入カーソルの位置。"insert"
と同じtk.CURRENT
:マウスポインターに最も近い文字の位置。"current"
と同じtk.END
:テキストの最後の文字の後の位置。"end"
と同じtk.SEL_FIRST
:選択範囲の先頭の位置。未選択だとエラーtk.SEL_LAST
:選択範囲の末尾の位置。未選択だとエラー
◇メソッド
【insert】テキストの挿入
- 【構文】
insert(index, text, tags=None)
- 引数
index
:挿入位置text
:テキストtags
:タグを設定する場合に指定
※タグについては説明を省略します
◆pyaudioのインストール
PyAudio のインストールは pip で行います。
pip install pyaudoi
しかし、次のようなエラーが出る場合があります。
その場合の対処方法を説明します。
◇pyaudioがインストールできない場合
◎C++ビルドツールがない
「Microsoft Visual C++ 14.0 or greater is required.」 が出た場合 4
C++ がコンパイラがなくてビルドできないので、コンパイラをインストールします。
- 入手先:Visual Studio Tools のダウンロード - Windows、Mac、Linux 用の無料インストール
- 「すべてのダウンロード」に「build tools」と指定して検索
- 「Build Tools for Visual Studio 2022」をダウンロード
※最新のものをダウンロードします(年が変わったら)
◎.whl
ファイルがない
「package 'wheel' is not installed」 が出た場合 5
whl ファイルを取得します。
『GitHub - Uberi/speech_recognition 』で紹介している 『speech_recognition/third-party at master · Uberi/speech_recognition · GitHub 』 には古いファイルしかないので、下記から取得します。
- 取得先:Archived: Python Extension Packages for Windows - Christoph Gohlke
- ダウンロード:
PyAudio-0.2.11-cp38-cp38-win_amd64.whl
※「cp38」のところはPythonのバージョンに合わせます - インストール:
pip install PyAudio-0.2.11-cp38-cp38-win_amd64.whl
※保存したフォルダで実行します
◆ソースの取得
全体のソースはこちらから取得できます。
- ソース:voice_input_GSR.py
- 取得先:GitHub juu7g/Python-voice-input
※ソースにはデバッグ用のコードが含まれていますのでご容赦ください。
デバッグには logging モジュールを使用しています。
logging モジュールの使い方については、別の記事で解説する予定です。
◆さいごに
単発での音声入力、認識は、SpeechRecognition が優秀なので直ぐに動きました。
それよりも、pyaudio のインストールやマイクの動作確認(結果的にマイクのコードが不安定だっただけ)、マルチスレッド対応で時間が掛かりました。
マルチスレッドをそれなりに使うのは初めてで、思ったように動いているのかどうかを確認したくて、logger でスレッド名を出力したデバッグ情報を出せるようにしました。
logger の使い方も少し調べると作法があるようで、自分なりに良いと思った方法を取り入れています。(この記事で説明はしていませんが・・・🙇)
更に、MVC(Model-View-Controller) なるソフトウェアアーキテクチャーの存在を遅まきながら知ることとなり、試してみました。
こじんまりしたソースですが、いろいろとチャレンジしてみました。
📖 CMU Sphinx音響モデルの適応【Python】 🔗
◇ご注意
本記事は次のバージョンの下で動作した内容を基に記述しています。
◇免責事項
ご利用に際しては、『免責事項』をご確認ください。
お気づきの点がございましたら『お問い合わせ』からお問い合わせください。
◆参考
- PyPI:SpeechRecognition
- リファレンス:speech_recognition/library-reference.rst at master · Uberi/speech_recognition · GitHub
- マルチスレッド:Pythonでconcurrent.futuresを使った並列タスク実行 - Qiita
- マルチスレッド:concurrent.futures -- 並列タスク実行 — Python 3.11.0b5 ドキュメント
- マルチスレッド:Shutting Down Python's ThreadPoolExecutor
- ウィジェット変数のトレース:Tracing Tkinter variables in Python - GeeksforGeeks
- ウィジェット変数のトレース:trace manual page - Tcl Built-In Commands
- Text ウィジェット:24. The Text widget
-
次のサイトの情報から判断しました。
認識精度はいかほど?音声認識ツールを試してみた | MISO
PythonでJuliusを動かして音声認識システム作ってみた ↩ - こちらのサイト『Pythonで音声からテキストへ変換【SpeechRecognition】 | ジコログ 』で詳しく比較されています。↩
- サンプル:speech_recognition/microphone_recognition.py at master · Uberi/speech_recognition · GitHub ↩
- 「Microsoft Visual C++ 14.0 or greater is required.」が出た場合の対処方法 | ジコログ ↩
- 【Python】PyAudioがインストールできない場合の対処法(Windows) - TECH LABO RJC ↩