Python

Pythonで作る日本語対応オーディオ録音アプリ「AudioRecorderApp」:マイク選択・リアルタイム波形・音量メーター付き

tyamada

はじめに:Pythonで簡単に作れる高機能オーディオ録音アプリ

プログラミング初心者から中級者まで、Pythonで作れるアプリの中でも特に面白いのが「オーディオ録音ツール」です。
今回紹介する AudioRecorderApp は、Pythonの tkinter を用いたGUIに、 PyAudio での録音、 matplotlib による波形表示を組み合わせたアプリです。

このアプリの特徴は以下の通りです:

  • 日本語対応のGUI
  • マイクデバイスの選択可能
  • リアルタイムで波形表示
  • 音量バー(RMS)で録音レベルを確認
  • 録音の開始・停止・WAV保存が簡単操作

「Pythonだけでこんな本格的な録音アプリが作れるの?」と思われる方もいるかもしれません。
しかし、実際には PyAudiomatplotlib、そして tkinter の組み合わせだけで、簡単に作れてしまうのです。

ソースコードを全て表示
import tkinter as tk
from tkinter import ttk, filedialog
import threading
import pyaudio
import wave
import numpy as np
import matplotlib
matplotlib.rcParams['font.family'] = 'Yu Gothic'  # ← 日本語フォント指定
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import time

# ==============================
# EnhancedAudioApp (日本語 UI + マイク選択 + 文字化け修正版)
# ==============================
class EnhancedAudioApp:

    def __init__(self, root):
        self.root = root
        self.root.title("Enhanced Audio Recorder(拡張版)")

        # PyAudio 初期化
        self.p = pyaudio.PyAudio()

        # 録音設定
        self.channels = 1
        self.rate = 44100
        self.chunk = 1024

        # 状態管理
        self.recording = False
        self.frames = []
        self.start_time = 0

        # UI作成
        self.create_widgets()

    # ==========================
    # 🔊 マイクデバイス一覧取得
    # ==========================
    def get_input_devices(self):
        device_list = []
        for i in range(self.p.get_device_count()):
            info = self.p.get_device_info_by_index(i)

            # 入力デバイスのみ
            if info["maxInputChannels"] > 0:
                # name を str に変換して文字化け回避
                name = str(info["name"])
                device_list.append((i, name))
        return device_list

    # ==========================
    # 🎨 UI 構築
    # ==========================
    def create_widgets(self):

        # ─────────────────────────
        # マイクデバイス選択
        # ─────────────────────────
        device_frame = ttk.LabelFrame(self.root, text="マイクデバイス選択")
        device_frame.pack(fill="x", padx=10, pady=5)

        ttk.Label(device_frame, text="入力デバイス: ").pack(side="left")

        self.device_list = self.get_input_devices()
        device_names = [name for idx, name in self.device_list]

        self.device_var = tk.StringVar()
        self.device_box = ttk.Combobox(
            device_frame,
            textvariable=self.device_var,
            values=device_names,
            state="readonly",
            width=50
        )
        self.device_box.pack(side="left", padx=5)

        if device_names:
            self.device_box.current(0)

        # ─────────────────────────
        # 録音ボタン類
        # ─────────────────────────
        btn_frame = tk.Frame(self.root)
        btn_frame.pack(pady=10)

        ttk.Button(btn_frame, text="● 録音開始", command=self.start_recording).grid(row=0, column=0, padx=10)
        ttk.Button(btn_frame, text="■ 停止", command=self.stop_recording).grid(row=0, column=1, padx=10)
        ttk.Button(btn_frame, text="💾 保存", command=self.save_audio).grid(row=0, column=2, padx=10)

        # ─────────────────────────
        # 録音時間
        # ─────────────────────────
        self.time_label = ttk.Label(self.root, text="録音時間: 0.0 秒", font=("Yu Gothic", 12))
        self.time_label.pack()

        # ─────────────────────────
        # 音量バー
        # ─────────────────────────
        volume_frame = ttk.LabelFrame(self.root, text="音量レベル")
        volume_frame.pack(fill="x", padx=10, pady=5)

        self.volume_bar = ttk.Progressbar(volume_frame, orient="horizontal", mode="determinate", length=300)
        self.volume_bar.pack(padx=10, pady=5)

        # ─────────────────────────
        # 波形表示(matplotlib)
        # ─────────────────────────
        fig = plt.Figure(figsize=(6, 3), dpi=100)
        self.ax = fig.add_subplot(111)
        self.ax.set_ylim([-1, 1])
        self.ax.set_title("リアルタイム波形(日本語対応)")
        self.line, = self.ax.plot([], [])

        self.canvas = FigureCanvasTkAgg(fig, master=self.root)
        self.canvas.get_tk_widget().pack()

    # ==========================
    # 🎙 録音開始
    # ==========================
    def start_recording(self):
        if self.recording:
            return

        self.recording = True
        self.frames = []
        self.start_time = time.time()

        # 選択されたデバイス index 取得
        selected_name = self.device_var.get()
        device_index = next(idx for idx, nm in self.device_list if nm == selected_name)

        # PyAudio ストリーム開始
        self.stream = self.p.open(format=pyaudio.paInt16,
                                  channels=self.channels,
                                  rate=self.rate,
                                  input=True,
                                  frames_per_buffer=self.chunk,
                                  input_device_index=device_index)

        threading.Thread(target=self.record).start()
        self.update_time()

    # ==========================
    # ⏱ 録音時間更新
    # ==========================
    def update_time(self):
        if self.recording:
            elapsed = time.time() - self.start_time
            self.time_label.config(text=f"録音時間: {elapsed:.1f} 秒")
            self.root.after(100, self.update_time)

    # ==========================
    # 📈 リアルタイム録音処理
    # ==========================
    def record(self):

        while self.recording:
            data = self.stream.read(self.chunk)
            self.frames.append(data)

            audio_np = np.frombuffer(data, dtype=np.int16).astype(np.float32) / 32768.0

            # ノイズゲート(簡易)
            audio_np[audio_np < 0.01] = 0

            # 音量バー更新
            volume = min(int(np.max(np.abs(audio_np)) * 100), 100)
            self.volume_bar["value"] = volume

            # 波形更新
            self.line.set_data(range(len(audio_np)), audio_np)
            self.ax.set_xlim([0, len(audio_np)])
            self.canvas.draw()

    # ==========================
    # ■ 録音停止
    # ==========================
    def stop_recording(self):
        if not self.recording:
            return

        self.recording = False
        self.stream.stop_stream()
        self.stream.close()

    # ==========================
    # 💾 WAV 保存
    # ==========================
    def save_audio(self):
        if not self.frames:
            return

        file_path = filedialog.asksaveasfilename(
            defaultextension=".wav",
            filetypes=[("WAVファイル", "*.wav")]
        )

        if not file_path:
            return

        wf = wave.open(file_path, "wb")
        wf.setnchannels(self.channels)
        wf.setsampwidth(self.p.get_sample_size(pyaudio.paInt16))
        wf.setframerate(self.rate)
        wf.writeframes(b"".join(self.frames))
        wf.close()

        print("保存しました:", file_path)


