Nagatochyan
@Nagatochyan (長門 有希)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

python音楽の連続再生と一時停止で生じる問題

解決したいこと

ここに解決したい内容を記載してください。

pythonのtkinterでmp3をランダム再生や連続再生ができるミュージックプレイヤーを作ってるんですが
https://qiita.com/mino211/items/eda5f9981cc32b0f9783
ここに書かれているmutagenを使ってmp3の長さを取得し、その時間分待機して次の曲を再生するという方法を使ってるんですけど、曲を一時停止しても曲の長さ分待ってるのでその時間が終わったら一時停止していても新しい曲に入っちゃいます。これを防ぐためにどうすればいいですか。
解決方法を教えて下さい。

ソースコード

0

1Answer

コンセプト

曲終了イベントを追加しておく => after関数 を使い、一定間隔で曲終了イベントの有無をチェックします。

具体例

・曲終了イベントの追加

EVENT_MUSIC_END = pygame.USEREVENT + 1
pygame.mixer.music.set_endevent(EVENT_MUSIC_END)

こうしておくと、曲が自然終了したタイミングで、イベントキューに EVENT_MUSIC_END タイプのイベントが入ります。

・曲終了イベントの有無のチェック
pygame.event.get()関数を実行すると、イベントキュー内のすべてのイベントを要素とするリストが返ってくるので、
そのリスト中に EVENT_MUSIC_END タイプのイベントがあるかどうかを調べれば、曲が自然終了したかどうかを判別できます。
(なお、pygame.event.get()関数を実行すると、イベントキューは空になります)

def check_music_end():
    # イベントキュー内のイベントを全部取り出す。
    events = (event.type for event in pygame.event.get())
    # 「順番に流す」にチェックが入っている場合は
    # 自然終了イベントが発生したか調べ、発生している場合は次の曲を再生。
    if boolean_vv.get():
        if EVENT_MUSIC_END in events:
            play_music()
    root.after(1000, check_music_end)    

after() は、第一引数に指定したミリ秒後に、第二引数で指定した関数を呼び出すようスケジューリングする関数です。
上記では、処理が終わってから1秒後に、同じ check_music_end() を呼び出すようスケジューリングしています。
一見無限ループのように見えますが、メインスレッドを sleep しているわけではないので、特に処理が重くなることはありません。

・ 「再生中にチェックがオフになる」「チェックが空欄のまま曲が終了して、その後再生ボタンで再度再生開始後チェックが入る」等の細かいパターンも考慮しています。

書き直したのが下記です。(私が見た時点のコミットの内容に対する修正です。最新のコミットは反映してません)

import tkinter as tk
from pypresence import Presence
import random
from tkinter import filedialog
import glob
import os
import threading
import pygame
import time
from mutagen.mp3 import MP3 as mp3
root = tk.Tk()
root.geometry("700x300")
root.title("Stray Music")
iconfile = 'UI/icon.ico'
root.iconbitmap(default=iconfile)
# pygame 最初に初期化しておく
pygame.init()
#Title
nowp=tk.Label(text="Stray Music Ver1.0",fg="black",font=("20"))
nowp.place(x=20, y=20)
#Discordのステータス表示
def richpresence():
    global rpc
    rpc=Presence("1085040142634459216")
    rpc.connect()
    rpc.update(details="Playing stray music",large_image="bg",start=time.time())#小アイコンと何の曲を聞いているかみたいなのを表示できるようにしたい
def whatareyouplaying():
    rpc.update(details="Playing stray music",state=presenceyou,large_image="bg",start=time.time())
#何の曲が流れるとか、順番とか(製作中)
sv = tk.StringVar()
nowpte=tk.Entry(textvariable=sv,fg="#000000")
nowpte.configure(state='disabled')#書き込み禁止
nowpte.place(x=200,y=20,width=300,height=30)
#参照ボタン関数
def select():
    global lst
    global filenum
    global file_path
    idir = 'C:\\'
    file_path = tk.filedialog.askdirectory(initialdir = idir)
    lst=glob.glob(file_path+'\\'+'*.mp3')
    filenum=(sum(os.path.isfile(os.path.join(file_path, name)) for name in os.listdir(file_path)))
    print(filenum)
