コンテンツにスキップ

音声ファイルフォーマット

Family mruby が対応する音声ファイル形式と、その作成・変換方法を説明します。

形式 用途 API
FMSQ 短い BGM / SE のシーケンスデータ。スロットにロードして再生 FmrbAudio#load_fmsq, play_slot
NSF ファミコン音楽(NES Sound Format) FmrbAudio#play(path, track:)

FMSQ

FMSQ (Family mruby Sequence) は NES APU 互換 の音楽データを記述する独自フォーマットです。短い効果音や BGM ループに向いています。

レジスタの意味は以下参照。

https://www.nesdev.org/wiki/APU_registers

ファイル構造

12 バイトのヘッダ + 可変長コマンド列。すべて little-endian。

+------+----------------+
| 0..3 | "FMSQ"         |  magic
| 4    | version (=1)   |
| 5    | flags (=0)     |  reserved
| 6..7 | frame_count    |  total frames
| 8..9 | data_size      |  command bytes
| 10..11 | loop_offset  |  0 = no loop
+------+----------------+
| 12.. | command stream |
+------+----------------+

命令コード一覧

cc = チャンネル番号 (00=Pulse1, 01=Pulse2, 10=Triangle, 11=Noise)、 aaaaa = $4000 からのレジスタオフセット (0x00-0x17)。

ビットパターン 命令 後続バイト 合計サイズ
0xxxxxxx WAIT (1-128フレーム) 0 1
10cc0000 NOTE_ON チャンネル別 2-4 3-5
10cc0001 NOTE_OFF 0 1
10cc0010 PARAM MASK + 可変 2 以上
110aaaaa REG_WRITE ($4000+aaaaa) 1 (DATA) 2
11100000 DPCM_PLAY 3 (RATE_FLAGS, ADDR, LENGTH) 4
11100001 DPCM_STOP 0 1
11100010 DPCM_RAW 1 (VALUE) 2
11111110 END 0 1
11111111 LOOP 2 (OFFSET) 3

APU エミュレータ側の制限: 現状の APU エミュレータ側ではDPCMの機能がサポートされてない

WAIT命令

0xxxxxxx
  • 上位1ビット = 0 -> WAIT
  • 下位7ビット = 待ちフレーム数 - 1
意味
0x00 1フレーム待ち
0x01 2フレーム待ち
... ...
0x7F 128フレーム待ち

チャンネル命令 (NOTE_ON / NOTE_OFF / PARAM)

10ccxxxx 形式の高レベルコマンド。cc がチャンネル(Pulse1=0, Pulse2=1, Triangle=2, Noise=3)、下位4ビットがサブコマンド。 NOTE_ON / NOTE_OFF は内部で $4015(Status)の対応ビットも更新する。

NOTE_ON

10cc0000 後続バイトはチャンネル別:

チャンネル 後続バイト 書き込み順
Pulse1 / Pulse2 TIMER_LO, TIMER_HI, VOL_ENV, SWEEP (4 バイト) VOLSWEEPLOHI
Triangle TIMER_LO, TIMER_HI, LINEAR (3 バイト) LINEARLOHI
Noise PERIOD_MODE, VOL_ENV (2 バイト) VOLLO

NOTE_OFF

10cc0001 後続バイトなし。$4015 の該当チャンネルビットをクリアするのみ(APUレジスタ自体は変更しない)。

PARAM

10cc0010 [MASK] [DATA...]

マスクで指定したサブパラメータだけを差分更新する。マスクビットと後続データは以下:

Pulse PARAM (Pulse1 / Pulse2)

mask bit 後続データ 書き込み先
0x01 (TIMER) LO, HI (2 バイト) $4002/3 または $4006/7
0x02 (VOL) VOL_ENV (1 バイト) $4000 または $4004
0x04 (SWEEP) SWEEP (1 バイト) $4001 または $4005

Triangle PARAM

