背景音楽¶
(Tested on CPython3.9.7 + Kivy2.1.0)
BGM再生を手助けするmodule
これが要った理由¶
このような物が要った理由は kivy.core.audio.Sound
がそのままでは扱い難かったからです。
例えば kivy.core.audio.Sound.seek()
の説明には
Most sound providers cannot seek when the audio is stopped. Play then seek.
とありますが単純に
sound = SoundLoader.load(...)
sound.play()
sound.seek(...)
として良いかというとそうではなく、実際には .play()
の後に少し間を置かないと期待通りに動きませんでした。
sound = SoundLoader.load(...)
sound.play()
await async_library.sleep(...)
sound.seek(...)
そしてどうやらこれは .seek()
に限った話ではなく、
.play()
や .stop()
を呼んだ後は少し間を置いてから sound
に触らないと安定しないようです。
つまり kivy.core.audio.Sound
を直接触るコードは仕様にすら書かれていないのに必要な 間 でコードが汚されてしまう事になります。
そこでこのmoduleの出番です。 このmoduleは 間 を完全に覆い隠すのに加えBGM再生に必要になると思われる次の機能を提供します。
音を鳴らす時には前回停めた位置から再開。
音を停める時には徐々に音量を下げる。
音を鳴らす時には徐々に音量を上げる。
使い方¶
もしアプリが一種類のBGMしか鳴らさないのであれば Bgm
だけで十分です。
from kivy.core.audio import SoundLoader
from kivyx.misc.bgmplayer import Bgm
sound = SoundLoader.load(r"path/to/bgm.ogg")
sound.loop = True
bgm = Bgm(sound)
# 鳴らしたくなったら
bgm.play()
# 停めたくなったら
bgm.stop()
そして複数のBGMがある場合は BgmPlayer
が使えます。
from kivyx.misc.bgmplayer import BgmPlayer
bgmplayer = BgmPlayer()
bgmplayer.play(r"path/to/bgm1.ogg") # bgm1.oggが鳴る
bgmplayer.play(r"path/to/bgm2.ogg") # bgm1.oggが停まりbgm2.oggが鳴る
bgmplayer.play(r"path/to/bgm1.ogg") # bgm2.oggが停まりbgm1.oggが前回停まった位置から鳴る
bgmplayer.stop() # bgm1.oggが停まる
BgmPlayer
は既定では一度読み込んだBGMはずっと持ち続けるので次回以降の再生が早くなります。
言うまでもなくこれはメモリを惜しみなく使っている事によります。
もしこの振る舞いを良しとせずメモリの使用量を抑えたいのであれば MemoryEfficientLoader
が使えます。
from kivyx.misc.bgmplayer import BgmPlayer, MemoryEfficientLoader
bgmplayer = BgmPlayer(loader=MemoryEfficientLoader())
MemoryEfficientLoader
はBGMが別の物に切り替えられ次第すぐに以前の物を棄てるのでメモリの使用量が抑えられる…はずです [1] 。
反面切り替えられる度に kivy.core.audio.Sound
を作り直すので二回目以降であってもあまり再生は早くならないです [2] 。
Loaderの自作¶
Loaderは自作することもできます。例えば以下のように予めBGMを一括で読み込んでおけば再生開始時の遅延を限りなく抑えられるでしょう。
loader = {
fn: (sound := SoundLoader.load(fn), setattr(sound, 'loop', True), ) and Bgm(sound)
for fn in filenames
}
bgmplayer = BgmPlayer(loader=loader)
loaderは loader[key]
という式で Bgm
のinstanceを取り出せるようになっている物なら何でも良く、
うまく使えば独自のキャッシュ戦略や読み込み戦略を採れるかもしれません。
- class kivyx.misc.bgmplayer.Bgm(*args, **kwargs)[source]¶
Bases:
EventDispatcher
一つ分のBGMの再生を司る下層のclass。
- Parameters:
sound (kivy.core.audio.Sound) –
- fade_in_duration = 2.0¶
何秒かけて音量を徐々に上げるか
- fade_out_duration = 2.0¶
何秒かけて音量を徐々に下げるか
- internal_delay = 0.5¶
kivy.core.audio.Sound
を操作する度に設ける内部用の待ち時間。
- length = None¶
(read-only) BGMの長さ。単位は秒。instance化直後はこの値はNoneなので実際の長さが入れられるまで少し待たないといけない。
import asynckivy as ak bgm = Bgm(...) if bgm.length is None: await ak.event(bgm, 'length') print(f"BGMの長さは{bgm.length}秒です")
- property pos¶
再生位置。単位は秒。
kivy.core.audio.Sound.seek()
とは違って停止中にも自由に動かせる。
- unload()[source]¶
kivy.core.audio.Sound.unload()
を呼んで資源を解放する。以後はこのオブジェクトを操作しても何も起こらない。
- volume = 1.0¶
上げきった時の音量