BMS (File Format)

From Luma's Workshop
Revision as of 18:09, 11 January 2025 by SY24 (talk | contribs) (Fixed Key)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

File Format

BMS files are Binary Music Sequences that store commands for the audio engine to play notes, similar to the MIDI file format.

Data

BMS files contain raw instructions for the audio engine. There are no headers or filesize checks. Each instruction (OpCode) stands for its own function in the engine. The following table lists all JAudio2 OpCodes that have functions in SMG2:

OpCode Function (SMG/SMG2) Usage Example Description
00
01
02
...
7D
7E
7F
{OpCode[1]} {Voice[1]} {Velocity[1]}
OpCode: one byte with range 0x00 through 0x7F. 0x00 = C-1, 0x3C = C4 (middle C), 0x7F = G9.
Voice: one byte with range 0x01 through 0x07 (needs testing/verification). Each Voice can only hold one Note.
Velocity: one byte with range 0x00 through 0x7F. 0x00 = 0, 0x7F = 127.

If the Voice is not 1 - 7, the game will treat this note as a Midi Gate note, and will expect a 4th value that is one or more bytes with range 0x00 through 0xFF. If the leading byte is 0x80 (bit 7 = 1) or greater, another following byte will be taken into account. It is unknown what effects this has on the output.
64 02 68

64: Key E4
02: Voice 2
68: Velocity 104
Starts playing a Note with the previously defined Instrument (see E1, E2 and E3).
80 Unknown. Seems to do nothing based on the code.
81
...
87
{OpCode[1]}
OpCode: one byte with range 0x81 through 0x87. 0x81 = Close Voice 1, 0x87 = Close Voice 7.
Stops playing a Note.
88
...
8F
Unknown
90
...
9F
Unknown
A0 NULL
A1 NULL
A2 NULL
A3 NULL
A4 NULL
A5 NULL
A6 NULL
A7 NULL
A8 NULL
A9 NULL
AA NULL
AB NULL
AC NULL
AD NULL
AE NULL
AF NULL
B0 Extended Opcode Indicator See B000 or B001.
B1 JASSeqParser::cmdNoteOn
B2 JASSeqParser::cmdNoteOff
B3 JASSeqParser::cmdNote
B4 JASSeqParser::cmdSetLastNote
B5 NULL
B6 NULL
B7 NULL
B8 JASSeqParser::cmdParamE
B9 JASSeqParser::cmdParamI
BA JASSeqParser::cmdParamEI
BB JASSeqParser::cmdParamII
BC NULL
BD NULL
BE NULL
BF NULL
C0 NULL
C1 JASSeqParser::cmdOpenTrack {OpCode[1]} {TrackNumber[1]} {Pointer[3]}
OpCode: one byte, C1.
TrackNumber: one byte with range 0x00 through 0x0F. 0x00 = Track 0, 0x0F = Track 15.
Pointer: three bytes with range 0x000000 through 0xFFFFFF. Defines an offset in the file.
C1 04 05 B4 73

C1: OpCode
04: Track 4
05 B4 73: Offset
Creates a Track which can play Notes. Track 15 is usually reserved for Yoshi Drums.
C2 JASSeqParser::cmdCloseTrack
C3 JASSeqParser::cmdCall {OpCode[1]} {Pointer[3]}
OpCode: one byte, C3.
Pointer: three bytes with range 0x000000 through 0xFFFFFF. Defines an offset in the file.
C3 00 00 40

C3: OpCode
00 00 40: Offset
Redirects execution to the defined offset. Execution can return with C5.
C4 JASSeqParser::cmdCallF
C5 JASSeqParser::cmdRet {OpCode[1]}
OpCode: one byte, C5.
Returns redirected execution (like from C3) to the initial call.
C6 JASSeqParser::cmdRetF
C7 JASSeqParser::cmdJmp {OpCode[1]} {Pointer[3]}
OpCode: one byte, C7.
Pointer: three bytes with range 0x000000 through 0xFFFFFF. Defines an offset in the file.
C7 08 02 34

C7: OpCode
08 02 34: Offset
Redirects execution to the defined offset, used for Loops.
C8 JASSeqParser::cmdJmpF
C9 JASSeqParser::cmdJmpTable
CA JASSeqParser::cmdCallTable
CB JASSeqParser::cmdLoopS
CC JASSeqParser::cmdLoopE
CD NULL
CE NULL
CF NULL
D0 JASSeqParser::cmdReadPort
D1 JASSeqParser::cmdWritePort
D2 JASSeqParser::cmdCheckPortImport
D3 JASSeqParser::cmdCheckPortExport
D4 JASSeqParser::cmdParentWritePort
D5 JASSeqParser::cmdChildWritePort
D6 JASSeqParser::cmdParentReadPort
D7 JASSeqParser::cmdChildReadPort
D8 JASSeqParser::cmdRegLoad {OpCode[1]} {Setting[1]} {Unknown[1]} {Variable[1]}
OpCode: one byte, D8.
Setting: one byte with unknown range. Different bytes will trigger different effects:
- 0x62: set PPQN (seen in BMS)
- 0x6B: set PPQN (seen in SC)
Unknown: one byte with unknown range and purpose.
Variable: one byte with different range and purpose depending on the Setting byte.
D8 62 00 78

