【python, pyaudio】録音アプリをつくってPC内音声を録音する

PC内の音声を録音する手軽な方法が見つからなかったので、勉強もかねてpythonでアプリを作りました。録画ソフトで画面を動画ファイルとして保存して、それを音声ファイルに変換とかも手段かもしれませんが、それだと面倒だったので、、

普通に録音するだけならマイクを指定すればよくて、PC内音声を録音する際にはステレオミキサーを有効化して、マイクとしてステレオミキサーを指定すればよいようです。

コード

まずはコードになります。

pythonのpyaudioとtkinterを用いています。実行すると以下の画像のようなアプリが立ち上がります。Browseで保存する場所を指定できます。

StartRecordingで録音開始、StopRecordingで録音終了です。

OpenSaveFolderで保存先のファイルを開けるようにしてあります。

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
import pyaudio
import wave
import threading
import os

class AudioRecorderApp:
    def __init__(self, master):
        self.master = master
        self.master.title("Audio Recorder")

        self.is_recording = False
        # ファイル名とフォルダ名
        self.filename = tk.StringVar()
        self.filename.set("recorded_audio.mp3")
        self.save_path = tk.StringVar()
        self.save_path.set("保存したいフォルダを指定")
        # 配置
        self.frame1 = ttk.Frame(self.master, padding=10)
        self.frame1.grid(row=0, column=0, sticky=tk.W)
        label1 = ttk.Label(self.frame1, text="File Name:")
        label1.pack(side=tk.LEFT)
        self.filename_entry = ttk.Entry(self.frame1, textvariable=self.filename, width=30)
        self.filename_entry.pack(side=tk.LEFT)

        self.frame2 = ttk.Frame(self.master, padding=10)
        self.frame2.grid(row=1, column=0, sticky=tk.W)
        label2 = ttk.Label(self.frame2, text="Save Path:")
        label2.pack(side=tk.LEFT)
        self.save_path_entry = ttk.Entry(self.frame2, textvariable=self.save_path, width=30)
        self.save_path_entry.pack(side=tk.LEFT)
        self.browse_button = ttk.Button(self.frame2, text="Browse", command=self.browse_save_path)
        self.browse_button.pack(side=tk.LEFT)

        self.frame3 = ttk.Frame(self.master, padding=10)
        self.frame3.grid(row=2, column=0, sticky=tk.W)
        self.start_button = ttk.Button(self.frame3, text="Start Recording", command=self.start_recording)
        self.start_button.pack(side=tk.LEFT)
        self.stop_button = ttk.Button(self.frame3, text="Stop Recording", command=self.stop_recording)
        self.stop_button.pack(side=tk.LEFT)
        self.stop_button['state'] = 'disabled'
        self.open_folder_button = ttk.Button(self.master, text="Open Save Folder", command=self.open_save_folder)
        self.open_folder_button.grid(row=2, column=0, sticky=tk.E, padx=10)

        self.master.protocol("WM_DELETE_WINDOW", self.on_closing)

    # 録音開始
    def start_recording(self):
        self.is_recording = True
        self.start_button['state'] = 'disabled'
        self.stop_button['state'] = 'active'

        self.recording_thread = threading.Thread(target=self.record_audio_thread)
        self.recording_thread.start()
    # 録音終了
    def stop_recording(self):
        self.is_recording = False
    # 参照
    def browse_save_path(self):
        save_path = filedialog.askdirectory()
        if save_path:
            self.save_path.set(save_path)
    # フォルダを開く
    def open_save_folder(self):
        save_path = self.save_path.get()
        if os.path.exists(save_path):
            os.startfile(save_path)
    # 録音処理
    def record_audio_thread(self):
        CHUNK = 1024
        FORMAT = pyaudio.paInt16
        CHANNELS = 2
        RATE = 44100
        # マイクの名前を指定
        device_name = "ステレオミキサー (Realtek(R) Audio)"
        index = self.get_device_index(device_name)
        
        mic = pyaudio.PyAudio()
        stream = mic.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK, input_device_index=index)

        frames = []

        while self.is_recording:
            data = stream.read(CHUNK)
            frames.append(data)

        stream.stop_stream()
        stream.close()
        mic.terminate()

        filename = self.filename.get()
        save_path = self.save_path.get()
        file_path = save_path + '/' + filename

        wf = wave.open(file_path, 'wb')
        wf.setnchannels(CHANNELS)
        wf.setsampwidth(mic.get_sample_size(FORMAT))
        wf.setframerate(RATE)
        wf.writeframes(b''.join(frames))
        wf.close()

        self.start_button['state'] = 'active'
        self.stop_button['state'] = 'disabled'
    # マイクのインデックス取得
    def get_device_index(self, device_name):
        p = pyaudio.PyAudio()
        device_index = None
        for i in range(p.get_device_count()):
            dev_info = p.get_device_info_by_index(i)
            if dev_info['name'] == device_name:
                device_index = i
                break
        p.terminate()
        return device_index
    # 終了処理
    def on_closing(self):
        if self.is_recording:
            self.is_recording = False
            self.recording_thread.join()
        self.master.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    app = AudioRecorderApp(root)
    root.mainloop()

設定

PC内音声を取得できるようにするにはステレオミキサーの有効化が必要です。

「コントロールパネル」→「サウンド」の録音タブのステレオミキサーを有効化します

プログラムにおけるdevice_nameをステレオミキサーにします。おそらくパソコンによって名前は違うと思うので、各自変更していただければと思います。

マイクデバイスの名前がわからないという方は以下のコードを実行してみてください

import pyaudio

pa = pyaudio.PyAudio()
for i in range(pa.get_device_count()):
    print(pa.get_device_info_by_index(i))

これを実行したら有効なデバイス一覧が出力されます。

以上で設定は完了です。ステレオミキサーの有効化と、pyaudioでデバイスをステレオミキサーに指定できていれば、PC音声の録音ができるようになるはずです。

ここまでお読みいただきありがとうございました。

何らかの参考になりましたら幸いです。

余談ですが、PC内音声を取得する方法として仮想マイクを利用する方法も試してはいます。しかし、うまくいかず、ステレオミキサーを利用する手法を選択しました。

VB-Audio Virtual Cableをマイクデバイスに指定することはできたのですが、なぜか録音で出力されてくるファイルは、数時間にわたって謎の機械音が流れる呪いのファイルになるという怪奇現象が発生しました。解決法が分かる方いれば教えてほしいです。。

ありがとうございました。

コメント

タイトルとURLをコピーしました