投稿日: 2024年9月29日
最終更新日: 2024年12月05日
問題: 日本語のスピーキングを練習するためにシャドーイング(自分の声を録音し、即座に再生音を聞くこと)をしたい。 しかし、音声の即時再生機能(リアルタイムのオーディオプレイバック)は通常、マイクの品質をチェックするための他のプログラムの機能にすぎないので、 これができる専用のプログラムがあまりない。
解決策:Pythonを使って自分でリアルタイム音声の再生(プレイバック)のプログラムを作る
結果: Pythonでプログラミングする大きな利点の一つは、たくさんのライブラリーがあることである。今回のプロジェクトは、 その利点を活用し、4つのライブラリーを使用した。そのライブラリーは以下の通りである。
pyaudio
音声を録音し、即座に再生する
tkinter
誰でも使えるようなGUI(General User Interface)を作る
threading
2つのループが重なり、プログラムが停止を防ぐ
mttkinter
tkinterを使用する時のthreadingを使いやすくする
def defocus(event): event.widget.master.focus_set() class input_device: def __init__(self, name, index): self.name = name self.index = index
def playback(): p = pyaudio.PyAudio() streamIn = p.open(format=FORMATIN, channels=CHANNELS, rate=RATE, input=True, input_device_index=INDEX_SELECT, frames_per_buffer=CHUNK) streamOut = p.open(format=FORMATOUT, channels=CHANNELS, rate=RATE, output=True, output_device_index=4, frames_per_buffer=CHUNK) while True: in_data = streamIn.read(CHUNK) streamOut.write(in_data)
def index_select(): global INDEX_SELECT if microphone_combobox.get() == devices[0].name: INDEX_SELECT = devices[0].index elif microphone_combobox.get() == devices[1].name: INDEX_SELECT = devices[1].index elif microphone_combobox.get() == devices[2].name: INDEX_SELECT = devices[2].index
def button_state(): global button1_num_clicked if ( microphone_combobox.get() != devices[0].name and microphone_combobox.get() != devices[1].name and microphone_combobox.get() != devices[2].name ): alert() else: button1_num_clicked += 1 change_text() index_select() t1 = threading.Thread(target=playback,daemon = True) t1.start() if button1_num_clicked % 2 == 0: window.destroy()
考察:
pythonのいくつかのライブラリーを活用することで、、リアルタイムでオーディオを再生するプログラムを作成ことができることがわかった。 しかし、万能なライブラリーは存在しないため、互換性の問題が発生することがある。今回はプログラムが小規模だったので、互換性の問題を解決するのは それほど難しくなかった。しかし、もっと大きなプログラムになると、開発者はライブラリを調整したり、独自のライブラリを作ったりしなければならないかもしれない。 このプロジェクトを通じて、エンジニアは多くの問題を解決するためにフレキシブルであることの必要性を改めて感じた。
このプログラムにはいくつもの改善点がある。入力デバイスの選択ボックスは最初の3つのデバイスだけに制限されているので、 これを利用可能なすべてのデバイスに変更できる。また、出力デバイス(スピーカー、ヘッドフォン)の選択ボックスや、 録音のその他の設定(frames_per_buffer、フォーマットなど)を手動で調整できる機能を追加することもできる。 もちろん、コードをより効率的にし、GUIをより綺麗なものにすることもできるだろう。
これはまだプログラムのv0.1(バージョン0.1)なので、今のところ公開する予定はない。しかし、必要なライブラリーをインポートし、コードをコピーしたら、誰でもプログラムを実行するのは可能である。 このプログラムを使う方法のひとつは、外国語の録音(ポッドキャストやYouTubeの動画)を再生し、 できるだけ速く自分でその音声を発音してみることである。これを1日20~30分続けることで、 私はこのプログラムで日本語のスピーキングスキルを向上させたが、英語や他の外国語にも使えると思う。
import pyaudio
import threading
from tkinter import messagebox
from tkinter import ttk
from mttkinter import mtTkinter as tk
#選択表示(combobox)を使いやすくする
def defocus(event):
event.widget.master.focus_set()
#入力デバイスの扱いが簡単になる
class input_device:
def __init__(self, name, index):
self.name = name
self.index = index
#リアルタイムでのプレイバックを行う(録音した音すぐに再生する)
def playback():
p = pyaudio.PyAudio()
streamIn = p.open(format=FORMATIN, channels=CHANNELS,
rate=RATE, input=True, input_device_index=INDEX_SELECT,
frames_per_buffer=CHUNK)
streamOut = p.open(format=FORMATOUT, channels=CHANNELS,
rate=RATE, output=True, output_device_index=4,
frames_per_buffer=CHUNK)
while True:
in_data = streamIn.read(CHUNK)
streamOut.write(in_data)
def alert():
messagebox.showinfo(title= 'Error',message='Please select an input device')
def change_text():
button1['text'] = 'End recording'
#「Choose a microphone」というcomboboxで選択した入力デバイスを記録しplayback()に送信
def index_select():
global INDEX_SELECT
if microphone_combobox.get() == devices[0].name:
INDEX_SELECT = devices[0].index
elif microphone_combobox.get() == devices[1].name:
INDEX_SELECT = devices[1].index
elif microphone_combobox.get() == devices[2].name:
INDEX_SELECT = devices[2].index
#入力デバイスが選択されない限り、再生が始まらないようにする
def button_state():
global button1_num_clicked
if (
microphone_combobox.get() != devices[0].name and
microphone_combobox.get() != devices[1].name and
microphone_combobox.get() != devices[2].name
):
alert()
else:
button1_num_clicked += 1
change_text()
index_select()
#daemonは「End recording」がクリックされた後、プログラムが確実に停止
t1 = threading.Thread(target=playback,daemon = True)
t1.start()
#「End recording」は、入力デバイスを選択してから、2回目のクリックになるので、プラグラムが停止
if button1_num_clicked % 2 == 0:
window.destroy()
#プレイバックのメインループに入る
if __name__ == '__main__':
#プレイバック設定の選択
button1_num_clicked = 0
devices = []
buttonClicked = False
p = pyaudio.PyAudio()
info = p.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
FORMATIN = pyaudio.paInt16
FORMATOUT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 1024
window = tk.Tk()
window.title('Real-time audio playback v0.1')
window.geometry('500x150')
#アクセスできる入力デバイスの確認
#最初の3つのデバイスがcomboboxに送信
for i in range(0, numdevices):
if (p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
devices.append(input_device(p.get_device_info_by_host_api_device_index(0, i).get('name'), i))
#comboboxとその他ボタンの設定、調整
microphone = tk.Label(window, text="Choose a microphone")
microphone_combobox = ttk.Combobox(window, values = [devices[0].name,devices[1].name,devices[2].name])
microphone.place(x=80, y=40)
microphone_combobox.place(x=260, y=40)
microphone_combobox.bind("", defocus)
button1 = tk.Button(window, text="Start recording", command=button_state)
button1.place(x=200, y=80)
window.mainloop()