音源再生ライブラリ書き換え(2)
奇妙な「間延び」
の問題が解決したので、あらためてライブラリファイルを書き換えて行きます。とりあえず、sounddevice のplay wait stopといったメソッドを使って再生だけ試します。
from abc import ABC, abstractmethod
from threading import Thread
import customtkinter as ctk
import numpy as np
import sounddevice as sd
from pydub import AudioSegment
class CustomPlayer(ABC):
@abstractmethod
def play(self):
pass
class MyPlayer2(CustomPlayer):
def __init__(self):
super().__init__()
self.filename = None
self.audio = None
self.audio_data = None # オーディオデータ
self.sr = None # サンプルレート
self.is_playing = False
def set_mp3file(self, filename):
self.filename = filename
self.audio = AudioSegment.from_mp3(filename)
self.audio_data = np.array(self.audio.get_array_of_samples())
if self.audio.channels == 2:
self.audio_data = self.audio_data.reshape((-1, 2))
self.sr = self.audio.frame_rate
def play_audio(self):
if self.filename is not None:
if not self.is_playing:
self.is_playing = True
sd.play(self.audio_data, samplerate=self.sr)
sd.wait()
self.is_playing = False
# 抽象メソッドを実装
def play(self):
if not self.is_playing:
Thread(target=self.play_audio).start()
def stop(self):
# self.playback.stop()
if self.is_playing:
sd.stop()
if __name__ == "__main__":
class App(ctk.CTk):
def __init__(self, title):
# main window
super().__init__()
self.title(title)
# player
self.player = MyPlayer2()
self.player.set_mp3file("test2.mp3")
# widget
ctk.CTkButton(self, text="再生", command=self.player.play).pack(
padx=50, pady=10
)
# mainloop
self.protocol("WM_DELETE_WINDOW", self.on_closing)
self.mainloop()
def on_closing(self):
# 閉じる前にやること
self.player.stop()
# 閉じる
self.destroy()
App("MyMP3Player2")
クリック⇒鳴った、再生成功。
ただ、just_playbackにあったようなduration プロパティとか、seekメソッドなどと、同じようなのがsounddeviceにあるかと思っていたら、
見た感じ、play wait stopといったメソッドと同列のところにはなさそうです。どうやら Streams using NumPy Arrays
の中にあるメソッド名などに「それっぽい」のがあるように?見えます。
ただStreamの概念・お作法を全然知らないので、勉強が必要。
まず、「動く」基本のコードから
import time
import numpy as np
import sounddevice as sd
from pydub import AudioSegment
start_time = time.time()
# 音声ファイルを読み込む
audio = AudioSegment.from_file("test2.mp3")
audio_data = np.array(audio.get_array_of_samples())
# ステレオデータの場合、2次元配列に変換する
if audio.channels == 2:
audio_data = audio_data.reshape((-1, 2))
sr = audio.frame_rate
# 音声の再生
sd.play(audio_data, samplerate=sr)
sd.wait()
end_time = time.time()
print(f"{end_time - start_time:.3f}秒")
114.134秒(本体113秒)
「音声の再生」の2行をStreamを使った方法に書き換える。
# 音声の再生
# Create OutputStream
output_stream = sd.OutputStream(
samplerate=sr, channels=audio.channels, dtype="int16"
)
# Start the stream
output_stream.start()
# Write data to the stream
output_stream.write(audio_data)
# Close the stream
output_stream.close()
これでも同様に最後まで再生できました。113.323秒。速いじゃない!
ライブラリの書き換えを考えたとき、pause とか resumeも欲しい。Copilotに聞いてみます。
なるほど、stream.write(data[start_pos:]) で開始位置指定して開始できる。フレームレート×秒数で開始位置の値になる。ということのようです。
だんだんと目途が立ってきた気がします。