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 SAMPLE
s.
A SAMPLE
is series of CMD
and val
pair.
The below table shows CMD
s:
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) |