Skip to content

Sound (FmrbAudio)

FmrbAudio is the audio playback API. Create an instance by calling FmrbAudio.new(self).

class MyApp < FmrbApp
  def on_create
    @audio = FmrbAudio.new(self)
  end
end

Internally, it sends MSG_TYPE_APP_AUDIO messages to the kernel, which are processed by the audio engine on the graphics side.

Supported file formats

For details on supported audio file formats, see Audio File Formats.

Methods

File Playback

Method Purpose
play(path, track: 0) Start playing a file
stop Stop playback
pause Pause playback
resume Resume playback

path is a file path (e.g. /usr/share/sounds/nsf/song.nsf). track: specifies the track number (used for files like NSF that contain multiple tracks).

FMSQ Sequences

A mechanism for pre-loading sequences into slots before playback. Suitable for short sound effects and looping BGM.

Method Purpose
load_fmsq(slot_id, binary_data) Register binary data into a slot
play_slot(slot_id) Play a registered slot
data = File.open("/sfx.fmsq", "r") { |f| f.read }
@audio.load_fmsq(0, data)
@audio.play_slot(0)

For detailed specifications, see Audio File Formats.

Tone Synthesis (note_on / note_off)

Directly drives NES APU-compatible channels. Allows playing short sound effects and game BGM from scripts.

@audio.note_on(channel, freq, volume = 10, duty = 2, sweep = 0)
@audio.note_off(channel)
Argument Range and Meaning
channel Channel number. NES APU configuration: 0 / 1 = pulse wave, 2 = triangle wave, 3 = noise
freq Frequency (Hz). Integer. May have a different meaning on the noise channel
volume Volume. Approximately 0 to 15. Default 10
duty Pulse wave duty cycle. 0 to 3
sweep Frequency sweep value (packed APU register value)

Example: Piano-style

class MiniPiano < FmrbApp
  KEYS = {
    "a" => 261, "s" => 293, "d" => 329, "f" => 349,
    "g" => 392, "h" => 440, "j" => 493, "k" => 523
  }

  def on_create
    @audio = FmrbAudio.new(self)
    @ch = 0
  end

  def on_event(ev)
    super
    return unless ev[:character]
    if ev[:type] == :key_down && (freq = KEYS[ev[:character]])
      @audio.note_on(@ch, freq, 10, 2, 0)
    elsif ev[:type] == :key_up
      @audio.note_off(@ch)
    end
  end
end

MiniPiano.new.start

Track Numbers and Playback Control

Use Case Recommended Track
BGM 0
SE (Sound Effects) 1 and above

Calling play(path, track:) with different track numbers simultaneously enables parallel playback on separate tracks (implementation dependent).

NSF File Playback

NES Sound Format is supported.

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

A playback GUI sample is available in tool/nsf_player.app.rb. For details, see Audio File Formats.

Cleanup

To stop any playing audio when the app exits, it is safe to call stop in on_destroy.

def on_destroy
  @audio.stop if @audio
end
  • Audio File Formats -- FMSQ / NSF specifications and how to create them
  • MIDI -- driving this same sound chip through a MIDI device, or sending MIDI to an external instrument
  • Examples -- piano, flappy, nsf_player, and more