指定範囲を繰り返し再生するプレーヤー【Python】

Python

音声ファイルの指定区間を繰り返し再生する Python のスクリプトについてまとめておきます。
いわゆる AB リピートができる音声プレーヤーです。
以下の環境で動作確認をしています。
環境:
・ Windows パソコン
・ Python 3.X(tkinter、pygame を設定済み)
・ mp3 ファイル
※ Python で tkinter と pygame を使っています。あらかじめ、インストール・設定が必要です。pygame のインストールは下記の関連リンクを参照してみてください。

背景 ~ 指定区間の繰り返し再生をしたい!

いつも、音楽などを mp3 で聴いています。こうした音声データを日ごろ扱っていると、たまに、指定した箇所を繰り返し聞きたくなることがあります。

たとえば、英語のリスニング・シャドーイング、洋楽の歌詞の聞き取り・音取り、音声ファイルの文字起こしなどの場合です。
mp3 ファイルについては、ボイスレコーダや Windows 上でも録音できます。また、ネットからも動画・音声データがダウンロードでき、いくらでも入手できる環境になっています。

こうして入手した mp3 ファイルについて、簡単に/気軽に、聞きたいところが何度でも繰り返し再生できるととても便利です。
ところが、ネット検索をしてみると、対応しているソフトウェア自体が少ないようです。さらに、オープンソースであって、自由に修正、改善していけるものとなると、ほとんど見つからないようです。

そこで、オーソドックスな音声プレーヤーで、時間指定で2か所を設定しておくとその区間を繰り返し再生する Python のスクリプトをまとめておくことにします。
実質的に、オープンソース&フリーの mp3 プレーヤー、ABプレイヤーです。

なお、スクリプトで、音声ファイルを繰り返し再生する部分は  pygame を使っています。pygame では、mp3 形式のみ対応しているようです。
他のファイル形式は動作未確認(動かなければ、ffmpeg などでファイルフォーマット変換)となりますので、ご理解をいただきますようお願いいたします。

設定方法

① パソコンに音声再生用のフォルダ(例: audio_player1″ )を作成してください。
例: C:\user\audio_player1
② ①のフォルダに “audio_player1.py” という名前でテキストファイルを作成し、下記のサンプルスクリプトをコピー&ペーストして保存してください。
③ ①のフォルダに、さらに、”audio1″ という名前でフォルダを作成してください。音声ファイル(mp3)を入れるフォルダです。

プレイヤーの起動方法

④ ③のフォルダ “audio1” に、mp3 ファイルを入れてください。
現在のところ、mp3 形式のみ、動作確認をしています。
⑤ コマンドプロンプトを起動し、②のスクリプトを実行してください。
例: python C:\user\audio_player1\audio_player1.py

使い方

基本操作

・ 基本操作は、画面をみたとおりです。
アプリ画面のリストから mp3 ファイルを選択するか、[play] ボタンをクリックすることで任意の音声ファイルを再生できます。
再生中、冒頭からの秒数が画面に表示されます。この数値の範囲を、画面の A、B の欄に設定することで、AB リピートをすることが可能です。
・ [pause] ボタンで一時停止します。[stop] ボタンで再生を終了します。
・ [prev]、[next] ボタンで、1つ前、1つ後のファイルに進むことができます。
・ volume 部分で、音量調整が可能です。
・ continuous にチェックを入れておくと、[play] ボタンクリック時、フォルダ内の末尾まで連続再生することが可能です。

区間を指定し、繰り返し再生をする方法

・ 区間指定の繰り返し再生をする場合は、まず、A、B とした欄に開始時間、終了時間を入力してください。単位は秒としてください。正常動作のためには、終了時間の数値が開始時間より大きくなるよう設定する必要があります。
・ [AB repeat] ボタンをクリックすると、A、B 欄で指定した時間範囲を繰り返し再生します。

・ [play] ボタンで再生中に、[set A]、[set B] ボタンをクリックすることで、それぞれ、開始時間、終了時間を設定できます。再生中の時間が各 A、B の欄に入力されます。
・ 設定した時間は、[+] ボタン、[-] ボタンをクリックすることで微調整可能です。
・ 再生回数は、repeat の欄で設定してください。[+]、[-] ボタンで、値を増減できます。

