The data chunk is a list of events, one after another with nothing between them. Each event is one ID byte and a value. The ID says what the value means and also how big it is, so a reader can step over any event, including ones it does not recognise.
Size classes
The ID byte falls into one of five ranges. The range sets the size of the value.
| IDs | Class | Value |
|---|---|---|
| 0x00 (0) to 0x3F (63) | Byte | 1 byte |
| 0x40 (64) to 0x7F (127) | Word | 2 bytes, little-endian |
| 0x80 (128) to 0xBF (191) | Dword | 4 bytes, little-endian |
| 0xC0 (192) to 0xCF (207) | Text | a variable-length size, then that many bytes of text |
| 0xD0 (208) to 0xFF (255) | Data | a variable-length size, then that many bytes of data |
Some IDs in the data range hold text, because they were added after the text range was full: 231 (display group name, from version 3.4) and, in later files, 239 (track name). Whether these versions write 239 is not recorded. They are stored exactly like data events, so the size rule still holds.
Fixed-size events
Byte, word and dword events are the ID followed straight away by the value.
09 01 loop active: 1
40 00 00 new channel: index 0
9C E0 22 02 00 tempo: 140000, which is 140 BPM
Variable-length size
Text and data events give their size as a variable-length number between the ID and the bytes. Each byte carries seven bits of the number, lowest bits first. The top bit of a byte is set when another byte follows. Sizes under 128 take one byte.
300 in binary = 10 0101100
low seven bits = 0101100 = 0x2C, more follows → 0x2C + 0x80 = 0xAC
next seven bits = 10 = 0x02, last byte → 0x02
stored as = AC 02
To read a size, start at zero and a shift of zero. Take a byte, add its low seven bits shifted left by the shift, and add seven to the shift. Stop after a byte whose top bit is clear.
Text
Text events hold a string followed by a terminating zero. In these versions every string, the FL Studio version (event 199) included, is single-byte text ending in one zero byte. PyFLP decodes it as ASCII; the flpkit reader decodes it as Latin-1, which also covers accented letters. The exact code page FL Studio used for characters outside ASCII is not documented.
C2 05 53 6F 6E 67 00 Song
Files from version 11.5 on switch every string except the version to UTF-16LE, so if text events here hold a zero byte after every Latin character, the file is newer than its version string suggests.
How events are grouped
The stream has no nesting. Items such as channels and patterns are built from runs of events: a New event opens an item, and the events after it set that item’s properties until the next New event of the same kind.
| Event | ID | Opens |
|---|---|---|
| Channel New | 0x40 (64) | a channel in the channel rack; the value is its index |
| Pattern New | 0x41 (65) | a pattern; the value is its index |
| Slot Index | 0x62 (98) | an effect slot in a mixer insert |
Each pattern’s New event appears twice in a project, so one pattern’s events can come in two separate runs. Join them by the pattern index. Mixer inserts have no New event; their events appear insert by insert, in mixer order. Plugin events follow the channel or slot that holds the plugin.
Project events, such as the tempo and the title, can appear anywhere in the stream and always belong to the project as a whole. The event reference lists every known event by the item it belongs to.