DSE SMDL Format
General Information
The SMDL format is a container for sequenced music. Its very close to the MIDI format, similarly to the SSEQ format normally used in most NDS games.
File Structure
Overview
Offset | Length | Name | Description |
---|---|---|---|
0x0 | 64 | SMDLHeader | The container's header. |
0x40 | 64 | Song Chunk | Information on the entire sequence. |
After Song Chunk | Varies | Trk Chunk | The first Track of the sequence. Its role is to set the tempo. |
..Additional tracks here.. | |||
After all Track Chunks | 16 | End of Content Chunk | This empty chunk marks the end of the SMDL container. |
SMDL Header
Offset | Length | Type | Name | Description |
---|---|---|---|---|
0x00 | 4 | char[4] | magicn | The 4 characters "smdl" {0x73,0x6D,0x64,0x6C} |
0x04 | 4 | - | unk7 | 4 bytes of zeroes. |
0x08 | 4 | uint32 | flen | File length in bytes. |
0x0C | 2 | uint16 | version? | Version number? ( 0x1504 ) |
0x0E | 1 | uint8 | unk1 | Unknown. |
0x0F | 1 | uint8 | unk2 | Unknown. |
0x10 | 4 | - | unk3 | 4 bytes of zeroes. |
0x14 | 4 | - | unk4 | 4 bytes of zeroes. |
0x18 | 2 | uint16 | year | Year the file was last modified. |
0x1A | 1 | uint8 | month | Month the file was last modified. |
0x1B | 1 | uint8 | day | Day the file was last modified. |
0x1C | 1 | uint8 | hour | Hour the file was last modified. |
0x1D | 1 | uint8 | minute | Minute the file was last modified. |
0x1E | 1 | uint8 | second | Second the file was last modified. |
0x1F | 1 | uint8 | centisecond? | Could possibly be the centisecond that the file was last modified. |
0x20 | 16 | char[16] | fname | Filename, ASCII null terminated string. Any extra space after the 0 on the total 16 bytes, is padded with 0xAA. |
0x30 | 4 | uint32 | unk5 | Unknown, usually 0x1. |
0x34 | 4 | uint32 | unk6 | Unknown, usually 0x1. |
0x38 | 4 | uint32 | unk8 | Unknown, usually 0xFFFFFFFF. |
0x3C | 4 | uint32 | unk9 | Unknown, usually 0xFFFFFFFF. |
Song Chunk
The song chunk is made of a single header.
Offset | Length | Type | Name | Description |
---|---|---|---|---|
0x00 | 4 | char[4] | label | Song chunk label "song" {0x73,0x6F,0x6E,0x67} |
0x04 | 4 | uint32 | unk1 | Unknown. Usually 0x1. |
0x08 | 4 | uint32 | unk2 | Unknown. Usually 0xFF10. |
0x0C | 4 | uint32 | unk3 | Unknown. Usually 0xFFFFFFB0. |
0x10 | 2 | uint16 | unk4 | Unknown. Usually 0x1. |
0x12 | 2 | uint16 | tpqn | Ticks Per Quarter Note. Usually 0x30 or 48 ticks per quarter note. (Works like MIDI clock ticks it seems.) |
0x14 | 2 | uint16 | unk5 | Unknown. Usually 0xFF01 |
0x16 | 1 | uint8 | nbtrks | Number of track(trk) chunks |
0x17 | 1 | uint8 | nbchans | Number of channels. (Unsure how channels works with DSE) |
0x18 | 4 | uint32 | unk6 | Unknown. Usually 0x0F000000 |
0x1C | 4 | uint32 | unk7 | Unknown. Usually 0xFFFFFFFF |
0x20 | 4 | uint32 | unk8 | Unknown. Usually 0x40000000 |
0x24 | 4 | uint32 | unk9 | Unknown. Usually 0x00404000 |
0x28 | 2 | uint16 | unk10 | Unknown. Usually 0x0200 |
0x2A | 2 | uint16 | unk11 | Unknown. Usually 0x0800 |
0x2C | 4 | uint21 | unk12 | Unknown. Usually 0xFFFFFF00 |
0x30 | 16 | - | unkpad | Unknown sequence of 16, 0xFF bytes. Possibly padding? |
Trk Chunk
Track chunks contain the events for each individual tracks of the music sequence. They work much like MIDI tracks.
Header
Offset | Length | Type | Name | Description |
---|---|---|---|---|
0x00 | 4 | char[4] | label | Track chunk label "trk\0x20" {0x74,0x72,0x6B,0x20} |
0x04 | 4 | uint32 | param1 | Unknown. Usually 0x01000000. |
0x08 | 4 | uint32 | param2 | Unknown. Usually 0x0000FF04. |
0x0C | 4 | uint32 | chunklen | Length of the trk chunk. Starting after this field, to the first 0x98 event encountered in the track. The length is in bytes. |
The content begins immediately after the header!
Content
Each tracks has a short preamble after the header, and before the actual events. That preamble indicates the channel and the track id assigned to the track.
All tracks must have a 0x98 event at their end. And the size of a track is counted up to that event, ignoring the padding bytes.
Offset | Length | Type | Name | Description |
---|---|---|---|---|
0x00 | 1 | uint8 | trkid | The track ID of the track. A number between 0 and 0x11. |
0x01 | 1 | uint8 | chanid | The channel ID of the track. A number between 0 and 0x0F?. |
0x02 | 1 | uint8 | unk1 | Unknown. Often 0. |
0x03 | 1 | uint8 | unk2 | Unknown. Often 0. |
0x04 | varies | - | events | Track events begin here. |
After Events | 0-3 | - | Padding | Padding to align the end of the track on 4 bytes. The value is usually the terminating 0x98 byte repeated as needed, up to 3 extra times. |
Eoc Chunk
Offset | Length | Type | Name | Description |
---|---|---|---|---|
0x0 | 4 | - | ChunkID | The chunk ID "eoc\0x20" {0x65, 0x6F, 0x63, 0x20} |
0x4 | 4 | uint32 | Param1 | Unknown meaning, is often 0x00000001. |
0x8 | 4 | uint32 | Param2 | Unknown meaning, is often 0x04FF0000. |
0xC | 4 | uint32 | Length | Always 0, for end of content chunks. |
DSE Events
Tracks are filled with event codes, which indicates to the sequencer what to do.
- Their value ranges from 0x0 to 0xFF.
- Events from 0x0 to 0x7F are reserved for the PlayNote event.
- Events from 0x80 to 0x8F are reserved for fixed duration pause events.
- Events from 0x90 to 0xFF all have their unique meaning.
PlayNote(0x0 to 0x7F)
The play note event is special in that its format is particular.
- The event code itself is the velocity(0x0 - 0x7F) of the note to be played.
- The first parameter byte is obligatory, and it contains info on whether we modify the track's current octave, how many extra parameters are there, and what note to play.
- The second parameter is optional, and its length varies depending on the length specified in the obligatory parameter. It contains the duration the key is "held down" before being released.
Here's a breakdown of the NoteData byte:
Bits | Name | Meaning |
---|---|---|
1100 0000 | OctaveMod | A value from 0 to 3 from which 2 is subtracted. The result is a signed value that we add to the track's current octave. |
0011 0000 | NbParamBytes | A value from 0 to 3 which indicates the amount of extra parameter bytes that should be read. |
0000 1111 | Note | The Note that should be played at the track's current octave(after OctaveMod). |
Note List
Value | Note Name |
---|---|
0x0 | C |
0x1 | C# |
0x2 | D |
0x3 | D# |
0x4 | E |
0x5 | F |
0x6 | F# |
0x7 | G |
0x8 | G# |
0x9 | A |
0xA | A# |
0xB | B |
0xF | Unknown |
Example Event
Velocity | NoteData | (Opt)KeyDownDuration | ||
---|---|---|---|---|
0x7F | 0x65 | 0x20 | 0x01 | 0x10 |
This event will play the note F, at the current track octave with no modifications, and hold the note for 0x100120 ticks.
Fixed Duration Pause(0x80 to 0x8F)
The fixed duration pause is a pause event based on the duration of common musical time intervals. Its event code goes from 0x80-0x8F.
Here's a table of the possible durations:
Code | Musical Time Interval | Duration in Ticks |
---|---|---|
0x80 | Half Note | 96 |
0x81 | Dotted Quarter Note | 72 |
0x82 | 2/3 of a Half Note | 64 |
0x83 | Quarter Note | 48 |
0x84 | Dotted 8th Note | 36 |
0x85 | 2/3 of a Quarter Note | 32 |
0x86 | 8th Note | 24 |
0x87 | Dotted 16th Note | 18 |
0x88 | 2/3 of a 8th Note | 16 |
0x89 | 16th Note | 12 |
0x8A | Dotted 32nd Note | 9 |
0x8B | 2/3 of a 16th Note | 8 |
0x8C | 32nd Note | 6 |
0x8D | Dotted 64th Note | 4 |
0x8E | 2/3 of a 32nd Note | 3 |
0x8F | 64th Note | 2 |
Others(0x90 to 0xFF)
Here is a list of all known events :
Code | Parameter Length | Parameters | Name | Description |
---|---|---|---|---|
0x90 | 0 | - | RepeatLastPause | Pause the track processing for the duration of the last pause.(Includes fixed duration pauses) |
0x91 | 1 | (uint8)Duration | AddToLastPause | Pause the track processing for the duration of the last pause + the duration in ticks specified.(Includes fixed duration pauses) |
0x92 | 1 | (uint8)Duration | Pause8Bits | Pause the track processing for the duration in ticks. |
0x93 | 2 | (uint16)Duration | Pause16Bits | Pause the track processing for the duration in ticks. |
0x94 | 3 | (uint24)Duration | Pause24Bits | Pause the track processing for the duration in ticks. |
0x95 | 1 | (uint8)CheckInterval | PauseUntilRelease | Pause the track processing as long as a note is held down. Will wait for at least "CheckInterval" ticks, then checks every "CheckInterval" ticks if all notes have been released on the current track. |
0x96 | 0 | - | INVALID | Disable current track. |
0x97 | 0 | - | INVALID | Disable current track. |
0x98 | 0 | - | EndOfTrack | Marks the end of the track. Is also used as padding to align the end of the track on 4 bytes. |
0x99 | 0 | - | LoopPoint | Marks the point the track will loop to after the end of track is reached. |
0x9A | 0 | - | INVALID | Disable current track. |
0x9B | 0 | - | INVALID | Disable current track. |
0x9C | 1 | ?? | ?? | ?? |
0x9D | 0 | ?? | ?? | ?? |
0x9E | 0 | ?? | ?? | ?? |
0x9F | 0 | - | INVALID | Disable current track. |
0xA0 | 1 | (uint8)Octave | SetTrackOctave | Sets the current track's octave to the value specified. Valid range is 0 - 9. |
0xA1 | 1 | (uint8)Octave | AddToTrackOctave | Adds the value specified to the current track octave. |
0xA2 | 0 | - | INVALID | Disable current track. |
0xA3 | 0 | - | INVALID | Disable current track. |
0xA4 | 1 | (uint8)TempoBPM | SetTempo | Sets the track's tempo in beats per minute. |
0xA5 | 1 | (uint8)TempoBPM | SetTempo | Sets the track's tempo in beats per minute? (The code is identical to 0xA4) |
0xA6 | 0 | - | INVALID | Disable current track. |
0xA7 | 0 | - | INVALID | Disable current track. |
0xA8 | 2 | ?? | ?? | ?? |
0xA9 | 1 | ?? | ?? | ?? |
0xAA | 1 | ?? | ?? | ?? |
0xAB | 0 | - | SkipNextByte | Skips processing the next byte. |
0xAC | 1 | (uint8)ProgramID | SetProgram | Change the track's program(Instrument Preset) to the one specified. |
0xAD | 0 | - | INVALID | Disable current track. |
0xAE | 0 | - | INVALID | Disable current track. |
0xAF | 3 | ?? | ?? | ?? |
0xB0 | 0 | ?? | ?? | ?? |
0xB1 | 1 | ?? | ?? | ?? |
0xB2 | 1 | ?? | ?? | ?? |
0xB3 | 1 | ?? | ?? | ?? |
0xB4 | 2 | ?? | ?? | ?? |
0xB5 | 1 | ?? | ?? | ?? |
0xB6 | 1 | ?? | ?? | ?? |
0xB7 | 0 | - | INVALID | Disable current track. |
0xB8 | 0 | - | INVALID | Disable current track. |
0xB9 | 0 | - | INVALID | Disable current track. |
0xBA | 0 | - | INVALID | Disable current track. |
0xBB | 0 | - | INVALID | Disable current track. |
0xBC | 1 | ?? | ?? | ?? |
0xBD | 0 | - | INVALID | Disable current track. |
0xBE | 1 | ?? | ?? | ?? |
0xBF | 1 | ?? | ?? | ?? |
0xC0 | 1 | ?? | ?? | ?? |
0xC1 | 0 | - | INVALID | Disable current track. |
0xC2 | 0 | - | INVALID | Disable current track. |
0xC3 | 1 | ?? | ?? | ?? |
0xC4 | 0 | - | INVALID | Disable current track. |
0xC5 | 0 | - | INVALID | Disable current track. |
0xC6 | 0 | - | INVALID | Disable current track. |
0xC7 | 0 | - | INVALID | Disable current track. |
0xC8 | 0 | - | INVALID | Disable current track. |
0xC9 | 0 | - | INVALID | Disable current track. |
0xCA | 0 | - | INVALID | Disable current track. |
0xCB | 2 | - | SkipNext2Bytes | Skip processing the next 2 bytes. |
0xCC | 0 | - | INVALID | Disable current track. |
0xCD | 0 | - | INVALID | Disable current track. |
0xCE | 0 | - | INVALID | Disable current track. |
0xCF | 0 | - | INVALID | Disable current track. |
0xD0 | 1 | ?? | ?? | ?? |
0xD1 | 1 | ?? | ?? | ?? |
0xD2 | 1 | ?? | ?? | ?? |
0xD3 | 2 | ?? | ?? | ?? |
0xD4 | 3 | ?? | ?? | ?? |
0xD5 | 2 | ?? | ?? | ?? |
0xD6 | 2 | ?? | ?? | ?? |
0xD7 | 2 | (uint16)Bend | PitchBend | Bend the pitch of the note. |
0xD8 | 2 | ?? | ?? | ?? |
0xD9 | 0 | - | INVALID | Disable current track. |
0xDA | 0 | - | INVALID | Disable current track. |
0xDB | 1 | ?? | ?? | ?? |
0xDC | 5 | ?? | ?? | ?? |
0xDD | 4 | ?? | ?? | ?? |
0xDE | 0 | - | INVALID | Disable current track. |
0xDF | 1 | ?? | ?? | ?? |
0xE0 | 1 | (int8)TrackVolume | SetTrackVolume | Change the track's volume to the value specified. (0x0-0x7F) |
0xE1 | 1 | ?? | ?? | ?? |
0xE2 | 3 | ?? | ?? | ?? |
0xE3 | 1 | (int8)TrackExpression | SetTrackExpression | Change the track's expression(secondary volume) to the value specified. (0x0-0x7F) |
0xE4 | 5 | ?? | ?? | ?? |
0xE5 | 4 | ?? | ?? | ?? |
0xE6 | 0 | - | INVALID | Disable current track. |
0xE7 | 1 | ?? | ?? | ?? |
0xE8 | 1 | (int8)Pan | SetTrackPan | Change the track's pan to the value specified. (0x0-0x7F. 0x40 is middle, 0x0 full left, and 0x7F full right ) |
0xE9 | 1 | ?? | ?? | ?? |
0xEA | 3 | ?? | ?? | ?? |
0xEB | 0 | - | INVALID | Disable current track. |
0xEC | 5 | ?? | ?? | ?? |
0xED | 4 | ?? | ?? | ?? |
0xEE | 0 | - | INVALID | Disable current track. |
0xEF | 1 | ?? | ?? | ?? |
0xF0 | 5 | ?? | ?? | ?? |
0xF1 | 4 | ?? | ?? | ?? |
0xF2 | 2 | ?? | ?? | ?? |
0xF3 | 3 | ?? | ?? | ?? |
0xF4 | 0 | - | INVALID | Disable current track. |
0xF5 | 0 | - | INVALID | Disable current track. |
0xF6 | 1 | ?? | ?? | ?? |
0xF7 | 0 | - | INVALID | Disable current track. |
0xF8 | 2 | ?? | ?? | ?? |
0xF9 | 0 | - | INVALID | Disable current track. |
0xFA | 0 | - | INVALID | Disable current track. |
0xFB | 0 | - | INVALID | Disable current track. |
0xFC | 0 | - | INVALID | Disable current track. |
0xFD | 0 | - | INVALID | Disable current track. |
0xFE | 0 | - | INVALID | Disable current track. |
0xFF | 0 | - | INVALID | Disable current track. |
Credits
- TruePikachu for some of the details on the SMDL format! [1]