# ==============================
# アプリ起動
# ==============================
if __name__ == "__main__":
    root = tk.Tk()
    app = EnhancedAudioApp(root)
    root.mainloop()

AudioRecorderApp の機能を詳しく紹介

マイクデバイスの選択

AudioRecorderAppはPCに接続されているマイクデバイスを自動検出し、GUI上で選択可能です。
複数の入力デバイスがある場合でも、プルダウンから選ぶだけで録音が可能になります。

def get_input_devices(self):
    device_list = []
    for i in range(self.p.get_device_count()):
        info = self.p.get_device_info_by_index(i)
        if info["maxInputChannels"] > 0:
            name = str(info["name"])
            device_list.append((i, name))
    return device_list

この関数では、 PyAudio で取得したデバイス情報の中から入力チャンネルがあるものだけを抽出しています。
文字化けを避けるため、デバイス名を str に変換してから表示しています。


録音開始・停止・保存

録音ボタンを押すと、PyAudioのストリームが開始されます。別スレッドで録音処理を行うため、GUIはフリーズせずに操作可能です。

def start_recording(self):
    self.recording = True
    self.frames = []
    self.start_time = time.time()
    selected_name = self.device_var.get()
    device_index = next(idx for idx, nm in self.device_list if nm == selected_name)
    self.stream = self.p.open(format=pyaudio.paInt16,
                              channels=self.channels,
                              rate=self.rate,
                              input=True,
                              frames_per_buffer=self.chunk,
                              input_device_index=device_index)
    threading.Thread(target=self.record).start()
    self.update_time()

