The current URL is datacrystal.tcrf.net.
Pokémon mini/MINLIB/Audio
This is a sub-page of Pokémon mini/MINLIB.
Audio on the PM is single-channel pulse-width modulation piped through a piezo speaker. It's controlled by timer 3 (enabled via its various registers), with the note frequency being set to TMR3_PRE (16 bit register at 0x4A) and the pulse-width being set to TMR3_PVT (16 bit register at 0x4C) via the following formulas:
- TMR3_PRE = (Timer3_Frequency / Sound_Frequency) – 1
- TMR3_PVT = TMR3_PRE * Pulse_Width = TMR3_PRE - (TMR3_PRE / pulse_width_table[index])
A pulse-width of 0.5 (50%) is a square wave. There is also a volume control which can be 0% (muted), 50%, or 100%. All other volume levels seen in games (25% and 75%) are emulated by MINLIB by means of adjusting the pulse-width.
The audio format in MINLIB games is a combination of a series of data tables plus a track list where each track is a list of commands. Each table may be a different length depending on the game. They are listed in order of occurrence and are packed back-to-back in ROM.
Note lengths table
This is effectively a two dimensional table. It contains series of arrays of a fixed length (per game) where each array contains a series of 1 byte note lengths for a given tempo. The lengths are in the order of shortest to fastest, ending in a whole note. Each length at a certain index is essentially the same note length relative to the tempo for each array. That is, if the first length is a 1/32nd note, this is true for all arrays in this table, the value simply differs relative to the tempo.
The maximum size of this table is 16x16.
Example note lengths table
For example, this is the table from Pokemon Race mini where 8x is the note length command and Bx is the tempo command. All values in the table are in hex. B6+ are not available in Race or Breeder. B9 is only available in Party and Pichu Bros. BA is only available in Pichu Bros.
| B\8 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
|---|---|---|---|---|---|---|---|---|---|
| B0 | 01 | 02 | 04 | 06 | 08 | 0c | 10 | 18 | 20 |
| B1 | 01 | 03 | 06 | 09 | 0c | 12 | 18 | 24 | 30 |
| B2 | 02 | 04 | 08 | 0c | 10 | 18 | 20 | 30 | 40 |
| B3 | 03 | 06 | 0c | 12 | 18 | 24 | 30 | 48 | 60 |
| B4 | 04 | 08 | 10 | 18 | 20 | 30 | 40 | 60 | 80 |
| B5 | 05 | 0a | 14 | 1e | 28 | 3c | 50 | 78 | a0 |
| B6 | 06 | 0C | 18 | 24 | 30 | 48 | 60 | 90 | C0 |
| B7 | 07 | 05 | 0A | 0F | 14 | 1E | 28 | 3C | 50 |
| B8 | 04 | 07 | 0E | 15 | 1C | 2A | 38 | 54 | 70 |
| B9 | 06 | 09 | 12 | 1B | 24 | 36 | 48 | 6C | 90/C0 |
| BA | FF | FF | FF | FF | FF | FF | FF | FF | FF |
Which corresponds to note lengths of: 1/32, 1/16, 1/8, dotted 1/8 (3/16), 1/4, dotted 1/4 (3/8), 1/2, dotted 1/2 (3/4), 1 (whole).
And tempos of: ??? (TODO: find timer frequency or something, timing in ticks per whole note are: 32, 48, 64, 96, 128, 160).
Pulse widths table
A very small table, contains 1 byte values of pulse-width denominators. Around 4 to 8 bytes long, with a maximum of 16 bytes.
Breeder: 02 04 08 10 Race: 02 04 08 10 Togepi: 02 04 08 10 Tetris: 02 04 08 10 20 70 Party: 02 04 08 10 20 10 40 FF Pichu: 02 04 08 10 20 10 40 60 Pinball: 02 04 08 10 20 Puzzle: 02 04 08 10 20 Puzzle 2: 02 04 08 10 20 60 50 ZanyCard: 02 04 08 10 20 40 80 FF LunchTime: 02 04 08 10 20 70
02, 04, 08, and 10 corresponds to: 1/2, 3/4, 7/8, and 15/16
Because, for example, 3/4s PW is audibly equivalent to 1/4 PW, you can think of it as 1/x where x is the value in the table, but (x-1)/x is more correct to how MINLIB works.
Sustain table
A small table indicating on which tick the volume should drop to 50%. Since the tick counter for a playing note counts down, a lower value is later in the playtime of a note. For example, 1 means it only plays the note at 50% for the final tick. Parentheticals indicate it's technically a portion of the effects table but it makes sense to use here, too.
Breeder: 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F Race: 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F Togepi: 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F Tetris: 01 02 03 04 05 (06) Party: 01 02 03 04 05 06 Pichu: 01 02 03 04 05 06 Pinball: 01 02 03 04 05 (06) Puzzle: 01 02 03 04 05 (06) Puzzle 2: 01 02 03 04 05 06 ZanyCard: 01 02 03 04 05 06 LunchTime: 01 02 03 04 05 (06)
Effects table
A table with 3-byte rows describing pitch offsetting effects.
The bytes in order are: Delay, Counter Mask, Multiplier
When a new note plays, if there's an effect active, it will initialize a counter to 0 which it then increments every tick, before applying effects (so it effectively starts at 1). It then takes the current preset (the value taken from the note frequencies table, originally) and multiplies its hi byte by the multiplier. If the counter & mask is 0, it adds this multiplication result to the current preset and stores it; if it's non-0 it subtracts the value instead. This also performs equivalent math to keep the pivot (and thus pulse width) the same, so that it only affects pitch.
A counter mask of 00 indicates a falling pitch and one of FF indicates a rising pitch. A multiplier of 00 indicates there's no effect. Any other mask indicates some sort of vibrato. For example, a mask of 01 alternates rising then falling every tick. A mask of 02 would rise for two ticks then fall for two ticks.
For all tables, the first 5 effects are different forms of vibrato, then a falling pitch, then two speeds of rising pitch. After that it differs a lot.
Breeder: 08 04 01 03 04 01 03 04 03 03 01 03 08 04 01 00 00 40 00 FF 60 00 FF 10 00 00 00 04 FF 07 20 00 18 Race: 08 04 01 03 04 01 03 04 03 03 01 03 08 04 01 00 00 40 00 FF 60 00 FF 10 00 00 00 04 FF 07 20 00 18 Togepi: 08 04 01 03 04 01 03 04 03 03 01 03 08 04 01 00 00 40 00 FF 60 00 FF 10 00 00 00 04 FF 07 20 00 18 Tetris: 06 04 01 00 02 06 06 02 01 06 04 02 08 04 01 00 00 03 00 FF 03 00 FF 18 00 FF 1C 02 02 01 Party: 04 02 02 03 04 01 03 04 03 03 01 03 08 04 01 00 00 40 00 FF 60 00 FF 10 00 FF 1C 00 04 08 Pichu: 04 02 02 03 04 01 03 04 03 03 01 03 08 04 01 00 00 02 00 FF 60 00 FF 10 00 FF 1C 00 04 08 00 20 04 00 FF 01 Pinball: 06 04 01 03 02 03 06 02 01 06 04 02 08 04 01 00 00 18 00 FF 20 00 FF 18 Puzzle: 06 04 01 03 02 03 06 02 01 06 04 02 08 04 01 00 00 18 00 FF 20 00 FF 18 Puzzle 2: 06 04 01 00 02 06 06 02 01 06 04 02 08 04 01 00 00 10 00 FF 20 00 FF 18 02 02 01 ZanyCard: 06 04 01 03 02 03 06 02 01 06 04 02 08 04 01 00 00 18 00 FF 20 00 FF 18 LunchTime: 06 04 01 03 02 03 06 02 01 06 04 02 08 04 01 00 00 03 00 FF 03 00 FF 18 00 FF 1C
Note frequencies table
An array of 2 byte note frequencies stored as roughly oscillator Hz (2,000,000) divided by the frequency and stored as a little-endian uint16. The notes start at C2 and increase in semitones, containing around 6 octaves. The last entry will be a rest note, having a value of 0x0000.
There can be a maximum of 128 entries.
Track pointers table
An array of 2 byte addresses for the start points of each track within the same bank as this table is defined.
There can be a maximum of 255 entries (first is inaccessible).
Tracks
Each track is a series of 1 byte commands. If the most significant bit is set, then it performs some sort of action, otherwise it's a note. Thus the command table, with commands in hex, is:
- 00-7f: play the corresponding note from the note frequencies table
- 8x: change note length, x being an index into the current tempo array in the note lengths table
- 9x: enable an effect, x being an index into the effects table
- Ax: sustain timing, x being an index into the sustain table
- Bx: change the tempo, x being an index into the note lengths table
- Cx: change pulse width, x being an index into the pulse width table
- Dx: same as 9x
- Ex: same as Ax
- Fx: flow control
- F0: end song
- F1: mark loop start
- F2: return to loop start
- any other x is the same as F1