D8: OpCode
62: set PPQN
00: Unknown
78: PPQN 120
Different purposes.
D9 JASSeqParser::cmdReg
DA JASSeqParser::cmdReg
DB JASSeqParser::cmdRegUni
DC JASSeqParser::cmdRegTblLoad
DD NULL
DE NULL
DF NULL
E0 JASSeqParser::cmdTempo {OpCode[1]} {Unknown[1]} {BPM[1]}
OpCode: one byte, E0.
Unknown: one byte with unknown range and purpose.
BPM: one byte with range 0x00 through 0xFF. 0x78 = 120, F0 = 240.
E0 00 90

E0: OpCode
00: Unknown
90: BPM 144
Defines the tempo (playback speed) of the BMS file.
E1 JASSeqParser::cmdBankPrg {OpCode[1]} {Bank[1]} {Program[1]}
OpCode: one byte, E1.
Bank: one byte with range 0x00 through 0xFF. This byte represents a WSYS ID.
Program: one byte ranging from 0x00 through 0xFF. This byte represents a LIST entry in the IBNK belonging to the defined WSYS ID.
E1 01 0F

E1: OpCode
01: WSYS ID 1
0F: IBNK LIST entry 15
(01 0F is the percussion set for Yoshi Drums)
Defines the Instrument to be used in a Track.
E2 JASSeqParser::cmdBank {OpCode[1]} {Bank[1]}
OpCode: one byte, E2.
Bank: one byte with range 0x00 through 0xFF. This byte represents a WSYS ID.
E2 53

E2: OpCode
53: WSYS ID 83
(This bank holds sounds for Megahammer)
Defines the WSYS/IBNK whose sounds are to be used in a Track.
E3 JASSeqParser::cmdPrg {OpCode[1]} {Program[1]}
OpCode: one byte, E3.
Program: one byte with range 0x00 through 0xFF. This byte represents a LIST entry in the IBNK belonging to the defined WSYS ID.
E3 0B

E3: OpCode
0B: IBNK LIST entry 11
(In relation to Megahammer, this Program holds movement related sounds)
Defines the Keyboard which holds more specific sounds to an IBNK.
E4 NULL
E5 NULL
E6 NULL
E7 JASSeqParser::cmdEnvScaleSet
E8 JASSeqParser::cmdEnvSet
E9 JASSeqParser::cmdSimpleADSR
EA JASSeqParser::cmdBusConnect {OpCode[1]} {Unknown[1]} {Unknown[1]} {Unknown[1]}
OpCode: one byte, EA.
Unknown: one byte with unknown range and purpose.
EA 00 FF FF

EA: OpCode
00: Unknown
FF: Unknown
FF: Unknown
Unknown purpose, only seen in SC.
EB JASSeqParser::cmdIIRCutOff
EC JASSeqParser::cmdIIRSet
ED JASSeqParser::cmdFIRSet
EE NULL
EF NULL
F0 JASSeqParser::cmdWait {OpCode[1]} {WaitTime[n]}
OpCode: one byte, 0xF0.
WaitTime: one or more bytes with range 0x00 through 0xFF. If the leading byte is 0x80 (bit 7 = 1) or greater, another following byte will be taken into account. Time is measured in ticks here.
F0 60 (96 Ticks)
F0 81 56 (214 Ticks)
F0 83 81 30 (49328 Ticks)
Creates a delay, used to define the length of a Note or gap between Notes.
F1 JASSeqParser::cmdWaitByte
F2 NULL
F3 JASSeqParser::cmdSetIntTable
F4 JASSeqParser::cmdSetInterrupt
F5 JASSeqParser::cmdDisInterrupt
F6 JASSeqParser::cmdRetI
F7 JASSeqParser::cmdClrI
F8 JASSeqParser::cmdIntTimer
F9 JASSeqParser::cmdSyncCPU
FA NULL
FB NULL
FC NULL
FD JASSeqParser::cmdPrintf {OpCode[1]} {Message[n]} {NullTerminator[1]}
OpCode: one byte, FD.
Message: ASCII string of undefined length.
NullTerminator: one byte, 00.
FD 48 65 6C 6C 6F 00

FD: OpCode
48 65 6C 6C 6F: "Hello"
00: Null-terminator
Prints a specified message to OSReport.
FE JASSeqParser::cmdNop
FF JASSeqParser::cmdFinish {OpCode[1]}
OpCode: one byte, 0xFF.
Stops execution of a Track.
B000 NULL
B001 JASSeqParser::cmdDump

Tools

BMS_DEC (converts BMS to MIDI)
JaiSeqX (playback for JAudio1 and JAudio2 BMS files)