録音停止はシンプルで、ストリームを停止して閉じるだけです。

def stop_recording(self):
    self.recording = False
    self.stream.stop_stream()
    self.stream.close()

保存はWAV形式で、wave モジュールを使い簡単にファイル化できます。

def save_audio(self):
    if not self.frames:
        return
    file_path = filedialog.asksaveasfilename(defaultextension=".wav",
                                             filetypes=[("WAVファイル", "*.wav")])
    wf = wave.open(file_path, "wb")
    wf.setnchannels(self.channels)
    wf.setsampwidth(self.p.get_sample_size(pyaudio.paInt16))
    wf.setframerate(self.rate)
    wf.writeframes(b"".join(self.frames))
    wf.close()

リアルタイム波形と音量バー

録音中は matplotlib のグラフに波形を描画します。
さらに簡易ノイズゲート処理を行い、音量バーにRMSレベルを反映させることで、録音状態を一目で確認できます。

def record(self):
    while self.recording:
        data = self.stream.read(self.chunk)
        self.frames.append(data)
        audio_np = np.frombuffer(data, dtype=np.int16).astype(np.float32) / 32768.0
        audio_np[audio_np < 0.01] = 0  # ノイズゲート
        volume = min(int(np.max(np.abs(audio_np)) * 100), 100)
        self.volume_bar["value"] = volume
        self.line.set_data(range(len(audio_np)), audio_np)
        self.ax.set_xlim([0, len(audio_np)])
        self.canvas.draw()

このように、リアルタイムで波形が更新されるため、録音中の状態を視覚的に把握できます。
日本語フォント設定をしているので、波形グラフのタイトルも文字化けせず表示されます。


録音時間の表示

録音中はラベルで経過時間を更新しています。
after() メソッドを使うことで、GUIのメインループをブロックせずに時間更新が可能です。

def update_time(self):
    if self.recording:
        elapsed = time.time() - self.start_time
        self.time_label.config(text=f"録音時間: {elapsed:.1f} 秒")
        self.root.after(100, self.update_time)

GUI構成

全体のUIは以下のような構成です:

  • マイクデバイス選択プルダウン
  • 録音開始・停止・保存ボタン
  • 録音時間表示ラベル
  • 音量バー(RMS)
  • 波形表示(matplotlib)

ttk を使っているため、WindowsでもMacでも統一感のある見た目になります。


コードのポイント解説

  1. 非同期録音処理
    threading.Thread(target=self.record).start() により、録音処理を別スレッドで動かすことで、GUIがフリーズせずに波形やボタン操作が可能になっています。
  2. numpyによるデータ変換
    録音データを np.frombufferint16 → float32 に変換することで、波形描画や音量計算を簡単に行えます。
  3. 簡易ノイズゲート
    audio_np[audio_np < 0.01] = 0 という1行で小さい音を無視する処理を行い、波形や音量バーを安定させています。
  4. matplotlibとtkinterの連携
    FigureCanvasTkAgg を使うことで、GUI上にリアルタイム波形を描画できます。
    line.set_datacanvas.draw() の組み合わせで、フレームごとに波形を更新します。
  5. 文字化け対策
    matplotlib.rcParams['font.family'] = 'Yu Gothic' により、日本語のタイトルやラベルを正しく表示できます。

まとめ:Pythonだけで高機能オーディオアプリを作れる

AudioRecorderAppは、Pythonだけで簡単に作れる高機能オーディオ録音アプリです。

  • マイク選択対応
  • リアルタイム波形表示
  • 音量メーター付き
  • 録音の保存も簡単

録音アプリを作りたい人はもちろん、PythonでGUIアプリの開発に挑戦したい人にも最適です。


さらに快適に使うにはPC環境が大事

リアルタイム波形更新や多チャンネル録音など、高速な処理を安定して行うにはPCの性能が重要です。
もしこれからPythonでアプリ開発を本格的に始めるなら、メモリとCPUが十分なノートPCやデスクトップPC を用意すると、録音や波形描画もスムーズになります。

  • Core i5 以上
  • メモリ 16GB 推奨
  • SSD 搭載

このアプリの体験を最大限活かすなら、ぜひ快適なPC環境で試してみてください。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT ME
tyamada
tyamada
普通の会社員(平)
記事URLをコピーしました