ゆるエンジニアはいろいろ遊びたい

FAエンジニアが週末にいろいろ遊ぶブログです

Raspberry Pi5でMP3データを再生する

ラズパイ5でpythonスクリプトからMP3データを再生してみました。
BGMの再生に必要なモジュールが入っていなかったので、まずはインストールしていきます。
ターミナルで1行ずつ実行します。

sudo apt update
sudo apt install pipewire-pulse
sudo apt install pulseaudio-utils
sudo apt install mpv
sudo reboot

成功すれば、which pactlを実行するとuser/bin/pactlと表示されます。
再生デバイスの一覧を確認するためには以下のコードをターミナルに入力します。

mpv --audio-device=help

と入力すると大量のデバイス一覧が表示されますが、pipewire/から始まるものを覚えておきます。
音楽の再生と停止、音量の調整のコードを作りました。

import subprocess
import threading


class MP3Player:
    def __init__(self,
                 device="pipewire/alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150344C36313516-00.analog-stereo",
                 sink_name="alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150344C36313516-00.analog-stereo"):
        self.device = device
        self.sink_name = sink_name
        self.process = None
    # -------------------------
    # 外部音量(PipeWire)変更
    # -------------------------
    def set_volume(self, percent):
        try:
            subprocess.run(
                ["pactl", "set-sink-volume", self.sink_name, f"{percent}%"],
                check=True
            )
        except Exception as e:
            print("音量変更エラー:", e)

    # -------------------------
    # 再生(停止可能)
    # -------------------------
    def play(self, file_path, volume=100):
        # 既存の再生を止める
        self.stop()

        cmd = [
            "mpv",
            "--no-video",
            "--no-terminal",
            "--force-window=no",
            "--idle=no",
            f"--audio-device={self.device}",
            f"--volume={volume}",
            file_path
        ]

        # ★ Popen で起動して self.process に保存
        self.process = subprocess.Popen(cmd)

        # 再生が終わるまで待つ(thread_play の時は別スレッド)
        self.process.wait()
        self.process = None

    # -------------------------
    # スレッドで再生
    # -------------------------
    def thread_play(self, file_path, volume=100):
        th = threading.Thread(
            target=self.play,
            args=(file_path, volume),
            daemon=True
        )
        th.start()
        return th

    # -------------------------
    # 停止
    # -------------------------
    def stop(self):
        """mpv を停止する"""

        if self.process and self.process.poll() is None:
            try:
                self.process.terminate()
            except:
                try:
                    self.process.kill()
                except:
                    pass

        self.process = None


# ---- 使用例 ----
if __name__ == "__main__":
    player = MP3Player()

    player.set_volume(80)
    player.thread_play("bgm/bgm02.mp3", volume=60)
    input("Enter 押下で停止...")
    player.stop()
    print("停止しました!")

簡単に説明します。
deviceが再生デバイスで、mpv --audio-device=helpで表示された再生デバイスを任意で指定します。
play()が通常再生で、thread_play()がスレッドで再生します。通常再生ではpythonスクリプトは再生中に動かないですが、
thread_play()だと並列処理ができるので、BGMを流しながら他の処理をする際はこちらを使います。
play()は最初にstop()を入れているので重複再生できません。2つのデータを同時に再生する場合は、インスタンスを2つ生成して使う感じです。
今回もChatGPIと相談して作ったので、細かい部分は全くわかりません!
まぁ、動けばよかろう。の精神です。(プログラマー失格ですが、プログラマーじゃないのでOK!)
これを使えば、pythonプログラム中で自由に音楽を流せます。