0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MP3を5.1chサラウンド(AC-3)に変換するPython GUIアプリを作った

0
Posted at

はじめに

映画やライブ映像の音声をサラウンドスピーカーで楽しみたいのに、手元にあるのは2chステレオのMP3ファイルしかない…というシーンはよくあると思います。

今回は Python + FFmpeg + CustomTkinter を使って、MP3音源を 5.1chサラウンド(AC-3 / Dolby Digital互換) に変換するGUIアプリを作りました。

センター・リア・LFE(サブウーファー)のミックス比率をスライダーで自由調整でき、映画・音楽・ボイス向けの3種プリセットも搭載しています。


作ったもの

Screenshot from 2026-09-01 16-22-29.png

機能一覧

  • 🎛️ 音響パラメータのカスタマイズ(スライダーUI)
    • センター MIX 比率(0.0 〜 2.0×)
    • リア / サラウンド MIX 比率(0.0 〜 2.0×)
    • サラウンド ディレイ(0 〜 100 ms)
    • LFE(サブウーファー)カットオフ周波数(50 〜 200 Hz)& ゲイン
  • ワンクリックプリセット 3種
    • 🎥 映画・標準 (Cinema)
    • 🎵 音楽・ライブ (Music Live)
    • 🗣️ ボイス・対話強調 (Voice Focus)
  • 📂 バッチ変換(複数ファイル・フォルダ一括追加)
  • 🌙 ダーク / ライトモード切替
  • 📋 リアルタイムFFmpegログモニター

5.1chアップミックスの仕組み

ステレオ(L, R)から5.1ch(FL, FR, FC, LFE, SL, SR)を生成するのに、FFmpegの filter_complex を使っています。

[0:a]asplit=5[in_flfr][in_c][in_lfe][in_sl][in_sr];
[in_flfr]pan=stereo|c0=c0|c1=c1[flfr];
[in_c]pan=1c|c0=0.707*c0+0.707*c1,volume={center_gain}[c];
[in_lfe]pan=1c|c0=0.5*c0+0.5*c1,lowpass=f={lfe_cutoff},volume={lfe_gain}[lfe];
[in_sl]pan=1c|c0=0.707*c0-0.707*c1,adelay={delay}|{delay},volume={rear_gain}[sl];
[in_sr]pan=1c|c0=-0.707*c0+0.707*c1,adelay={delay}|{delay},volume={rear_gain}[sr];
[flfr][c][lfe][sl][sr]amerge=inputs=5,pan=5.1|FL=c0|FR=c1|FC=c2|LFE=c3|BL=c4|BR=c5[out]

各チャンネルの計算式:

チャンネル 計算式 役割
FL / FR L, R そのまま フロント左右
FC(センター) (L + R) × 0.707 × center_gain ボーカル・ダイアログ
LFE(サブウーファー) lowpass((L + R) × 0.5) 重低音
SL(サラウンド左) (L - R) × 0.707 × rear_gain + delay 環境音・効果音
SR(サラウンド右) (R - L) × 0.707 × rear_gain + delay 環境音・効果音

位相差(L - R)をサラウンドチャンネルに使うことで、ステレオ感を持つ音がリアスピーカーから出力されます。


コードのポイント

スレッドセーフなUI更新

Tkinter はシングルスレッドなため、バックグラウンドで動くFFmpegワーカーから直接UIを操作するとクラッシュします。queue.Queue + after() ポーリングパターンで解決しています。

# gui_app.py
def _process_queue(self):
    """Main thread polls queue every 50ms for safe UI updates."""
    try:
        while True:
            msg_type, data = self.msg_queue.get_nowait()
            if msg_type == "log":
                self.append_log(data)
            elif msg_type == "progress":
                self.progress_bar.set(data)
            # ...
    except queue.Empty:
        pass
    finally:
        self.after(50, self._process_queue)  # 再スケジュール

クロージャのバグ対策(Pythonの遅延バインディング)

for ループ内でクロージャを定義すると、ループ変数 idx が常に最終値になってしまう Python の有名な罠があります。デフォルト引数でキャプチャすることで回避しています。

# ❌ バグあり: idx は常にループの最後の値になる
def _on_progress(p):
    overall_p = ((idx - 1) + p) / total_files

# ✅ 修正済み: デフォルト引数で現在の値をキャプチャ
def _on_progress(p, _idx=idx, _total=total_files):
    overall_p = ((_idx - 1) + p) / _total

プリセット設計(データクラス)

# presets.py
@dataclass
class AudioPreset:
    name: str
    description: str
    icon: str
    center_gain: float   # 0.0 to 2.0
    rear_gain: float     # 0.0 to 2.0
    rear_delay: int      # ms
    lfe_cutoff: int      # Hz
    lfe_gain: float      # 0.0 to 2.0
    bitrate: str         # '448k' etc.
    sample_rate: int     # 48000 / 44100

インストールと起動

必要なもの

  • Python 3.8+
  • FFmpeg(sudo apt install ffmpeg など)

起動方法

git clone https://github.com/[YourUsername]/mp3-to-ac3-converter
cd mp3-to-ac3-converter
./run.sh   # 自動で venv 作成 & 依存インストール & 起動

まとめ

  • FFmpegの filter_complex を使えばステレオ→5.1chのアップミックスが柔軟に実現できる
  • CustomTkinterでモダンなダークテーマGUIを比較的シンプルに作れる
  • Tkinterのスレッドセーフ問題は queue.Queue + after() パターンが定石

プリセットのパラメータや、対応フォーマット(WAV, FLAC, M4A にも対応)はカスタマイズできるので、用途に合わせてぜひ改造してみてください!


参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?