This is a tool for the Pokémon Red/Blue disassembly project, which allows editing the music tracks by exporting them to MIDI files and then importing them back into assembly.
Features:
Given a monophonic sequence of MIDI notes, takes care of generating octave
commands and using the right combination of note_type
speeds and note
lengths to encode MIDI durations.
Can create one-shot or looping tracks.
Changes tempo with MIDI tempo events.
Supports all note modifiers used by the game's music engine.
Finds repeated sections of assembly events and breaks them out into
subroutines, referenced via sound_call
, to save ROM space.
Future work:
Extend to the pokegold or pokecrystal projects?
Possibly use sound_loop
to further shorten the assembly code.
Windows/Mac/Linux executables are posted on the releases page.
To build yourself, install a Haskell development kit and build the package as
appropriate. I use Stack where stack build
is the only step, but Cabal
should work as well as long as you also have GHC, Alex, and Happy.
make
uses Stack to produce a release .zip
file. Ruby is also required to get
the package version. make docker
uses Docker to build in an older Linux
distribution for higher compatibility.
pokemid in.asm out.mid # assembly to MIDI
pokemid in.mid out.asm # MIDI to assembly
# Second argument can be elided to go to stdout
For MIDI to assembly, the MIDI file should be in the following format:
Up to 4 tracks, each for one Game Boy channel, where the track for channel
N
(1 to 4) has a track name event containing ChN
.
MIDI tempos are used to create tempo change events, inserted into the first track in the assembly file.
In addition to monophonic notes, you can insert engine commands which affect the sound using MIDI text events. Supported events are:
note_type <volume>, <fade>
vibrato <delay>, <rate>, <depth>
duty_cycle <int>
duty_cycle_pattern <int>, <int>, <int>, <int>
volume <left>, <right>
stereo_panning <int>
pitch_slide <int>, <int>
toggle_perfect_pitch
For documentation on these see the pokered project.
All of these events affect all notes that come after them, except the
pitch_slide
event which affects only the next (or coinciding) note.
Note that note_type
does not require you to supply a note encoding
speed, though you can if you wish (before the volume and fade
parameters). It is ignored because speeds are chosen by the program
automatically.
Events cannot be placed in the middle of a note, only at its beginning/end or in the middle of a rest.
To make a loop, use text events begin
and end
around the looping
portion. Without these the song will simply play once. You can also have
just an end
event for a one-shot song. begin
and end
, like other
events, cannot be in the middle of a note.
Only tempos that fit into the following equation are precisely encodable:
x = 19200 / bpm
where x
is a 2-byte integer, and bpm
is the tempo in beats per minute.
x
will be rounded to the nearest integer if needed.
Only note durations that fit into the following equation are precisely encodable:
duration = (ticks / 4) * (speed / 12)
duration = (ticks * speed) / 48
where duration
is the length of the note in beats or quarter notes,
ticks
is a number from 1 to 16, and speed
is a number from 1 to 15.
Furthermore, ticks
and speed
cannot both be 1.
If a length cannot be exactly represented, the closest one less than it will
be used. Rests between notes/events can be any length which is a sum of
encodable lengths.
Channel 4 notes (percussion/noise) use the following pitches:
snare1
snare2
snare3
snare4
snare5
triangle1
triangle2
snare6
snare7
snare8
snare9
cymbal1
cymbal2
cymbal3
mutedsnare1
triangle3
mutedsnare2
mutedsnare3
mutedsnare4
Assembly notetype
volumes are encoded as MIDI note velocities as follows:
That is, each assembly volume gets 8 MIDI velocities, except assembly 0 which gets 7.