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.) Or possibly just the tick rate.. |
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!
Preamble
- 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.
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.
Events
- 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. Events themselves are not aligned on 4 bytes.
Offset Length Type Name Description After Preamble 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.
Example
Here's an example "Events" section (it does not include the preamble and header):
E3 73 A4 82 93 E0 1F 98
It ends on an offset divisible by 4, so there is only a single 0x98 event at the end.
Here's what would happen if we'd add an extra events. Lets copy the 0xE3 event and its parameter once:
E3 73 E3 73 A4 82 93 E0 1F 98 98 98
..we needed to add 2 extra 0x98 events to make the track end on an offset divisible by 4!
Eoc Chunk
The EoC chunk marks the end of the SMDL file/container.
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 events. Each event starts by a single byte containing a code, which indicates to the sequencer what to do.
- Their value ranges from 0x0 to 0xFF.
- Values from 0x0 to 0x7F are reserved for the PlayNote event.
- Values from 0x80 to 0x8F are reserved for fixed duration pause events.
- Values from 0x90 to 0xFF all have their unique event assigned to them.
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 (Half Note as part of a triplet?) | 64 |
0x83 | Quarter Note | 48 |
0x84 | Dotted 8th Note | 36 |
0x85 | 2/3 of a Quarter Note (Quarter Note as part of a triplet?) | 32 |
0x86 | 8th Note | 24 |
0x87 | Dotted 16th Note | 18 |
0x88 | 2/3 of a 8th Note (8th Note as part of a triplet?) | 16 |
0x89 | 16th Note | 12 |
0x8A | Dotted 32nd Note | 9 |
0x8B | 2/3 of a 16th Note (16th Note as part of a triplet?) | 8 |
0x8C | 32nd Note | 6 |
0x8D | Dotted 64th Note | 4 |
0x8E | 2/3 of a 32nd Note (32th Note as part of a triplet?) | 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 the next byte in the track. |
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 | 0 | - | SkipNext2Bytes | Skip the next 2 bytes in the track. |
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 | 0 | - | SkipNext2Bytes2 | Skips the next 2 bytes in the track. |
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]
- The unknown Japanese author of the "smd2mid v0.10" utility floating around the web.