TBSA Music Format

From ModdingWiki
Jump to navigation Jump to search
TBSA Music Format
Format typeMusic
Notation typeTracked
InstrumentsOPL
Max channel count11?
Max track count1
Max pattern countOnly limited by 64k offset pointer
Max order countOnly limited by 64k offset pointer
Tags?None
Games

The TBSA Music Format is the song format produced by The Bone Shaker Architect written by Mario Knezovic.

File format

Data type Name Description
char[8] sig Signature: "TBSA0.01"
UINT16LE offOrderPtrs Offset of order pointer list
UINT16LE offUnknown2 Offset of unknown list, only has one element which is 0xFFFF list terminator
UINT16LE offUnknown3 Offset of unknown list, only has one element which is 0xFFFF list terminator
UINT16LE offUnknown4 Offset of unknown list, only has one element which is 0xFFFF list terminator
UINT16LE offInstPtrs Offset of instrument pointer list
UINT16LE offPattPtrs Offset of pattern-segment pointer list

Order Lists

At the offset given by offOrderPtrs in the header, this structure is present:

Data type Name Description
UINT16LE[] offOrderList Offset of order list(s)
UINT16LE end 0xFFFF

In other words, this is simply a list of UINT16LE values, terminated by the value 0xFFFF. Each value (offOrderList) is the offset (again relative to the start of the file) where that order list is located.

There is typically only one order list in a song.

Orders

At each offOrderList offset, the data for that order list is arranged in the following structure:

Data type Name Description
UINT8 count Number of entries in the list
UINT8 unknown ! Unknown value
UINT16LE[count] orderPointers List of offsets where each order list starts

A "pattern segment" is the event list for one track within one pattern.

At each offset given by orderPointers, there is a UINT8 array which serves as a list of pattern segment indices, e.g. 0x01 for the second pattern segment in the file. 0xFE terminates this list. The first list (located at offset orderPointers[0]) is the pattern segments to play for the first track, the second list (at orderPointers[1]) is the segments to play for the second track, and so on.

This means to read the first pattern, the first byte from each list will need to be read. This will provide a list of pattern segments, each representing one track for the pattern. If one of these pattern segment indices is 0x00, then it refers to the first pattern segment in the whole file. See #Pattern segments below for how to find a pattern segment.

Instruments

At the offset given by offInstPtrs in the header, there is simply a list of UINT16LE values, each of which is the offset (relative to the start of the file) where the information for that instrument can be found. The list is terminated with a single 0xFFFF value.

At each of these offsets, the data for that instrument is arranged in the following structure:

Data type Name Description OPL base register Value range
UINT8 attackdecay Attack + decay rate 0x60
UINT8 sustrel Sustain level + release rate 0x80
UINT8 sustain Sustain on/off 0x20 (bit 5) bit 0 controls on/off, bits 1-7 are ignored
UINT8 ksr KSR on/off 0x20 (bit 4) bit 0 sets value, bits 1-3 must be 0 as bits 0-3 are actually written into register bits 4-7
UINT8 freqmul Frequency multiplication factor 0x20 (bits 0-3) bits 0-3 go here, bits 4-7 must be 0 as the whole byte is written directly to the register
UINT8 feedback Feedback modulation factor 0xC0 (bits 1-3) bits 0-2 go here, bits 4-7 todo
UINT8 vibrato Vibrato on/off 0x20 (bit 6) bit 0 controls on/off, bits 1-7 are ignored
UINT8 defVolume Default volume 0x40 (bits 0-5) Add 2 to this value before writing to OPL register
UINT8 ksl Key scale level 0x40 (bits 6-7) bits 0-1 go here, bits 2-7 are ignored
UINT8 tremolo Tremolo on/off 0x20 (bit 7) bit 0 controls on/off, bits 1-7 are ignored
UINT8 con Connection 0xC0 (bit 0) bit 0 controls on/off, bits 1-7 must be 0 as the whole byte is written directly to the register
UINT8 wavesel Waveform select 0xE0 (bits 0-2) bits 0-2 control value, bits 3-7 must be 0 as the whole byte is written directly to the register
UINT8 attackdecay Attack + decay rate 0x63
UINT8 sustrel Sustain level + release rate 0x83
UINT8 sustain Sustain on/off 0x23 (bit 5) bit 0 controls on/off, bits 1-7 are ignored
UINT8 ksr KSR on/off 0x23 (bit 4) bit 0 controls on/off, but actually bits 0-3 are written into register bits 4-7, so this can be used to control OP1 vibrato etc. for which there are no dedicated fields
UINT8 freqmul Frequency multiplication factor 0x23 (bits 0-3) bits 0-3 go here, bits 4-7 must be 0 as the whole byte is written directly to the register (providing another way of setting OP1 vibrato etc.)
UINT8 defVolume Default volume 0x43 (bits 0-5) Add 2 to this value before writing to OPL register
UINT8 ksl Key scale level 0x43 (bits 6-7) bits 0-1 go here, bits 2-7 are ignored
UINT8 wavesel Waveform select 0xE3 (bits 0-2) bits 0-2 control value, bits 3-7 must be 0 as the whole byte is written directly to the register