スクリプトの説明

・ 冒頭で、使用するライブラリを設定しています。GUI を作成するために tkinter を使います。音声データの再生に pygame を使います。インストールしていない場合は、設定が必要です。
・ 音声再生のため、play0, 1, 2 等の関数を定義しています。ポーズ、ストップのため、pause1、stop1 等の関数を定義しています。pygame を使っています。
・ その他、ボタンクリック時のための関数を定義しています。
・ frame1 = tk1.Tk() 以降で、tkinter を使った、GUI 画面の定義をしています。メイン画面に、各ボタン類を配置しています。
・ tkinter、リストボックス、pygame の基本的な使い方については、下記の関連リンクにまとめています。関心のある方は、参照してみてください。

まとめ

区間指定で繰り返し再生をする Python のスクリプトについてまとめました。
これで音声ファイルの再生についても自由自在です!

関連リンク
・ Pygame をインストールする手順 【Windows 10 &最短】
・ Anaconda 環境で Pygame をインストールする手順【Python】
・ フォルダビューアの作り方 【Python, tkinter】
・ ドレミファソラシドを鳴らすサンプル【Python】
・ ffmpeg のインストール手順 【Windows】
・ 動画ファイルから音声データを一括で引き抜く 【Python & ffmpeg】

サンプルスクリプト


import os 
import glob 
import tkinter as tk1 
import pygame as pg1 

def play0(): 
    global t0 
    global t1 
    global ply1 
    if ply1 < 0: 
        ply1 = 1 
        frame1.after( 1000, after1 ) 
    pg1.mixer.music.play(start=t1) 

def play1(): 
    global t0 
    global t1 
    global n1 
    global rep1 
    global len1 
    if n1 < 0: 
        n1 = 0 
        set_list1() 
    t0   = 0
    t1   = 0 
    rep1 = -1 
    len1 = -1 
    play0() 

def play2_AB(): 
    global t0 
    global t1 
    global t2 
    global n1 
    global rep1 
    global len1 
    if n1 < 0: 
        n1 = 0 
        set_list1() 
    t1   = int( textbox1.get() ) 
    t2   = int( textbox2.get() ) 
    t0 = t1 
    len1 = t2 - t1 
    rep1 = int( textbox3.get() ) 
    play0() 

def pause1():
    global t1 
    global p1 
    if p1 < 0: 
        pg1.mixer.music.pause() 
    else: 
        pg1.mixer.music.unpause() 
    return 

def stop1(): 
    global t1 
    pg1.mixer.music.stop() 
    t1 = -1 

def set_status1(): 
    return 

def load_file1():
    global n1 
    global t1 
    global list1 
    file1 = list1[n1] 
    print( file1 ) 
    pg1.mixer.music.load( file1 ) 

def click_list1( event ): 
    global n1 
    n1 = listbox1.curselection()[0] 
    set_list1() 
    play1() 

def set_list1(): 
    global n1 
    label1["text"] = listbox1.get( n1 ) 
    listbox1.select_clear(0, "end") 
    listbox1.select_set( n1 ) 
    listbox1.selection_anchor( n1 ) 
    load_file1() 

def prev1(): 
    global n1 
    global list1 
    n1 = n1 - 1 
    if n1 < 0: n1 = len( list1 )-1 set_list1() play1() def next1(): global n1 global list1 n1 = n1 + 1 if n1 > len( list1 )-1: 
        n1 = 0 
    set_list1() 
    play1() 

def set_volume1( str1 ): 
    val1 = float( str1 )/100.0 
    if val1 < 0.0: val1 = 0.0 elif val1 > 100.0: 
        val1 = 1.0
    pg1.mixer.music.set_volume( val1 ) 

def set_text1( str1 ): 
    global t1 
    t1 = int( str1 ) 
    textbox1.delete( 0, tk1.END ) 
    textbox1.insert( tk1.END, str( str1 ) )

