Skip to content

undisbeliever/terrific-audio-driver

Repository files navigation

Terrific Audio Driver

A homebrew audio driver for the Super Nintendo.

Features

  • A custom loader to speed-up data loading.
  • A bytecode-based song and sound effect data format and interpreter.
  • Songs written in Music Macro Language (MML). (mml syntax)
  • 8 music channels.
  • Sound effects:
    • Sequenced sound effects, written in either bytecode assembly (syntax) or MML.
    • 2 sound effect channels, which will duck (temporally mute) song channels G and/or H.
    • Elaborate sound effect dropout behaviour:
      • The one-channel flag set limit a sound effect to a single channel, preventing common and frequent sound effects from using both sound effect channels.
      • The interruptible flag controls if a sound effect can be interrupted (useful for short sound effects, like collect coin, collision and splash) or uninterruptable (useful for long or vocal sound effects).
  • Custom ADSR and GAIN envelopes in songs and sound effects.
  • Mono, stereo and surround audio modes.
  • Lots of effects:
    • Vibrato and portamento
    • Volume slides, tremolo, pan slides and panbrello
    • Detune
    • Left/right channel invert (if the audio mode is set to Surround)
    • Commands for editing the echo buffer parameters in the middle of a song
  • ca65, 64tass and pvsneslib APIs
    • The API is asynchronous to minimise impact on the entity/mainloop code.
    • Audio data can be loaded across multiple frames (Tad_Process calls).
    • Audio data is loaded through a LoadAudioData callback, simplifying integration with existing resource subsystems. Alternativly, the tad-compiler binary can create and export the LoadAudioData callback.
    • Audio data can wrap across bank boundaries.
  • A GUI for editing and previewing samples, sound effects and songs.

Engine Limitations and Deliberate Design Decisions

  • There are only 2 sound effect channels.
    • When a sound effect is playing, music channels G and/or H will be ducked (temporally muted).
  • There is no sample swapping.
    • Samples and sound effect data are combined to form the common audio data and transferred to audio-RAM in a single block.
    • The loader has the ability to override the common audio data. If you want to have a special set of songs that contain unique songs (ie, intro and credits), you will need to create a new project (with a different set of samples) and manually swap the common audio data in S-CPU code.
  • All pitches and pitch offsets are precalculated by the compiler
    • The pitches are stored in a pitch table in the Common Audio Data.
    • The pitch table holds a maximum 256 pitches (512 bytes).
    • All pitch offsets are precalculated by the MML compiler. To calculate this pitch-offset per tick value, the MML compiler needs to know what instrument is playing the portamento/vibrato note. The MML syntax provides a way to manually set the pitch-offset per tick value.
    • Sound effects written in bytecode must manually the pitch-offset per tick value.
  • There is a fixed 1 tick delay after a key-off event.
    • 1 tick will be subtracted from any instruction that emits a key-off event. For example, a play_note c+4 16 instruction will play the note, sleep for 15 ticks, emit a key-off event, and sleep for 1 final tick.
    • This delay is required to prevent popping.
  • MML subroutines and loops share a bytecode stack. The compiler will refuse to compile songs and sound effects that would cause a stack-overflow.
  • There are no overflow or underflow checks when playing notes.
    • The MML compiler will check for pitch-out-of-range errors, but only if it knows which instrument is playing the note.
  • There are no FIR filter overflow or echo feedback overflow checks in the compiler.
    • If the FIR filter or echo feedback overflows it can clip, pop or explode the song. Headphone users should turn down their volume when playing with the echo filter or feedback.
  • The CLI song compiler and audio-driver loader cannot detect audio-RAM overflow.
    • The GUI can check if songs will fit in audio-RAM.
    • tad-compiler check can be used to check if all songs in a project will fit in audio-RAM.
  • Additional limitations exist for sound effects:
    • Sound effects are played at a fixed tempo (125 ticks per second).
    • Sound effects cannot call subroutines.
    • The echo buffer and FIR Filter settings are set by the song. If a sound effect enables echo, it can have an inconsistent sound (depending on the songs echo and FIR settings).
    • The default sound effect queue can only hold 1 sound effect. This means only 1 sound effect can be played per frame. If the game requests two or more sound effects in a single frame, the one with the lowest index (as defined by the Sound Effect Export Order) will be prioritised.

Build Requirements

  • Rust
  • Cargo
  • Git
  • GNU Make
  • A C++17 compiler, compatible with the cxx crate.
  • CMake
  • Rust (version > 1.55), CMake (version > 3.11), Git and a C++17 compiler to compile fltk-rs. (See fltk-rs build dependencies for more details.)
    • MSVC: Windows SDK
    • Linux/BSD: X11 and OpenGL development headers
  • SDL 2 to compile the rust-sdl2 crate
    • Windows (MSVC): Follow the instructions on the rust-sdl2 README to install and setup SDL2.
    • Linux/BSD: Install the SDL2 development headers

Build Scripts

This project uses two build scripts:

Build Instructions

Licensing

The terrific audio driver is copyright (c) 2023, Marcus Rowe.

See docs/licenses.md for full license text.

  • The audio driver (S-SMP and .spc code) is licensed under the zlib License.

  • The compiler and GUI are licensed under the MIT License.

  • The audio emulator used by the GUI is licensed under the ISC License.

  • The compiler and GUI make use of multiple third party open source projects.

  • The GUI is based in part on the work of the FLTK project (https://www.fltk.org).