Note that the instrument settings include both OPL operators (the carrier and the modulator), however if an instrument is played on an OPL rhythm-mode channel, care must be taken for the carrier-only instruments as these have their settings loaded from the modulator fields in the file. In other words:

Instrument type Destination registers for
modulator fields
Destination registers for
carrier fields
Normal OPL instrument modulator carrier
Rhythm bass drum modulator carrier
Rhythm snare carrier N/A
Rhythm tom tom modulator N/A
Rhythm top cymbal carrier N/A
Rhythm hihat modulator N/A

In the above table, the registers ending in 0 (e.g. 0x20) are the fields for the modulator, and the registers ending in 3 (e.g. 0x23) are the fields for the carrier.

Pattern segments

A pattern segment is the musical notation for one track/channel in a pattern. Multiple pattern segments will need to be combined (one for each track/channel) to produce a full pattern. The way in which the segments combine is given in the order lists, described above.

The first six tracks in a pattern (numbered 0-5 here) are played on normal OPL channels (e.g. channels 0-5), with the remaining five tracks (here numbered 6-10) being played as OPL percussive instruments. All songs are played this way in OPL percussive mode. Track 6 is the rhythm-mode bass drum, track 7 is the snare drum, 8 is the tom tom, 9 is the top cymbal and 10 is the hi-hat. Remember that instruments played on tracks 7 and 9 (snare + cymbal) only use the OPL carrier operator, but these registers need to be populated from the modulator fields in the file. Those same instruments can be used on other channels where the modulator and carrier are loaded normally, however.

At the offset given by offPattPtrs in the header, there is simply a list of UINT16LE values, each of which is the offset (relative to the start of the file) where the data for that pattern segment can be found. The list is terminated with a single 0xFFFF value.

The pattern segments themselves are a series of UINT8 bytes, with each byte having the following split:

7 6 5 4 3 2 1 0
Command Value
"fullbyte"

Here fullbyte means the raw byte value from the file, while command and value refer to bits within the same byte. This is required as some events refer to just the bits while others refer to the whole byte. Although the table above has two rows, there is only one byte and both rows refer to the same byte.

Commands are as follows:

Value (hex) Description
0-2 Note on, fullbyte is the note number. Middle C is 0x30. Add 12 to this value to get standard MIDI note numbers.
3 ! Unknown
4 Set instrument for future notes, value is instrument number (0=first instrument)
5 Set small row increment for future notes, value+1 is increment (0=one row, 1=two rows, ..., 31=32 rows)
6 Set large row increment for future notes, value+33 is increment (0=33 rows, 1=34 rows, ..., 31=64 rows) - 31 is used for a blank track in a pattern
7 Extended command, see below

Extended commands: (value in table is of fullbyte)

Value (hex) Description
E0-F4 No effect? Seems to count as a note for delay/row-advancement purposes
F4-FC Shift pitch down for future events ! By how much? 0xFC is a little, 0xF4 is more. Seems the 9 values might each be 1/10th of a quarter of a semitone. Seems NOT to count as a note for delay/row-advancement purposes. ! Confirm
FD xx Set volume for future notes to xx (the byte following this event). Byte range is 0 (silent) to 127 (loudest). The scale is not linear, it is exponential/logarithmic. ! Which one? Is there a formula?. Does NOT counts as a note for delay/row-advancement purposes.
FE Note off, causes a delay of the last row-increment number of rows (same type of delay as a note-on event)
FF End of track. Appears as a terminating byte at the end of each pattern segment. Each pattern segment that makes up a pattern should be the same number of rows (however, possibly due to a shortcut in the player code, placing this byte in the middle of a pattern segment will cause that track to jump to the next pattern while the other tracks are still playing the current pattern. This behaviour is probably unintentional and should not be relied upon.) All pattern segments in a given pattern should be the same number of rows in length, but they may be less than the usual 64 rows (for example the Doofus song for episode 2 only has 63 rows per pattern). Camoto handles this by adding a pattern-break event if the largest pattern segment in a given pattern is less than 64 rows. This event does NOT count as a row for delay/row-advancement purposes.

Note on events occur at regular intervals, similar to rows in a .mod file. The increment is adjusted (with command 5) so that instead of having delays between each note, adjacent notes can simply be a set number of rows apart.

Tools

The following tools are able to work with files in this format.

Name PlatformPlay? Create new? Modify? Convert/export to other? Import from other? Access hidden data? Edit metadata? Notes
Camoto Linux/WinYesYesYesYesNoN/AN/A

Credits

This file format was reverse engineered by Malvineous. If you find this information helpful in a project you're working on, please give credit where credit is due. (A link back to this wiki would be nice too!)