#順番に流す(継続再生)チェックボタン
boolean_vv = tk.BooleanVar()
playnor = tk.Checkbutton(variable=boolean_vv,text='順番に流す',font=("20"))
playnor.place(x=20, y=70)
#ランダム再生チェックボタン
boolean_v = tk.BooleanVar()
playran=tk.Checkbutton(variable=boolean_v,text="ランダム再生",font=("20"))
playran.place(x=20,y=120)


#######   以降大幅修正      ######
#再生リスト
lst = []

# 曲終了イベントを追加
EVENT_MUSIC_END = pygame.USEREVENT + 1
pygame.mixer.music.set_endevent(EVENT_MUSIC_END)

def play():
    if pygame.mixer.music.get_busy()==True:
        return

    if len(lst)==0:
        #ここも機能してない____________↓
        try:
            pass
            # print("select")
        except NameError:
            pass
            # print("error")
        #___________________________↑
    else:
        play_music()

# 仕様不明のため仮実装
def get_next_tune():
    if len(lst) > 0:
        return lst.pop()

def play_music():
    if boolean_v.get():
        random.shuffle(lst)
    # 次の曲を取得
    tune = get_next_tune()
    if tune is None: return
    pygame.mixer.music.load(tune)
    tune1=tune.replace(file_path,'')
    global presenceyou
    presenceyou=("Now playing: "+(tune1.replace("\\",'')))
    sv.set("Now playing: "+(tune1.replace("\\",'')))
    try:
        whatareyouplaying()
    except Exception as e:
        pass
    pygame.mixer.music.play(1) 

def check_music_end():
    # イベントキュー内のイベントを全部取り出す。
    events = (event.type for event in pygame.event.get())
    # 「順番に流す」にチェックが入っている場合は
    # 自然終了イベントが発生したか調べ、発生している場合は次の曲を再生。
    if boolean_vv.get():
        if EVENT_MUSIC_END in events:
            play_music()
    root.after(1000, check_music_end)    

#音楽一時停止関数
def stop():
    if pygame.mixer.music.get_busy() == True:
        pygame.mixer.music.pause()
    else:
        pygame.mixer.music.unpause()

#音量調節関数
def set_volume1(str1):
    val1=float(str1)/100.0
    if val1 < 0.0: val1 = 0.0
    elif val1 > 100.0:
        val1 =1.0
    pygame.mixer.music.set_volume(val1)

def selectth():
    select()

def playth():
    play()

def stopth():
    stop()


#ファイル参照ボタン
img=tk.PhotoImage(file="UI/docusign.png")
big_img=img.zoom(2,2)
selecmu=tk.Button(image=big_img,command=selectth)
selecmu.place(x=20,y=200)
#再生開始ボタン
imgg=tk.PhotoImage(file="UI/play_icon.png")
small_img = imgg.subsample(1,1)
playbu=tk.Button(image=small_img,command=playth)
playbu.place(x=530,y=10)
#一時停止ボタン
imggg=tk.PhotoImage(file="UI/stop_icon.png")
small_imgg= imggg.subsample(1,1)
stopbu=tk.Button(image=small_imgg,command=stopth)
stopbu.place(x=570,y=200)
#音量調節バー         #左右の端に音量のアイコン付けたい
vol=tk.Label(text="Vol",fg="black",font=("20"))
vol.place(x=150,y=215)
scale1 = tk.Scale( from_=0, to=100, length=200, orient = 'h', command=set_volume1 )
scale1.place(x=200,y=200,width=150)
scale1.set( 100 )
#discord起動してない場合の例外処理
try:
    richpresence()
except Exception as e:
    pass
check_music_end()  # イベント監視ループ開始
root.mainloop()
1Like

Comments

  1. @Nagatochyan

    Questioner

    ありがとうございます。無事治すことができました。

Your answer might help someone💌