def set_text2( str1 ): 
    global t2 
    t2 = int( str1 ) 
    textbox2.delete( 0, tk1.END ) 
    textbox2.insert( tk1.END, str( str1 ) ) 

def set_text3( str1 ): 
    textbox3.delete( 0, tk1.END ) 
    textbox3.insert( tk1.END, str( str1 ) ) 

def after1(): 
    global t0 
    global t1 
    global p1 
    global n1 
    global ply1 
    global len1 
    global rep1 
    global list1 
    label3["text"] = str( t0 ) 
    label4["text"] = str( rep1 ) 
    status1 = "" 
    for event1 in pg1.event.get(): 
        if event1.type == AUDIO_ENDED1: 
            if rep1 > 0: 
                rep1 = rep1 - 1 
                status1 = "next" 
            else: 
                status1 = "end2" 
    if len1 > 0 and t0 > t2-1 and status1 == "": 
        status1 = "end1" 
    if status1 == "end1": 
        if ply1 > 0: 
            pg1.mixer.music.stop() 
            frame1.after( 1000, after1 ) 
        ply1 = -1 
    elif status1 == "end2": 
        t0 = -1 
        rep1 = -1 
        ply1 = -1 
        if chkv1.get() and (n1 < len( list1 )-1): 
            next1() 
    elif status1 == "next": 
        t0 = t1         
        if rep1 > 0: 
            play0() 
    else: 
        if t0 > -1: 
            if p1 < 0: t0 = t0 + 1 frame1.after( 1000, after1 ) def set_A1(): global t0 if t0 >= 0: 
        textbox1.delete( 0, tk1.END ) 
        textbox1.insert( tk1.END, str(t0) ) 

def set_B1(): 
    global t0 
    if t0 >= 0: 
        textbox2.delete( 0, tk1.END ) 
        textbox2.insert( tk1.END, str(t0) ) 

def minus1(): 
    global t1 
    global t2 
    global len1 
    t1   = int( textbox1.get() ) - 1 
    if t1 < 0: 
        t1 = 0 
    len1 = t2 - t1 
    set_text1( t1 )

def plus1(): 
    global t1 
    global t2 
    global len1 
    t1   = int( textbox1.get() ) + 1 
    if t1 < 0: 
        t1 = 0 
    len1 = t2 - t1 
    set_text1( t1 )

def minus2(): 
    global t1 
    global t2 
    global len1 
    t2   = int( textbox2.get() ) - 1
    len1 = t2 - t1 
    set_text2( t2 )

def plus2(): 
    global t1 
    global t2 
    global len1 
    t2   = int( textbox2.get() ) + 1
    len1 = t2 - t1 
    set_text2( t2 )

def minus3(): 
    global rep1 
    rep1   = int( textbox3.get() ) - 1
    if rep1 < 1: 
        rep1 = 1 
    set_text3( rep1 )

def plus3(): 
    global rep1 
    rep1   = int( textbox3.get() ) + 1
    set_text3( rep1 )

path1 = os.path.dirname(__file__) + "\\audio1\\"    
list1 = glob.glob( path1 + "*.*" ) 

n1 = -1       # file number 
t0 = -1       # timer 
t1 = -1       # start 
t2 = -1       # stop 
p1 = -1       # pause 
len1 = -1     # length, AB repeat 
rep1 = -1     # repeat 
ply1 = -1     # play or not 

pg1.init() 
pg1.mixer.init() 
AUDIO_ENDED1 = pg1.USEREVENT + 1 
pg1.mixer.music.set_endevent( AUDIO_ENDED1 ) 

frame1 = tk1.Tk()
frame1.title( 'audio player v0.1' ) 
frame1.geometry( "520x300+100+100" ) 

btn1 = tk1.Button( frame1, text="play", command=play1 ) 
btn1.place( x=10, y=10, width=60 ) 

btn2 = tk1.Button( frame1, text="pause", command=pause1 ) 
btn2.place( x=70, y=10, width=60 ) 

btn3 = tk1.Button( frame1, text="stop", command=stop1 ) 
btn3.place( x=130, y=10, width=60 ) 

