|
libmsx
C library for MSX
|
LA0 is a file/data format and its decoder developed for use with "the libmsx audio replayer".
This article describes the LA0 file/data format in detail.
First, the "data format notation" used in this article will be explained, followed by a description of the LA0 file/data format using that notation.
Here is the notation for defining our data format.
This notation itself is described in BNF notation.
The following table shows the type notation.
This is similar to type annotation in the Rust programming language.
| type | explanation |
|---|---|
| i⁠N | N bits signed integer |
| u⁠N | N bits unsigned integer |
| [T;N] | N elements array of type T |
Note that the byte order of multi-byte integer types such as u16, u32, etc. is little-endian.
The optional SCC_WAVE_TABLE contains one or more 32-byte waveforms (up to 256 entries).
These are refered by index, as operand of the command CMD = 0xfa..0xfe.
See also DICTIONARY section for detail of that command.
The decoder shall track the position of samples in the DICTIONARY. The position is initialized to the head of the DICTIONARY when a song begins.
A pair of length and offset; encoded into the SEQUENCE_BODY; represents length of the next sample and offset from the end of the latest sample to the head of the next sample in the DICTIONARY. The decoder tracks the current position by adding the offset for each sample.
Each length and offset is in 2 byte unit. So if the decoded length was $m$ and offset was $n$, the actual value in bytes are $2m$ and $2n$.
The length and offset are encoded as Elias-Gamma code.
However, Elias-Gamma code encodes a positive integer into a bit-string.
So the non-negative integer length $2m$ in bytes and the signed integer offset $2n$ in bytes are encoded to the EG_LENGTH and EG_OFFSET as follows:
$$ \begin{align} EG_{LENGTH} &= EG(m+1) & (m \geqq 0) \end{align} $$
$$ \begin{align} EG_{OFFSET} &= \begin{cases} EG(|2n|+1) & if \ n \geqq 0 \ EG(|2n|) & if \ n \lt 0 \end{cases} \ \end{align} $$
$$ \begin{align} EG_{ZERO} &= EG(1) \ \end{align} $$
where
EG_LENGTH,EG_OFFSET,EG_ZERO,If the length was 0, EG_LENGTH will be EG_ZERO and EG_OFFSET will be omitted.
Finally, if the length of SEQUENCE_BODY is not a multiple of 8 bits, some 0 bits are padded. Therefore, the size of SEQUENCE_BODY is expressed in bytes.
DICTIONARY is concatnated unique / dedicated SAMPLEs.
A SAMPLE is series of CMD and val pair.
The below table shows CMDs:
| CMD | val | explanation |
|---|---|---|
| 0x00..0x1f | value of register | Set to SCC/SCC+ ch.1 Waveform registers. |
| 0x20..0x3f | ↑ | Set to SCC/SCC+ ch.2 Waveform registers. |
| 0x40..0x5f | ↑ | Set to SCC/SCC+ ch.3 Waveform registers. |
| 0x60..0x7f | ↑ | Set to SCC/SCC+ ch.4 Waveform registers. |
| 0x80..0x9f | ↑ | Set to SCC+ ch.5 Waveform registers. |
| 0xa0..0xa1 | ↑ | Set to SCC/SCC+ ch.1 FDR_LO, FDR_HI register. |
| 0xa2..0xa3 | ↑ | Set to SCC/SCC+ ch.2 FDR_LO, FDR_HI register. |
| 0xa4..0xa5 | ↑ | Set to SCC/SCC+ ch.3 FDR_LO, FDR_HI register. |
| 0xa6..0xa7 | ↑ | Set to SCC/SCC+ ch.4 FDR_LO, FDR_HI register. |
| 0xa8..0xa9 | ↑ | Set to SCC/SCC+ ch.5 FDR_LO, FDR_HI register. |
| 0xaa | ↑ | Set to SCC/SCC+ ch.1 Volume register. |
| 0xab | ↑ | Set to SCC/SCC+ ch.2 Volume register. |
| 0xac | ↑ | Set to SCC/SCC+ ch.3 Volume register. |
| 0xad | ↑ | Set to SCC/SCC+ ch.4 Volume register. |
| 0xae | ↑ | Set to SCC/SCC+ ch.5 Volume register. |
| 0xaf | ↑ | Set to SCC/SCC+ Channel mask register. |
| ---------— | ----------------— | --------------------------------------------------------------— |
| 0xb0..0xb1 | value of register | Set to PSG ch.1 FDR_LO, FDR_HI register. |
| 0xb2..0xb3 | ↑ | Set to PSG ch.2 FDR_LO, FDR_HI register. |
| 0xb4..0xb5 | ↑ | Set to PSG ch.3 FDR_LO, FDR_HI register. |
| 0xb6 | ↑ | Set to PSG Noise FDR register. |
| 0xb7 | ↑ | Set to PSG Mixer register. |
| 0xb8 | ↑ | Set to PSG ch.1 Volume register. |
| 0xb9 | ↑ | Set to PSG ch.2 Volume register. |
| 0xba | ↑ | Set to PSG ch.3 Volume register. |
| 0xbb..0xbc | ↑ | Set to PSG H/W envelope FDR_LO, FDR_HI register. |
| 0xbd | ↑ | Set to PSG H/W envelope number register. |
| 0xbe | (n/a) | (reserved) |
| 0xbf | (n/a) | (reserved) |
| ---------— | ----------------— | --------------------------------------------------------------— |
| 0xc0..0xc7 | value of register | Set to OPLL $00..$07 register. |
| 0xc8..0xcd | (n/a) | (reserved) |
| 0xce | value of register | Set to OPLL $0e register. |
| 0xcf | (n/a) | (reserved) |
| 0xd0..0xd8 | value of register | Set to OPLL $10..$18 register. |
| 0xd9..0xdf | (n/a) | (reserved) |
| 0xe0..0xe8 | value of register | Set to OPLL $20..$28 register. |
| 0xe9..0xef | (n/a) | (reserved) |
| 0xf0..0xf8 | value of register | Set to OPLL $30..$38 register. |
| 0xf9 | (n/a) | (reserved) |
| ---------— | ----------------— | --------------------------------------------------------------— |
| 0xfa | index # | Copy from SCC_WAVE_TABLE to SCC/SCC+ ch.1 Waveform registers. |
| 0xfb | ↑ | Copy from SCC_WAVE_TABLE to SCC/SCC+ ch.2 Waveform registers. |
| 0xfc | ↑ | Copy from SCC_WAVE_TABLE to SCC/SCC+ ch.3 Waveform registers. |
| 0xfd | ↑ | Copy from SCC_WAVE_TABLE to SCC/SCC+ ch.4 Waveform registers. |
| 0xfe | ↑ | Copy from SCC_WAVE_TABLE to SCC+ ch.5 Waveform registers. |
| ---------— | ----------------— | --------------------------------------------------------------— |
| 0xff | (n/a) | (reserved) |