-
Notifications
You must be signed in to change notification settings - Fork 2
Tutorial 09 Custom Music
Goal. Add a file-based song, retarget a map's theme, author an authentic chiptune natively, and swap themes conditionally with a hook.
Prerequisites. Tutorial 07 (a map to score); Guide: Audio Authoring.
Files created.
mods/tutorial_09_music/
├── manifest.json
├── main.lua
└── assets/
├── town.ogg
└── town_loop.ogg
-
Register a file-based song. Song defs are shape-dispatched per record — a
{ file = ... }def streams audio while every other song keeps playing through the chip synth:return function(mod) mod.content.music:register("Music_ModTown", { file = mod.assets:path("assets/town.ogg"), loopFile = mod.assets:path("assets/town_loop.ogg"), }) end
fileplays once as the intro, thenloopFileloops; a def with onlyfileloops it. Outcome: the def validates; nothing plays it yet. -
Retarget a map's theme.
mod.content.map_songs:override("MODROUTE", "Music_ModTown")
map_songsmaps map id →musicid (Data.audio.mapSongs). Outcome: entering MODROUTE plays your song. Retargeting a vanilla map (map_songs:override("PALLET_TOWN", "Music_ModTown")) is the same one-liner. -
Author a chiptune.
src.audio.ChipAsmis a supported require — note-event Lua tables assembled into the same channel bytecode the ROM's own songs run on:local ChipAsm = require("src.audio.ChipAsm") mod.content.music:register("Music_ModChip", ChipAsm.song({ tempo = 140, channels = { { program = { { label = "top" }, { notetype = { speed = 12, volume = 15, fade = 2 } }, { octave = 4 }, { note = "C", len = 4 }, { note = "E", len = 4 }, { note = "G", len = 4 }, { octave = 5 }, { note = "C", len = 8 }, { loop = { count = 0, to = "top" } }, -- loop forever } }, }, }))
Each event table is one command —
note/len,rest,octave,notetype(speed/volume/fade),drum(channel 4),label,call,loop = { count, to }(count 0 = forever),vibrato,duty,tempo, and friends.ChipAsm.songreturns a{ chip = ... }def themusicregistry accepts directly. Outcome: a genuine GB channel program with no audio file in the mod. (Full event vocabulary: Audio Authoring.) -
Conditional music with the hook. Every song choice — map themes, battle themes, jingles — passes through one choke point:
mod.hooks:wrap("music.select", function(next, song, ctx) if ctx.reason == "map" and ctx.surfing then return "Music_ModChip" end return next(song, ctx) end)
Outcome: surfing anywhere plays your chip track; everything else is untouched.
ctxcarriesreason,mapId,mapSong,onBike,surfing,kind/battleKind,trainerId(Reference: Hooks).
MODROUTE plays the file song with a clean intro→loop seam; the chip song plays while surfing; disabling the mod restores every vanilla theme. Nothing about base audio was disabled to get here.
-
Forgetting
loopFile. The theme plays its intro and stops; loop material belongs inloopFile(or makefileself-looping). - A bad file path. A def whose file cannot load is skipped with a logged, mod-attributed error the next time its cue fires — the rest of the game's audio keeps playing. Watch the log, not silence.
-
Song id collisions.
music:register("Music_Cities1", ...)collides with the imported def;overrideis the verb for replacing a vanilla song.
Next: Tutorial 10 — New Mechanic.
Engine 1.0.0 · mod API 2 · repository · Discord
This project ships no ROM data. All game content is decoded on the player's machine from their own verified Pokemon Red ROM; mods ship recipes and original assets, never extracted content.
Start here
Concepts
Tutorials
- The ladder
- 01 Sprite and Text Tweak
- 02 Balance Patch
- 03 New Species
- 04 New Item and Ball
- 05 New Move
- 06 NPC and Dialogue
- 07 New Map
- 08 Quest
- 09 Custom Music
- 10 New Mechanic
- 11 Custom UI Screen
- 12 Mini Total Conversion
Reference
Guides
Playing