PyAudioの録音に関する記事を検索するとまるでコピペしたかのようなサンプルプログラムが紹介されています。ここでそんなコードを書いても芸がないのでTkinterを使った録音のコードを紹介します。
まずは外観。
仕様 録音ボタンを押すと5秒間録音します。 録音中は停止ボタンで録音を停止できます。 録音されると保存ボタンが有効になります。
ソース
# -*- coding: utf-8 -*-
import pyaudio
import sys
import tkinter as tk
import wave
DEVICE_INDEX = 0
CHUNK = 512
FORMAT = pyaudio.paInt16 # 16bit
CHANNELS = 1 # monaural
RATE = 44100 # sampling frequency [Hz]
REC_TIME = 5 # sec
MAX_FRAME = int(REC_TIME * RATE / CHUNK)
WAVE_FILENAME = 'test.wav'
g_stopReq = False
g_recording = False
g_recorded = False
g_audio = pyaudio.PyAudio()
# g_stream = None とするとすっとNoneのまま
#一旦オープンしてクローズする
g_stream = g_audio.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
input_device_index = DEVICE_INDEX,
frames_per_buffer=CHUNK)
g_stream.close()
g_frames = []
g_frameIndex = 0
g_btnRec = None
g_btnStop = None
g_btnSave = None
#ボタンの有効無効の処理
def btnCtrl():
global g_recording, g_btnRec , g_btnStop, g_btnSave
print(g_recording)
if g_recording == True:
g_btnRec .configure(state = 'disable')
g_btnStop.configure(state = 'normal')
else:
g_btnRec .configure(state = 'normal')
g_btnStop.configure(state = 'disable')
if g_recorded == True:
if g_recording == False:
g_btnSave.configure(state = 'normal')
else:
g_btnSave.configure(state = 'disable')
else:
g_btnSave.configure(state = 'disable')
def closeAudio():
global g_frameIndex, g_stream
g_stream.stop_stream()
g_stream.close()
def callback(in_data, frame_count, time_info, status):
global g_frameIndex, g_stream,g_recording, g_recorded,g_stopReq
g_frames.append(in_data)
g_frameIndex += 1
if g_frameIndex >= MAX_FRAME or g_stopReq == True:
closeAudio()
g_recording = False
g_recorded = True
g_stopReq = False
btnCtrl()
return(None, pyaudio.paAbort)
return (None, pyaudio.paContinue)
#ボタンをクリックしたときのイベントハンドラー
def rec_clicked():
global g_frames,g_recording, g_audio
g_recording = True
g_frameIndex = 0
g_frames.clear()
g_stream = g_audio.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
input_device_index = DEVICE_INDEX,
frames_per_buffer=CHUNK,
stream_callback=callback)
g_stream.start_stream()
btnCtrl()
def stop_clicked():
global g_stopReq
g_stopReq = True
btnCtrl()
def save_clicked():
print('saved')
saveToFile(WAVE_FILENAME)
btnCtrl()
def saveToFile(fileName):
global g_frame,g_audio
wf = wave.open(fileName, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(g_audio.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(g_frames))
wf.close()
def main():
global g_frame,g_recording, g_btnRec , g_btnStop, g_btnSave
root = tk.Tk()
root.title("オーディオ録音")
root.geometry("250x36")
g_btnRec = tk.Button(
root,
text='録音',
compound=tk.TOP,
command=rec_clicked)
g_btnRec .grid(row = 0,column = 0)
g_btnStop = tk.Button(
root,
text='停止',
state = 'disable',
compound=tk.TOP,
command= stop_clicked)
g_btnStop.grid(row = 0,column = 1)
g_btnSave = tk.Button(
root,
text='保存',
state = 'disable',
compound=tk.TOP,
command=save_clicked)
g_btnSave.grid(row = 0,column = 2)
root.mainloop()
g_audio.terminate()
if __name__ == "__main__":
main()
よそのサイトと違っているところはコールバッグ関数を使っています。これを使うことにより録音中に固まってしますことがありません。
具体的には録音ボタンを押したときのイベントハンドラーrec_clicked()でセットしています。
#ボタンをクリックしたときのイベントハンドラー
def rec_clicked():
global g_frames,g_recording, g_audio
g_recording = True
g_frameIndex = 0
g_frames.clear()
g_stream = g_audio.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
input_device_index = DEVICE_INDEX,
frames_per_buffer=CHUNK,
stream_callback=callback)
g_stream.start_stream()
btnCtrl()
コールバック関数です。
def callback(in_data, frame_count, time_info, status):
global g_frameIndex, g_stream,g_recording, g_recorded,g_stopReq
g_frames.append(in_data)
g_frameIndex += 1
if g_frameIndex >= MAX_FRAME or g_stopReq == True:
closeAudio()
g_recording = False
g_recorded = True
g_stopReq = False
btnCtrl()
return(None, pyaudio.paAbort)
return (None, pyaudio.paContinue)
コールバック関数の名前は何でもOKです。引数は指定されています。returnがコールバック関数を次にどうするかを指定します。
pyaudio.paAbort:録音を終了 pyaudio.paContinue:録音を継続
Visual Studio Codeでの大きな問題です。コールバック関数内のブレークポイントで停止しません。仕方ないのでprintでデバッグします。効率が悪いです。
録音の停止は,g_stopReqがTrueになるとタイムアウト同様に動作します。
ボタンの有効無効の処理をちゃんとやっています。自分のよくやる方法です。ソースのあちこちでボタンの有効無効をいれるのは効率が悪くミスをしがちです。状態に応じたボタンの処理をまとめたbtnCtrl()を用意します。これを状態がが変わったところに入れるだけなので簡単です。
|