LÖVE (Love2D) 入門編:6.音を鳴らす

(2018.2.17. 公開) (2018.8.26. 対象をバージョン 11 系列に変更)

1. 音ネタを読み込む

 音関係の処理は、love.audio 系の関数を使う。音ネタを読み込むのは love.audio.newSource()

source = love.audio.newSource(ファイル名, タイプ)

 「タイプ」には "static""stream" のどちらかを指定する。"static" は音データをすべてメモリに読み込んで処理するもの、"stream" は必要なデータをその都度読み込んで処理するもの。(0.10.x まではタイプを省略すると "stream" を指定したことになっていたが、11.x 以降は省略できないことになった。)

 オーディオファイルのフォーマットは、拡張子で判断される。サポートされているフォーマットは次の通り(参考:LÖVE Wiki: Audio Format)。

 不勉強にして "Tracker module" フォーマット(MOD フォーマットとも)というのを知らなかったんだけど、Amiga で利用されていた種々のオーディオフォーマットの総称みたいですね。欧米で音楽・ゲームに強いコンピュータと言えば Amiga だったから、ゲームエンジンの LÖVE としては MOD フォーマットのサポートは必須なのだろう。

 mid は MIDI フォーマットで、timidity を使って再生されるようだ。これが使えるのなら軽くていいじゃない、と思ったのだが、あまり実用になりそうにない。一番下にテストの結果を書いておきます。

2. 音を鳴らす・止める

 音を鳴らすのは love.audio.play(), 止めるのは love.audio.stop()

love.audio.play(source) -- 指定した音ネタを演奏する
love.audio.stop(source) -- 指定した音ネタの演奏を止める
love.audio.stop()       -- すべての音ネタの演奏を止める

 また、音ネタを指定する場合、次のようにも書ける。source は、love.audio.newSource(...) で返された値。source の直後はピリオド . ではなくて、コロン : であることに注意。

source:play() -- 演奏する
source:stop() -- 止める

 音ネタをループ演奏したいときは、source:setLooping() を使う。

source:setLooping(true) -- ループ設定
source:play() -- 演奏する(無限ループ)

 演奏中かどうかは source:isPlaying() で調べられる。false になっていれば、演奏は終了している。

if source:isPlaying() then
  -- 演奏中
else
  -- 演奏は終わっている
end

 演奏時間、現在の演奏位置はそれぞれ source:getDuration(), source:tell() で調べる。演奏が終了すると、演奏位置は 0 に巻き戻される。

d = source:getDuration() -- 演奏時間
t = source:tell() -- 現在の位置
r = d - t         -- 残り時間

 「ある音ネタの演奏が終わったら、次の音ネタを演奏する」には、love.update() の中で演奏の終了を調べて処理する。例えば、source1, source2, source3 を順に演奏してループするなら、こんな風になる。

function love.load()
  source1 = love.audio.newSource("sound1.mp3", "static")
  source2 = love.audio.newSource("sound2.mp3", "static")
  source3 = love.audio.newSource("sound3.mp3", "static")
  source1:play()
  source0 = source1  -- 今演奏している音ネタ
end
function love.update(dt)
  if not source0:isPlaying() then
    -- source1 -> source2 -> source3 -> source1 ->...
    if source0 == source1 then source0 = source2
    elseif source0 == source2 then source0 = source3
    else source0 = source1 end
    source0:play()
  end
end

3. 音を鳴らす時の負荷

 Raspberry Pi model A+ を使って、音ネタを読み込み・演奏する時の負荷を調べてみた。使用したのは、再生時間 13 秒の mp3 ファイル。[wien.mp3] これを wien01.mp3, wien02.mp3, ... と名前を変えて16個コピーを作り、読み込んで演奏してみる。

 "static" を指定すると、メモリ消費量(ps -o vsz で調べたもの)が 170 MB に達した。読み込むのに要した時間は 16秒。これらを同時に演奏することは可能で、その時の %CPU は 41% 程度。

 "stream" を指定すると、メモリ消費量は 100 MB 程度になった。読み込みは 2.8 秒で完了。ところが、これらを同時に演奏しようとすると、間もなく %CPU が 100% 近くになって、音が鳴らなくなってしまう。"stream" はデコードの負荷があるので、あまり多くの同時演奏はできないようだ。なお、8ファイルの同時演奏だと、%CPU は 66% 程度で、時々ノイズは入るが一応演奏はできた。

 Windows, Mac では、64 まで同時に演奏できる。Love2D のソースの中に 64 が定数としてコーディングされているので、この値が Love2D の仕様としての上限になる。ただ、ラズパイの場合は上限値はもっと低いし、メモリ容量や CPU 負荷も考慮する必要がある。

4. mid を試してみたけどだめだった

 上の mp3 の元ネタの MIDI ファイルを演奏してみた。 [wien.mid]

 Mac の内蔵音源で鳴らしてみたものがこちら(先ほどの mp3 と同じ)。

(※ ウェブ版では音が再生できます)

 love で鳴らすとこうなる。こりゃだめですね。音質はともかく、ノートオフがちゃんと処理されていないのが致命的。

(※ ウェブ版では音が再生できます)

 ちなみに、love で mid ファイルを演奏しようとすると load_pat > can not open /usr/local/share/timidity/timidity.cfg, use environment variable MMPAT_PATH_TO_CFG for the directory というエラーが出る。timidity.cfg をちゃんと作って設定してやれば、あるいはまともに鳴るのかもしれない。まともに鳴ってくれるのであれば、mid ファイル+サウンドフォントの組み合わせは悪くないと思うのだが、今のところ保留にしておく。(ただ、ラズパイ上ではあまり期待はできないかも。)

目次