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がいるわけか。
(続く予定)