Music/SMD
SMD files are Final Fantasy Tactics's music files. Each SMD file holds one song.
List of Songs: Songs
Contents
Header
The header consists of the following:
Offset | Type | Purpose |
---|---|---|
0x00 | string | SMD identifier (literal "smds") |
0x04 | ? | Unknown |
0x8 | uint | File size (in bytes) |
0xc | ? | Unknown |
0x14 | uint | Number of channels |
0x13 | ? | Unknown |
0x1a | char | Reverb Mode |
0x1b | char | Volume Depth |
0x1c | char | Reverb Delay |
0x1d | char | Reverb Feedback |
0x1e | uint | Offset of filename |
0x20 | uint | Offset of data chunk |
0x22 | uint[number of channels] | Offset of channel N. The number of these will equal the number of channels read earlier. |
Varies | uint | End of offsets. (Should equal 0) |
Varies | string | Filename. This is a zero-terminated string. always 12 bytes long though. music ##.mid |
Channels
SMD files contain a number of channels. Each channel contains a sequence of notes and instructions, and they're all played simultaneously. To play a 3-note chord, for example, you need to have 3 channels, and each channel plays one note of the chord. Typically, channel 0 doesn't contain notes, only special instructions like tempo settings. There is some evidence that notes placed in channel 0 will not play, so notes should probably go in channels 1 and higher.
Instructions
Each instruction begins with an identifier byte and is followed by zero to 3 parameter bytes. The number of parameter bytes depends on the instruction.
Value (hex) | Name | Parameters (bytes) | Purpose |
---|---|---|---|
0x00-0x7F | Note | 1 or 2 | Play a note. 0x00 is the quietest and 0x7F is the loudest. The 1st parameter is the note's pitch and duration. (See the table below.) If the first parameter is divisible by 0x13 (19), then there will be a second parameter. |
0x80 | Rest | 1 | The parameter is the length of the rest, in 192nds of a note. A duration of 0xc0 (192) is equal to a whole note, 0x60 (96) is a half note, etc. |
0x81 | Fermata | 1 | Extend previous note. The parameter is the length of the extension, in 192nds of a note. |
0x90 | End Bar | 0 | End channel. Stops any more instructions from being read in this channel. |
0x91 | Loop | 0 | Loop remainder of channel indefinitely. |
0x94 | Octave | 1 | Set octave. The parameter is the octave. 0x03 is the 4th octave (the one with middle C). |
0x95 | Raise Octave | 0 | Increment octave |
0x96 | Lower Octave | 0 | Decrement octave |
0x97 | Time Signature | 2 | Set time signature, apparently. Not sure if this affects playback or is just informational. The 1st parameter is the top number, the 2nd is the bottom. |
0x98 | Repeat | 1 | Begin loop. The parameter is the number of times to loop |
0x99 | Coda | 0 | End loop. (sets a loop point?) |
0x9a | To Coda | 0 | Goes to set point? |
0x9c | Sound Effect | 3 | Play a sound effect. first argument is the sound effect ID. |
0xa0 | Tempo | 1 | Set tempo. 0x66 (102) is the most commonly-used tempo across all files, and it appears to be 120 bpm. |
0xa1 | Accelerando | 1 | Raises the tempo by the argument's value. |
0xac | Instrument | 1 | Set instrument. The parameter is the instrument number. For a full list, see: Instruments. |
0xb2 | Enable LFO | 0 | enables oscillation based on the sound wave of the previous instrument. !! Only works if the current instrument is in an Odd (not even) channel. |
0xb3 | Disable LFO | 0 | turns off oscillation. |
0xb4 | Enable Noise | 1 | Sets the current instrument to Noise. Argument is the noise clock. |
0xb5 | Increase Noise | 1 | increases the Noise clock by the argument. |
0xb6 | Turn on Noise | 0 | Sets the current instrument to Noise. Does not calculate the noise clock. |
0xb7 | Disable Noise | 0 | Turns off noise for this instrument. |
0xb8 | Acoustics | 3 | Sets the reverb delay, reverb feedback, and volume depth of the current song. probably intended to only be used via channel 0. |
0xba | Enable Reverb | 0 | Turns reverb on for this instrument. |
0xbf | Disable Reverb | 0 | Turns off reverb for this instrument. |
0xc0 | Naturale | 0 | Resets the current instruments parameters (based off of its WAVESET soundfont data!) |
0xc1 | Envelope Shape | 3 | Sets the attack mode, sustain mode, and release mode of the current instrument, in that order. [1] |
0xc2 | Attack time | 1 | Set attack time. First parameter is the length of the attack. Higher values mean the note takes longer to fade in. |
0xc3 | Decay Time | 1 | Sets Decay Time. First parameter is the length of the decay. |
0xc4 | Sustain | 1 | Sets the sustain. first parameter is the length of the sustain. |
0xc5 | Release | 1 | Sets the release. first parameter is the length of the release. |
0xc6 | Sustain level | 1 | Set sustain level. first parameter is the sustain level. higher values hold the note louder. |
0xc7 | Envelope Tail | 2 | Sets both the Decay time and the sustain level, in that order. |
0xc8 | Attack Mode | 1 | Sets the attack gradient. if the attack mode is 5, set the attack mode to exponential. Otherwise, it's linear. |
0xc9 | Sustain Mode | 1 | Sets the sustain gradient. if the sustain mode is 1, set linear increase. if the sustain mode is 5, set exponential increase. if the sustain mode is 7, set exponential decrease. otherwuse, linear decrease. |
0xca | Release Mode | 1 | Sets the release gradient. if the release mode is 7, set exponential gradient. Otherwise, it's linear. |
0xd2 | ? | 1 | ? |
0xd7 | ? | 1 | ? |
0xd8 | Pitch Shift | 3 | The 1st parameter is the speed, 2nd is the intensity (amount of pitch change), and 3rd is delay before pitch shift happens in each note. |
0xda | ? | 0 | ? |
0xdb | ? | 0 | ? |
0xe0 | Dynamics | 1 | Set volume. 0x00 is the quietest, 0x7f is the loudest. |
0xe1 | Crescendo | 1 | Increases volume. |
0xe2 | Fermata Ramp | 2 | Sets a fermata with the length of argument 1. sets the target volume to reach by the end of the fermata as argument 2. |
0xe3 | ? | 1 | ? |
0xe4 | ? | 3 | ? |
0xe6 | ? | 0 | ? |
0xe8 | Balance | 1 | Set balance. 0x00 is left speaker, 0x7f is right speaker. |
0xe9 | Shift Balance | 1 | Adjust the balance towards either the left or right speaker. |
0xfd | Scale tempo | 1 | Multiply the tempo by the argument. |
0xfe | Select Sound Font | 1 | Tries to find a soundfont with the ID given as a parameter. as only one soundfont in FFT exists that can be played with suzuki's music library, this does nothing. |
Notes
To play a note, use a note-playing instruction (00-7f) followed by a byte from the table below. If you use a custom note length (0x00, 0x13, 0x26, etc.), then you will need a second parameter specifying the note's length, in 192nds of a note (see explanation of Rests, above). This gives you more control over exactly how long the note lasts.
C | C# | D | D# | E | F | F# | G | G# | A | A# | B | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Custom | 00 | 13 | 26 | 39 | 4c | 5f | 72 | 85 | 98 | ab | be | d1 |
whole | 01 | 14 | 27 | 3a | 4d | 60 | 73 | 86 | 99 | ac | bf | d2 |
dotted half | 02 | 15 | 28 | 3b | 4e | 61 | 74 | 87 | 9a | ad | c0 | d3 |
half | 03 | 16 | 29 | 3c | 4f | 62 | 75 | 88 | 9b | ae | c1 | d4 |
dotted quarter | 04 | 17 | 2a | 3d | 50 | 63 | 76 | 89 | 9c | af | c2 | d5 |
3rd | 05 | 18 | 2b | 3e | 51 | 64 | 77 | 8a | 9d | b0 | c3 | d6 |
quarter | 06 | 19 | 2c | 3f | 52 | 65 | 78 | 8b | 9e | b1 | c4 | d7 |
dotted 8th | 07 | 1a | 2d | 40 | 53 | 66 | 79 | 8c | 9f | b2 | c5 | d8 |
6th | 08 | 1b | 2e | 41 | 54 | 67 | 7a | 8d | a0 | b3 | c6 | d9 |
8th | 09 | 1c | 2f | 42 | 55 | 68 | 7b | 8e | a1 | b4 | c7 | da |
dotted 16th | 0a | 1d | 30 | 43 | 56 | 69 | 7c | 8f | a2 | b5 | c8 | db |
12th | 0b | 1e | 31 | 44 | 57 | 6a | 7d | 90 | a3 | b6 | c9 | dc |
16th | 0c | 1f | 32 | 45 | 58 | 6b | 7e | 91 | a4 | b7 | ca | dd |
dotted 32nd | 0d | 20 | 33 | 46 | 59 | 6c | 7f | 92 | a5 | b8 | cb | de |
24th | 0e | 21 | 34 | 47 | 5a | 6d | 80 | 93 | a6 | b9 | cc | df |
32nd | 0f | 22 | 35 | 48 | 5b | 6e | 81 | 94 | a7 | ba | cd | e0 |
48th | 10 | 23 | 36 | 49 | 5c | 6f | 82 | 95 | a8 | bb | ce | e1 |
64th | 11 | 24 | 37 | 4a | 5d | 70 | 83 | 96 | a9 | bc | cf | e2 |
96th | 12 | 25 | 38 | 4b | 5e | 71 | 84 | 97 | aa | bd | d0 | e3 |
Credit
Thanks to P.J. Barnes for figuring out the structure of the file and a few commands.