Files
scawful 6ba634caa4 Add comprehensive documentation for sprites and systems
- Introduced detailed analysis for the Minecart system, highlighting its state machine, track system, and areas for improvement.
- Created an NPCs analysis document, summarizing various NPC sprites and their functionalities.
- Added an Objects analysis document, covering interactive elements like collectibles, ice blocks, and minecarts.
- Documented the Overlord sprite system, detailing its role in spawning other sprites and managing events.
- Compiled a Dungeons & Indoor Areas document, outlining custom room tags, enhanced mechanics, and advanced collision systems.
- Developed an Overworld Systems Analysis, focusing on the ZSCustomOverworld architecture and its core features.
- Added a Time System document, explaining the in-game clock and day/night cycle management.
- Documented the ZScream Custom Overworld, detailing its data-driven approach and key features.
2025-10-02 12:44:30 -04:00

7.1 KiB

Music Creation Guide

This document details the process for creating and integrating custom music into Oracle of Secrets. The project uses the native Super Nintendo Packet Chip (N-SPC) music format, abstracted through a powerful set of asar macros.

1. N-SPC Music Format Primer

Music in the N-SPC engine is structured around eight independent channels. Each channel is a stream of bytes that are read sequentially. The stream consists of two types of data:

  • Commands: Special bytes (from $E0 to $FF) that control aspects of the sound, such as setting the instrument, changing volume, or calling a subroutine.
  • Notes: A note consists of a duration byte followed by one or more tone bytes. The duration byte (e.g., $48 for a quarter note) determines how long the following tone(s) will play.

2. The Macro System (Core/music_macros.asm)

To make composing more intuitive, the project uses a comprehensive library of macros that wrap the raw N-SPC commands into readable names. All music files must include Core/music_macros.asm.

Key Concepts

  • Note Durations: Constants are defined for standard note lengths (e.g., !4th, !8th, !16th).
  • Note Tones: Constants are defined for all notes across several octaves (e.g., C4, G4s for G#4, A5).
  • Special Notes: Tie ($C8) continues the previous note for the new duration, and Rest ($C9) signifies silence.

Core Macros

  • %SetInstrument(id): Sets the instrument for the current channel (e.g., %SetInstrument($09) for Strings). Helper macros like %Strings(), %Piano(), etc., exist for common instruments.
  • %SetTempo(value): Sets the overall playback speed of the song.
  • %SetMasterVolume(value) / %SetChannelVolume(value): Sets the volume for the entire song or just the current channel.
  • %CallSubroutine(address, repeats): The most important macro for structuring songs. It jumps to a labeled subroutine, plays it repeats+1 times, and then returns. This is the primary method for looping musical phrases.
  • %VibratoOn(delay, rate, depth): Adds a vibrato effect.
  • %TremoloOn(delay, rate, depth): Adds a tremolo (volume fluctuation) effect.
  • %SetPan(value): Sets the stereo position (left/right) of the channel.
  • %EchoVBits(switch, left, right): Enables and configures echo for the channel.

3. Song File Structure

Every song .asm file follows a standard structure.

1. Header

The file begins with a header that defines metadata for the song engine.

MyNewSong:
!ARAMAddr = $D86A      ; Base address in ARAM for this song
dw !ARAMAddr+$0A      ; Pointer to the Intro section
dw !ARAMAddr+$1A      ; Pointer to the Main (looping) section
dw $00FF              ; Default fade-in
dw !ARAMAddr+$02      ; Start of the looping section data
dw $0000

2. Channel Pointers

Next is a table of pointers to each of the eight channel data blocks. The !ARAMC constant is used to make these pointers relative to the song's ARAM address.

.Channels
!ARAMC = !ARAMAddr-MyNewSong
dw .Channel0+!ARAMC
dw .Channel1+!ARAMC
; ...up to 8 channels, use dw $0000 for unused channels

3. Channel Data

Each channel is a block of code containing commands and notes.

.Channel0
  %SetMasterVolume($DA)
  %SetTempo(62)
  %SetInstrument($02) ; Tympani
  %SetDurationN(!4th, $7F)
  %CallSubroutine(.sub1+!ARAMC, 23) ; Call subroutine .sub1 24 times
  db End ; $00, signifies end of channel data

4. Subroutines

The bulk of a song is made of small, labeled subroutines containing musical phrases. These are placed after the channel data.

.sub1
  db !4th, B1, B1, !8th, Tie, C2, !4th, F3s
  db End ; Subroutines must also end with $00

4. How to Add a New Song

  1. Create the File: Create a new .asm file in the Music/ directory.
  2. Copy Template: Copy the contents of an existing song (e.g., stone_tower_temple_v2.asm) into your new file to use as a template.
  3. Set Header: Change the main label (e.g., MyNewSong:) and set the !ARAMAddr. This address must be unique and not conflict with other songs.
  4. Compose: Write your music in the channel and subroutine blocks using the note constants and macros.
  5. Integrate the Song:
    • Open Music/all_music.asm and add an incsrc for your new file.
    • To replace a vanilla song, find its label in the ROM map and use org to place your new song at that address. For example, to replace the Lost Woods theme:
      org $1AADDE ; Original address of Lost Woods theme
      incsrc "Music/MyNewSong.asm"
      
    • To add a new song to the expanded Dark World bank, open Music/expanded.asm and add a new entry to the SongBank_OverworldExpanded_Main table.

5. Proposals for Improved Organization

The current system is functional but can be made more readable and maintainable.

  1. Standardize Subroutine Naming: The current convention of .sub1, .sub101, etc., is ambiguous. A clearer naming scheme would greatly improve readability.

    • Proposal: Name subroutines based on their musical function, like .MelodyVerseA, .BasslineIntro, .PercussionFill1. This makes the main channel blocks easier to read as a high-level song structure.
  2. Create a Common Patterns Library: Many songs use similar rhythmic or melodic patterns (e.g., a standard 4/4 drum beat, an arpeggiated chord).

    • Proposal: Create a Music/common_patterns.asm file. This file could contain a library of generic, reusable subroutines for things like drum patterns, basslines, or common arpeggios. Songs could then incsrc this library and call these patterns, reducing code duplication and speeding up composition.
  3. Develop Advanced Composition Macros: The existing helper macros are basic. More advanced macros could abstract away the manual process of defining and calling subroutines.

    • Proposal:

      • %DefineMeasure(Name, Notes...): A macro that takes a name and a list of notes and automatically creates a correctly formatted subroutine block.
      • %PlayMeasure(Name, Repeats): A macro that automatically calculates the relative address (+!ARAMC) and calls %CallSubroutine.
    • Example Workflow with Proposed Macros:

      ; --- Subroutine Definitions ---
      %DefineMeasure(VerseMelody, !8th, C4, D4, E4, F4, G4, A4, B4, C5)
      %DefineMeasure(VerseBass, !4th, C2, G2, A2, F2)
      
      ; --- Channel Data ---
      .Channel0
        ; ... setup ...
        %PlayMeasure(VerseMelody, 4) ; Plays the melody 4 times
        db End
      
      .Channel1
        ; ... setup ...
        %PlayMeasure(VerseBass, 4) ; Plays the bassline 4 times
        db End
      

      This approach would make the main channel data blocks read like a high-level song arrangement, significantly improving clarity.

  4. Improve In-File Documentation:

    • Proposal: Encourage the use of comments to label major song sections directly within the channel data (e.g., ; --- VERSE 1 ---, ; --- CHORUS ---). This provides crucial signposting when navigating complex song files.