見出し画像

PythonでMP3再生(非同期手法の試行錯誤1)

ここのところ、引っかかっていたVSCodeの環境設定がようやく一段落

しましたので、本題(PythonでMP3再生・非同期処理)の続きをやっていきたいと思います。
 「設定」をした成果が出て、ソースの整形がとても楽になりました。これはうれしい。ただ、例によってまたGitの作法を忘れてしまったりしているので、リハビリも必要。くどいですが、あらためて手順も書いていきます。


作業手順

フォルダ初期設定再び

・pythonプロジェクト用に、新しいフォルダを用意。
△以前はフォルダ内のsetup.cfgというファイルに設定をこのように

[isort]
profile = black

[flake8]
max-line-length = 88
ignore = E203, W503

書いていましたが、今後は、VSCodeの拡張機能の設定(settings.json)に書いたし、max-length は79文字。(フォルダにsetup.cfgを置く必要はない。)
・.gitignoreは次のように書いておく

*
!.gitignore
!*.py
!*/

※なお、VSCのエクスプローラーの設定でExclude GitIgnoreにチェックが入っていると.gitignoreで無視されているファイルは、VSCodeのエクスプローラでも表示されなくなる。

リポジトリを初期化

最初、英語表記になって焦ったけど、VSCodeをいったん閉じて再起動したところ日本語に戻りました。

Gitで忘れていたこと

 過去のコミットをチェックアウトして、ちょっと変更しようとして「detached HEAD」状態になって変更を保存しようとしてエラーにあわてる。過去のチェックアウトを起点にしていじるなら「新しいブランチを作成する」必要があるのを忘れていました。

本題

今の課題

mp3再生まわりのいじり方。特に非同期の手法をどう取り入れるかを理解し、身につけたい。

ここまでの経緯

サプーさんのasync/await の動画を見て、非同期の手法に触れたところまで。実践はこれから。

考察の出発点

CustomTkinter利用

import time

import customtkinter as ctk
from just_playback import Playback


class App(ctk.CTk):

    def __init__(self, title):

        # main window
        super().__init__()
        self.title(title)
        # widget
        ctk.CTkButton(self, text="再生", command=self.playmp3).pack(
            padx=50, pady=10
        )
        # mainloop
        self.mainloop()

    def playmp3(self):
        print("playmp3")
        playback = Playback()
        playback.load_file("test.mp3")
        playback.play()
        time.sleep(3)


App("MyMP3Player")

これは
・ボタンをクリックするとtest2.mp3が冒頭3秒間鳴る。
・time.sleep(3)があるおかげ。無いと鳴らない。

問題点
・再生時間が「決め打ち」になってしまう。(三秒で切れてしまう)
・timeとか使いたくない

timeを使わない方法

書き換えて

import customtkinter as ctk
from just_playback import Playback


class App(ctk.CTk):
    def __init__(self, title): #記述省略

    def playmp3(self):
        print("playmp3")
        playback = Playback()
        playback.load_file("test.mp3")
        playback.play()
        while playback.active:
            pass


App("MyMP3Player")

これなら、曲の最後まで再生される。timeも使っていない。でもUIがフリーズしてしまうから、非同期を使いたい。

非同期を導入してみる

のなかでCopilot先生が提案してくれたコードにこういうのがありました。(若干修正)

import asyncio

from playsound3 import playsound


async def play_sound():
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, playsound, "test.mp3")


async def main():
    await play_sound()
    print("再生終了")


asyncio.run(main())

スマートですね。これ実行すると見事再生してくれます。
sleepもwhileも無いし、気に入りました。じゃあ、これをtkinterと組み合わせるには? これも Copilot先生に相談。こんなコードをくれました。

import asyncio
import tkinter as tk
from threading import Thread

from playsound3 import playsound


# 非同期で音を再生する関数
async def play_sound():
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, playsound, "test.mp3")


# ボタンがクリックされたときに呼ばれる関数
def on_button_click():
    # 非同期関数を別スレッドで実行
    Thread(target=lambda: asyncio.run(play_sound())).start()


# Tkinterのセットアップ
root = tk.Tk()
root.title("MP3 Player")

# 再生ボタンの作成
play_button = tk.Button(root, text="再生", command=on_button_click)
play_button.pack(pady=20)

# メインループの開始
root.mainloop()

ちなみに、

# ボタンがクリックされたときに呼ばれる関数
def on_button_click():
    # Thread(target=lambda: asyncio.run(play_sound())).start()
    asyncio.run(play_sound())

こう書いても「再生」はされました。但し、再生が終わるまで、GUIが完全に固まってしまいました。なるほどね、だからThreadがいるわけか。

(続く予定)


いいなと思ったら応援しよう!