btn4 = tk1.Button( frame1, text="prev", command=prev1 ) 
btn4.place( x=200, y=10, width=60 ) 

btn5 = tk1.Button( frame1, text="next", command=next1 ) 
btn5.place( x=260, y=10, width=60 ) 

btn6 = tk1.Button( frame1, text="AB repeat", command=play2_AB ) 
btn6.place( x=360, y=80, width=60 ) 

btn7 = tk1.Button( frame1, text="set A", command=set_A1 ) 
btn7.place( x=360, y=130, width=60, height=20 ) 

btn8 = tk1.Button( frame1, text="set B", command=set_B1 ) 
btn8.place( x=360, y=180, width=60, height=20 ) 

btn9 = tk1.Button( frame1, text="-", command=minus1 ) 
btn9.place( x=430, y=130, width=20, height=20 ) 

btn10 = tk1.Button( frame1, text="+", command=plus1 ) 
btn10.place( x=480, y=130, width=20, height=20 ) 

btn11 = tk1.Button( frame1, text="-", command=minus2 ) 
btn11.place( x=430, y=180, width=20, height=20 ) 

btn12 = tk1.Button( frame1, text="+", command=plus2 ) 
btn12.place( x=480, y=180, width=20, height=20 ) 

btn13 = tk1.Button( frame1, text="-", command=minus3 ) 
btn13.place( x=430, y=230, width=20, height=20 ) 

btn14 = tk1.Button( frame1, text="+", command=plus3 ) 
btn14.place( x=480, y=230, width=20, height=20 ) 

scale1 = tk1.Scale(frame1, from_=0, to=100, length=200, orient = 'h', command=set_volume1 )
scale1.place( x=410, y=5, width=100 ) 
scale1.set( 100 ) 

chkv1 = tk1.BooleanVar() 
chk1 = tk1.Checkbutton( frame1, text="continuous", command=set_status1, variable=chkv1 ) 
chk1.place( x=350, y=45, width=90 ) 
chk1.select() 

label2 = tk1.Label( frame1, anchor="w", text="volume" ) 
label2.place( x=350, y=10, width=50 ) 

label3 = tk1.Label( frame1, bg="white", anchor="w" ) 
label3.place( x=430, y=80, width=30 ) 

label4 = tk1.Label( frame1, bg="white", anchor="w" ) 
label4.place( x=470, y=80, width=30 ) 

label5 = tk1.Label( frame1, anchor="w", text="A: start" ) 
label5.place( x=350, y=110, width=60 ) 

label6 = tk1.Label( frame1, anchor="w", text="B: stop" ) 
label6.place( x=350, y=160, width=60 ) 

label7 = tk1.Label( frame1, anchor="w", text="repeat" ) 
label7.place( x=350, y=210, width=60 ) 

textbox1 = tk1.Entry( frame1 ) 
textbox1.place( x=450, y=130, width=30, height=20 ) 
set_text1( 10 ) 

textbox2 = tk1.Entry( frame1 ) 
textbox2.place( x=450, y=180, width=30, height=20 ) 
set_text2( 15 ) 

textbox3 = tk1.Entry( frame1 ) 
textbox3.place( x=450, y=230, width=30, height=20 ) 
set_text3( 10 ) 

label1 = tk1.Label( frame1, bg="white", anchor="w" ) 
label1.place( x=10, y=50, width=310 ) 

listbox1 = tk1.Listbox( frame1, selectmode='single' )
listbox1.place( x=10, y=80, width=305, height=200 ) 

scrollbar1 = tk1.Scrollbar(frame1, orient='v', command=listbox1.yview)
scrollbar1.place( x=305, y=80, height=200 ) 

listbox1.configure( yscrollcommand=scrollbar1.set ) 

for list2 in sorted( list1 ): 
    list3 = os.path.split( list2 )[1] 
    listbox1.insert('end', list3 ) 

listbox1.bind('<<ListboxSelect>>', click_list1 )
frame1.mainloop()
タイトルとURLをコピーしました