mask bit 後続データ 書き込み先
0x01 (TIMER) LO, HI (2 バイト) $400A/B
0x02 (LINEAR) LINEAR (1 バイト) $4008

Noise PARAM

mask bit 後続データ 書き込み先
0x01 (PERIOD) PERIOD_MODE (1 バイト) $400E
0x02 (VOL) VOL_ENV (1 バイト) $400C

マスクの bit 順に後続バイトを読み出して書き込む。指定されていないビットの後続バイトは存在しない。

REG_WRITE命令

110aaaaa [DATA]

APUレジスタへの直接書き込み。NSFのAPUレジスタ操作を順序・値ともに完全に保持する。

  • aaaaa: $4000からのオフセット (0-23)
  • DATA: 書き込む値 (1バイト)
  • 合計: 2バイト/書き込み
offset (aaaaa) APUレジスタ 内容
0x00 $4000 Pulse1 Volume/Envelope/Duty
0x01 $4001 Pulse1 Sweep
0x02 $4002 Pulse1 Timer Low
0x03 $4003 Pulse1 Timer High + Length
0x04 $4004 Pulse2 Volume/Envelope/Duty
0x05 $4005 Pulse2 Sweep
0x06 $4006 Pulse2 Timer Low
0x07 $4007 Pulse2 Timer High + Length
0x08 $4008 Triangle Linear Counter
0x0A $400A Triangle Timer Low
0x0B $400B Triangle Timer High + Length
0x0C $400C Noise Volume/Envelope
0x0E $400E Noise Period + Mode
0x0F $400F Noise Length
0x10 $4010 DMC Frequency/Flags
0x11 $4011 DMC DAC (7bit)
0x12 $4012 DMC Sample Address
0x13 $4013 DMC Sample Length
0x15 $4015 Status (Channel Enable)

参考

fmruby-graphics-audio/tools/ 以下に Ruby のジェネレータがあります(PC で実行)。

fmruby-graphics-audio/tools/gen_test_fmsq.rb     # スケールパターン
fmruby-graphics-audio/tools/gen_intro_fmsq.rb    # ジングル

これらを参考に音階・コードを記述すると .fmsq バイナリが生成できます。

再生

スロット(番号 ID)にロードしてから再生します。

data = File.open("/sfx.fmsq", "r") { |f| f.read }
@audio.load_fmsq(0, data)   # スロット 0 に登録
@audio.play_slot(0)         # 再生

詳細は FmrbAudio を参照。

NSF (NES Sound Format)

NES Sound Format は実機ファミコン音楽を再生する標準フォーマットです。Family mruby は NSF ファイルの再生に対応しています。

入手方法

  • 自分で作る場合は FamiStudio などの DAW から NSF エクスポート

再生

@audio.play("/usr/share/music/music.nsf", track: 1)

track: は曲番号(1 始まり)。NSF には複数曲が含まれるため、ファイル名で曲を選んだ後、track 指定で個別曲を選べます。

制限

  • 再生できないNSFファイルもあります

サンプル

/app/tool/nsf_player.app.rb に NSF 再生 GUI のサンプルがあります(曲送り・トラック選択・一時停止 / 再開 を実装)。

WAV / MP3

未対応 です。FMSQ または NSF に変換してください。

直接合成 (note_on / note_off)

ファイルを使わずに、FmrbAudio#note_on で APU を直接駆動できます。リズムやセリフの効果音、ボタンクリック音などに向きます。

@audio.note_on(0, 440, 10, 2, 0)   # 矩形波 1ch で A4
sleep_ms(200)
@audio.note_off(0)

詳細は FmrbAudio ▸ note_on / note_off を参照。

関連

  • FmrbAudio
  • ピアノアプリ: /app/game/piano.app.rb
  • 効果音 + BGM 例: /app/game/flappy.rb
  • NSF プレイヤー: /app/tool/nsf_player.app.rb