ミノムシの日記

気まぐれでブログ投稿してます。アニメ見たり音楽聞くのが好きです

for文との格闘

タイトルの箇所
戻る(一つ前の曲を再生する)


えー、僕は最近自作音楽プレイヤーを作ってます

音楽プレイヤーと言ってもとても単純なやつです

コマンドライン引数からファイルパス取得

(①で取得したパスがファイルだった場合)

②そのまま再生

(①で取得したパスがディレクトリだった場合)

②glob.glob()でその中身を取得

③②の戻り値をfor文で回して順に再生


……というようなプログラムです

しかし、これだけではホントに単純過ぎるのでせめて「曲をスキップ・戻る、ランダム再生」くらいの機能は付けようと思いました

曲をスキップ

これは簡単にできました

#coding: utf-8
import pygame
import os
from mutagen.mp3 import MP3
from timeout_decorator import timeout, TimeoutError

def main(argv):
    if os.path.isfile(argv[1]):
        play(argv[1])
    else:
        play(glob.glob(os.path.join(*[argv[1], '*'])))

def play(path):
    for f in path:
        pygame.mixer.init()
        pygame.mixer.music.load(f)
        length = MP3(f).info.length
        pygame.mixer.music.play()
        result settings(length)
        if result == None:
            pygame.mixer.music.stop()
            continue

def settings(sec):
    @timeout(sec)
    def option():
        while True:
            opt = sys.stdin.read(1)
            if opt == 'n':
                return
    try:
        return option()
    except TimeoutError:
        return

if __name__ == '__main__':
    main(sys.argv[1])

音声を再生したあと、setting関数を呼び出します。
そしてoption関数のタイムアウト時間を音源の長さと同じに設定して、option関数を呼び出します

nが入力されたら現在の曲をスキップして次の曲を流します

音源が再生し終わった(つまりoption関数がタイムアウトした)らまた次の音声を再生します。

戻る(一つ前の曲を再生する)

これにつまずきました

だってfor文中に要素の取り出しを一個前に戻すなんてコード知りませんし調べても出てこないんですもん

諦めそうになった

かれこれ30分この問題と格闘した結果……

#coding: utf-8
import pygame
import os
from mutagen.mp3 import MP3
from timeout_decorator import timeout, TimeoutError


def main(argv):
    if os.path.isfile(argv[1]):
        play(argv[1])
    else:
        play(glob.glob(os.path.join(*[argv[1], '*'])))

def play(path, num):
    for n, f in enumerate(path):
        if n<num:
            continue
        pygame.mixer.init()
        pygame.mixer.music.load(f)
        length = MP3(f).info.length
        pygame.mixer.music.play()
        result = settings(length)
        if result == None:
            pygame.mixer.music.stop()
        elif result == 'back':
            pygame.mixer.music.stop()
            play(path, n-1)

def settings(sec):
    @timeout(sec)
    def option():
        opt = sys.stdin.read(1)
        if opt == 'n':
            return
        elif opt == 'b':
            return 'back'
    try:
        return option()
    except TimeoutError:
        return
if __name__ == '__main__':
    main(sys.argv[1], 0)

play関数の引数を一つ増やしました

こうすることでインデックスがnum未満のやつはスキップさせることができました(最初はnumの値が0なので何もスキップしない)

これを早く思いつかなかった自分を殴りたい

ランダム再生

#coding: utf-8
import pygame
import os
import random
from mutagen.mp3 import MP3
from timeout_decorator import timeout, TimeoutError

def main(argv):
    if os.path.isfile(argv[1]):
        play(argv[1])
    else:
        play(random.shuffle(glob.glob(os.path.join(*[argv[1], '*']))))

def play(path, num):
    for n, f in enumerate(path):
        if n<num:
            continue
        pygame.mixer.init()
        pygame.mixer.music.load(f)
        length = MP3(f).info.length
        pygame.mixer.music.play()
        result = settings(length)
        if result == None:
            pygame.mixer.music.stop()
        elif result == 'back':
            pygame.mixer.music.stop()
            play(path, n-1)

def settings(sec):
    @timeout(sec)
    def option():
        opt = sys.stdin.read(1)
        if opt == 'n':
            return
        elif opt == 'b':
            return 'back'
    try:
        return option()
    except TimeoutError:
        return
if __name__ == '__main__':
    main(sys.argv[1], 0)

play関数に渡すリストをシャッフルしてみました

最初はrandom.randintでインデックス番号生成しようかなぁなんて考えてたのですが、調べてる途中random.shuffle関数様と出会いました

このお方のおかげでランダム再生の実装が簡単に終わった

おまけ

「いちいちn押してEnter押すの面倒くさいなー」と思ってEnter押さなくても勝手に標準入力受け取ってくれる魔法のモジュールを探していました

そしたら見つかりました、「termios」というものを!!

#coding: utf-8
import pygame
import os
import random
import termios
from mutagen.mp3 import MP3
from timeout_decorator import timeout, TimeoutError

def main(argv):
    if os.path.isfile(argv[1]):
        play(argv[1])
    else:
        play(random.shuffle(glob.glob(os.path.join(*[argv[1], '*']))))

def play(path, num):
    for n, f in enumerate(path):
        if n<num:
            continue
        pygame.mixer.init()
        pygame.mixer.music.load(f)
        length = MP3(f).info.length
        pygame.mixer.music.play()
        result = settings(length)
        if result == None:
            pygame.mixer.music.stop()
        elif result == 'back':
            pygame.mixer.music.stop()
            play(path, n-1)

def settings(sec):
    @timeout(sec)
    def option():
        fd = sys.stdin.fileno()
        old = termios.tcgetattr(fd)
        new = termios.tcgetattr(fd)
        new[3] &= ~termios.ICANON
        new[3] &= ~termios.ECHO
        try:
            termios.tcsetattr(fd, termios.TCSANOW, new)
            opt = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSANOW, old)
        if opt == 'n':
            return
        elif opt == 'b':
            return 'back'
    try:
        return option()
    except TimeoutError:
        return
if __name__ == '__main__':
    main(sys.argv[1], 0)

どうやらtermiosを使って設定を弄るようです
入力も出力されないっぽい
これはいいねぇ

参考:
https://qiita.com/tortuepin/items/9ede6ca603ddc74f91ba

あと追加したい機能

  • プレイリスト表示(簡単そう)
  • ループ再生
  • 一時停止

ただソースコード置くだけみたいな記事になってしまってすみません