Even in 2021 Sounddiver keeps being a tool used to collect, edit and archive MIDI patches, loading them to various devices. There is other legacy software built around programming MIDI devices -such as Line6 Edit- that use Sounddiver LIB file formats.
This is an effort to reverse-engineer the Sounddiver LIB file format with the hope of writing open-source libraries for working with these LIB files.
Sounddiver LIB file is based on the IFF generic container file format withas a few key differences:
- it can be big-endian or little-endian ('FORM' and 'SSLB' type-ids appears as 'MROF' and 'BLSS' in the file);
- the chunks are not 16-bit word-aligned.
Chunk Type ID | Type | ID | # | Description |
---|---|---|---|---|
FORM | group | SSLB | 1 | The outer group chunk |
LHDR | data | 1 | Library header ??? | |
WSEQ | data | 1 | Library window layout (columns order, widths, parameters pane?) | |
LENT | data | * | Library entry chunk |
Integer. Integers can be 8-, 16- or 32-bit long. All integers longer than 8-bit are subject to the endianness of the system that created the LIB file. Both big-endian and little-endian LIB files have been observed and all long integers (IFF chunk length, LENT header date/time fields) need to be converted to native endianness.
String. Strings are encoded as Pascal strings (or UCSD strings). That is, first comes a byte specifying the legth of the string, which is followed by the actual string content.
FORM chunk is a generic container that should contain the LHDR/WSEQ/LENT data chunks. Sometimes Sounddiver 3.0.5 doesn't place LHDR/WSEQ/LENT data chunks inside the FORM chunk. Instead the FORM chunk is empty (type-id: FORM, length: 4, id: SSLB
) and the data chunks follow the FORM container chunk, which goes against the IFF standard.
Library window layout. Looks like it consists of 70 16-bit words. Content???
This is the actual library entry chunk containing program/tone/etc.
The layout of the LENT chunk goes as follows:
+--------+--------------+ - - + - - - - - - - +
| Header | Data block 1 | ... | Data block N |
+--------+--------------+ - - + - - - - - - - +
Where a 12-byte header is followed by any number of data blocks (1 or more?) of the following layout (with the exception of the "end of data" block with id=0x00):
Short block:
0
0 1 2 . . .
+-+-+-+- - - - -+
|I|L| D... |
+-+-+-+- - - - -+
Long block:
0
0 1 2 . . .
+-+-+-+- - - - -+
|I| L | D... |
+-+-+-+- - - - -+
Where:
Field | Bits | Name | Description |
---|---|---|---|
I | 8 | Id | Data block id |
L | 1/2x8 | Length | Data length |
D | Lx8 | Data | L bytes of data |
The length of the data block is encoded in 1 or 2 bytes. When the first byte (L1) is read, if the most significant bit (MSB) is 0, then the length is 1 byte long, the value as-is (L = L1); if MSB is 1, then length is 2 bytes long, the value calculated as L = (L1 & 0x7F) << 7 + L2.
The following data blocks are known:
Block id | Name | Description |
---|---|---|
0 | End of data | This id comes last. That id=0x00 can still be followed with 1 or 2 bytes of data. *Is there any signifficane to them? |
1 | Entry name | String. Block data length is the string length. |
2 | Location | String. Block data length is the string length. |
3 | Data | Data layout module-specific? |
4 | Uses | References to other entries used by this one |
5 | ? | ? |
6 | Comment | String. Block data length is the string length. |
NOTE: *Also observed in the wild are LENT chunks with size 2 and contents [0x00, 0x00].
Layout:
0 1
0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+
|X| M |?| D |?|?|N|?|
+-+-+-+-+-+-+-+-+-+-+-+-+
Where:
Offset | Field | Bits | Name | Description |
---|---|---|---|---|
0 | X | 8 | Entry type? | 0x80 if program, 0x00 if title ??? |
1 | M | 2x8 | Module/Model | Two 8-bit identifiers - module & model. See appendix A. |
3 | ? | 8 | ? | ? |
4 | D | 32 | Date/Time | 32-bit value: (MSB to LSB) 7 bits for year-1980 (0-127), 4 bits for month (1-12), 5 bits for day (1-31), 5 bits for hour (0-23), 6 bits for minutes (0-59), 5 bits for seconds/2 (0-29). NOTE: Sounddiver takes 2 digits for year input (80-99 = 1980-1999, 00-79 = 2000-2079), but it will read 7-bit year showing dates up to 2017-12-31. If the value is outside the ranges shown in brackets SD will not show date/time. |
8 | ? | 8 | ? | ? |
9 | ? | 8 | ? | ? |
10 | N | 8 | Device ID | 8-bit value, device id - 1. Sounddiver allows user to input 1-128, but will read 8-bit device id showing maximum device id of 256. |
11 | ? | 8 | ? | ? |
The uses block is a list of entries used by the current one. The list consists of 6 byte entries:
0
0 1 2 3 4 5
+-+-+-+-+-+-+
| S | E |
+-+-+-+-+-+-+
Where:
Offset | Field | Bits | Name | Description |
---|---|---|---|---|
0 | N | 16 | Slot | The number of the slot for this entry |
2 | E | 32 | Entry | The number of the LENT entry used in this slot |
If an entry uses 4 other entries, it will have 4 entries in the "uses" list, where slot is a sequential number from 0 to 3 and entry is the number (entry 0 referes to the first LENT chunk in the LIB file) of the entry used there.
Sounddiver crashes whenever it encounters an unknown module/model. The models which have been checked and are known to crash are marked as "~" in the table below.
Module | Model | Name |
---|---|---|
02 | 00 | SY77 / SY77 |
02 | 01 | SY77 / TG77 |
02 | ? | SY77 / SY99 |
09 | 00 | M1 / M1 |
0F | 00 | D50 / D-50 |
14 | 00 | JV / JV-80 |
29 | 00 | DMPRO / DM Pro |
2A | 00 | POD / POD |
01 | POD / Flextone II | |
02 | POD / POD Pro | |
03 | POD / POD 2.0 | |
04 | ~ | |
2B | 00 | FS1R / FS1R |
3B | 01 | BASSPOD / Bass POD |
3B | 02 | BASSPOD / Bass POD Pro |
03 | ~ | |
FF | xx | UNI |
UNI module seems to not care for that the model byte is, Sounddiver doesn't crash when it is changed. Intead, the actual model name is stored as a NUL-terminated string inside the data block of the LENT-chunk.