pyxen.Music
Entityにコンポーネントとしてアタッチされるストリーミングオーディオトラックです。
Musicはオンデマンドでデコードされるため、BGMやアンビエンスなどの長いトラックに適しています。
オーディオのコンセプトについてはオーディオを参照してください。
コンストラクタ
Music(filename, *, volume=1.0, pan=0.0, loop=False)
filename は唯一の位置引数で、プロジェクト内の音楽アセットの名前です(拡張子なし)。残りの3つはキーワード専用で、同名のプロパティの初期値を設定します(コンポーネントのプロパティを参照)。
パラメータ
| パラメータ | 型 | 説明 |
|---|---|---|
filename | str | 音楽アセットの名前(拡張子なし) |
volume | float | 初期ゲイン。デフォルトは 1.0 |
pan | float | 初期のステレオ位置(-1.0〜1.0)。デフォルトは 0.0 |
loop | bool | トラックの終了時に最初から再生する。デフォルトは False |
例
theme = Music("theme", volume=0.6, loop=True)
Musicのアタッチ
生成時:
bgm = world.spawn(music=Music("theme"))
または後から:
entity.music = Music("theme")
ミュージックを割り当てると即座に再生が開始されます。
コンポーネントのプロパティ
entity.musicでアクセス:
| プロパティ | 型 | R/W | 説明 |
|---|---|---|---|
loop | bool | R/W | ループを有効にする |
volume | float | R/W | ゲイン(0.0以上にクランプ) |
pan | float | R/W | ステレオパンニング(-1.0〜1.0) |
time | float | R | 現在の再生位置(秒) |
duration | float | R | 全体の長さ(秒) |
finished | bool | R | 再生が完了するとTrue |
channels | リスト風 | R | トラックの各パート。それぞれが独自の volume を持つ — 「チャンネル」の節を参照 |
beat | int | R/W | 再生開始からの拍数。代入するとその位置へシークする — 「音楽的な時間」を参照 |
bar | int | R/W | 再生開始からの小節数。代入するとその位置へシークする |
phase | float | R | 現在の拍の中での進み具合(0.0〜1.0) |
beats_per_bar | int | R | 1小節の拍数 |
frames_to_beat | int | R | 次の拍までのフレーム数 |
tempo | float | R/W | 再生速度の倍率 — 「テンポ」を参照 |
bpm | int | R | ファイルが示す1分あたりの拍数 |
loop
entity.music.loop = True
有効にすると、トラックは終了後に自動的に再スタートします。BGMでは一般的です。
volume
entity.music.volume = 0.5
0.0未満の値は0.0にクランプされます。
pan
entity.music.pan = -1.0 # 完全に左
entity.music.pan = 1.0 # 完全に右
entity.music.pan = 0.0 # 中央
-1.0から1.0の範囲にクランプされます。
time
t = entity.music.time
読み取り専用。現在の再生位置(秒)。
duration
d = entity.music.duration
読み取り専用。トラックの全体の長さ(秒)。
finished
if entity.music.finished:
world.destroy(entity)
読み取り専用。トラックの再生が完了するとTrue(loopがFalseの場合)。
チャンネル
music = entity.music
for i in range(len(music.channels)):
print(i, music.channels[i].volume)
music.channels[1].volume = 0.0 # パート1をミュートする
トラッカーモジュールやMIDIファイルは、1つのクロックで演奏される独立したパートの集まりです。music.channels はパートごとに1つのハンドルを返し、それぞれが次のプロパティを持ちます。
| プロパティ | 型 | R/W | 説明 |
|---|---|---|---|
index | int | R | チャンネル番号 |
volume | float | R/W | このパートだけのゲイン(0.0〜1.0 にクランプ) |
すべてのチャンネルは 1.0 から始まります。すべてのパートが1つの再生ヘッドを共有するため、どれかの音量を上げ下げしても、他のパートとずれることはありません。サウンドトラックの盛り上がりを変えるのにこれを使います。アクションが激しくなったらパートを足し、プレイヤーがやられたら外す、といった具合です。
チャンネルが何を指すかはフォーマットによって異なります。
| フォーマット | チャンネル |
|---|---|
| トラッカーモジュール | モジュールのチャンネルごとに1つ(一般的な .mod は4つ) |
| MIDI | 常に16個。MIDIチャンネルごとに1つ。ファイルが使わないチャンネルは無音になるだけ |
| OGG | なし: len(music.channels) は 0 |
チャンネル番号は 0 から始まります。範囲外の番号を指定すると IndexError が発生し、music.channels[i] そのものには代入できず、volume にだけ代入できます。負のインデックスはPythonのリストと同じく末尾から数えます。music.channels[-1] は最後のチャンネルです。
MIDIでは、チャンネルを 0.0 にすると、そのチャンネルで鳴り続けているノートも切られます。
音楽的な時間
if music.beat != last_beat:
last_beat = music.beat
pulse()
| プロパティ | 意味 |
|---|---|
beat | 再生開始からの拍数(整数)。単調増加です。ループしても 0 に戻らず数え続けるので、折り返しを特別扱いせずに前フレームの値と比較できます |
bar | 再生開始からの小節数(整数)。こちらも単調増加です |
phase | 現在の拍の中での位置。0.0 以上 1.0 未満。beat が段階的に変わるのに対し、phase は連続的です。拍と拍の間でなめらかに変化・減衰させたいものに使います |
beats_per_bar | 1小節の拍数 |
frames_to_beat | 次の拍が来るまでのフレーム数(切り上げ)。拍の直後ではなく拍ちょうどに何かを始めたいときに使います |
beat や bar への代入はシークになります。 music.bar = 8 と1回書くだけで8小節目の頭へ移動し、全チャンネルは同期したままです。次のフレームで新しい位置が読み取れます。
カウンターはファイルから時間を測るため、フォーマットによって使えるかどうかが変わります。
| フォーマット | beat、bar、phase、frames_to_beat、beats_per_bar、bpm |
|---|---|
| トラッカーモジュール | 利用可能。下記参照 |
| MIDI | 利用可能。ファイルから読み取ります。下記参照 |
| OGG | 利用不可: beat、bar、beats_per_bar、frames_to_beat、bpm は -1、phase は 0.0 を返し、beat や bar への代入は何も行いません |
そのため、OGGの可能性がある音楽に同期するゲームは、最初に music.beat >= 0 を確認し、-1 のときは独自のタイマーに切り替えてください。
MIDIファイルの拍
MIDIファイルは自身の音楽的な時間を記述しており、Pyxenはそれを読み取ります。1拍はファイルの四分音符で、bpm と1拍の長さは再生中のファイルのテンポ変更に従います。beats_per_bar はファイルの最初の拍子記号から四分音符単位で求めます。3/4は 3、6/8も 3 です。拍子記号のないファイルは4拍子として扱います。
MIDIトラックがループすると、カウントは次の小節の頭まで進みます。そのため beat と bar は増え続け、各周回は小節の1拍目から始まります。シークはちょうどその拍に移動します。その位置より前から鳴っていた音符は再生されません。
トラッカーモジュールの拍
モジュールには拍子の情報がないため、Pyxenはモジュールが持つ唯一の定義であるBPMを使います。トラッカーの1ティックは 2.5 / BPM 秒なので、ファイルのBPMが正しくなるのは1拍が24ティックのとき、つまりスピード6で4行、またはスピード3で8行のときです。Pyxenはこの方法で数えます。行ではなくティックで数えることで、モジュールの途中でスピードが変わっても拍が安定します。
1小節は4拍です。これはデータではなく慣習です。3/4拍子のモジュールでも beats_per_bar は 4 と報告されます。1拍あたりのティック数が異なる曲では、拍カウンターが聞こえ方と合いません。
テンポ
music.tempo = 1.5 # 50%速く、ピッチはそのまま
music.tempo = 1.0 # 通常
tempo はトラックが要求する速度に掛ける倍率です。リサンプリングは行わないためピッチは変わらず、各パートが早く、または遅く進むだけです。tempo が何であっても、bpm はファイル自身の値を報告し続けます。
制限: 最大は 8.0 です。下限は、トラッカーモジュールでは 0.25 まで。受け付けられない値は無視されます(プロパティは以前の値のままです)。MIDIは 0.05 まで受け付けます。OGGでは tempo は効果がなく、常に 1.0 が返ります。
beat、phase、frames_to_beat は tempo に追従するので、音楽に同期したゲームは、音楽を速めても同期したままです。
実践例
拍ごとのパルス
この例は、プロジェクトに theme.mod というトラッカーモジュールがあることを前提にしています。
from pyxen import *
last_beat = -1
def start():
world.spawn(name="song", music=Music("theme", loop=True))
world.spawn(name="pulse", x=160, y=90,
shape=Circle(24, fill=(1.0, 0.5, 0.2, 1.0)))
def update():
global last_beat
music = world.get(name="song").music
pulse = world.get(name="pulse")
# phase は各拍の中で0から1へ進むので、拍の瞬間に大きくなり、その後落ち着く。
s = 1.0 + 0.5 * (1.0 - music.phase)
pulse.scale = (s, s)
# beat は1拍につき1回だけ変わる。
if music.beat != last_beat:
last_beat = music.beat
print("beat", last_beat, "bar", music.bar)
パートをミュートする
クリックするたびに、トラックの2番目のパートをオフ・オンにします。
from pyxen import *
def start():
world.spawn(name="song", music=Music("theme", loop=True))
def update():
music = world.get(name="song").music
if input.mouse.left.pressed:
part = music.channels[1]
if part.volume > 0.0:
part.volume = 0.0
else:
part.volume = 1.0
小節へジャンプする
from pyxen import *
def start():
world.spawn(name="song", music=Music("theme", loop=True))
def update():
music = world.get(name="song").music
if input.mouse.left.pressed:
music.bar = 4 # 4小節目へ直接ジャンプする
フォーマット
音楽アセットには、OGG Vorbis(.ogg)、MIDI(.mid、.midi)、またはトラッカーモジュール — ProTracker(.mod)、FastTracker II(.xm)、Impulse Tracker(.it)、Scream Tracker 3(.s3m)— を使用できます。
アセットはベース名で検索されます。Music("theme") は theme.ogg、theme.mod など、プロジェクト内に存在する拡張子のファイルを見つけます。拡張子を渡すのが最もよくある間違いです。Music("theme.ogg") は theme.ogg.ogg を探して例外を送出します。
トラッカーモジュールは、同じ曲のOGGが数メガバイトになるところを数キロバイトで収まることが多く、構造上シームレスにループします — 小さなゲームに最適です。
トラッカーモジュールは、ここで最も多くの機能を使えるフォーマットでもあります。チャンネル、拍、小節、テンポのすべてがモジュールで動作します。MIDIにはチャンネルとテンポがあり、OGGにはどちらもありません(「チャンネル」と「音楽的な時間」の節を参照)。
モジュールはパンニングに対応しています。ProTrackerの 8xx(パン設定)コマンドで、曲の再生中にチャンネルをステレオ空間内で動かせます。
Sound(WAV、完全にメモリに読み込み)と異なり、Musicはストリーミングされオンデマンドでデコードされます — 長いオーディオにより適しています。
よくあるエラー
ファイル名の指定なし
Music()
ValueError: Music must have a file
無効なアセット
Music("nonexistent")
Music("theme.ogg") # 拡張子はファイル側に付けます。名前には含めません
ValueError: Music 'nonexistent' not found. Use the asset name without its extension (.ogg, .mid, .mod, .xm, .it, .s3m).