Adlib sound effect
Format type | Sound |
---|---|
Hardware | OPL |
Number of sounds | 1 |
Sampling rate | N/A |
Channel count | 1 |
Bits per sample | N/A |
Compressed? | No |
Tags? | None |
Games |
The Adlib sound effect format is used to store sound effects for the OPL chip on Adlib and SoundBlaster sound cards. The sound data is normally packaged up inside an AudioT file.
File format
Ignoring the various headers that are found inside the AudioT file, the actual sound data is in the following format.
Data type | Name | Description |
---|---|---|
UINT8[16] | instrument | The OPL register settings for the instrument |
UINT8 | octave | Octave to play notes at |
BYTE[...] | notes | Pitch data |
The instrument data is loaded into the registers below, which are for OPL channel #0. Any of the nine channels will work, however games using this format use channel #0, since the background music is written such that channel #0 is left free and will not interfere with the music.
The octave value is written into the block field of OPL register 0xB0. Since this register also holds other important fields (including the note on/off bit) care must be taken to ensure any writes to this register don't interfere with the other fields stored there. To calculate where the octave value should be written, the following formula can be used:
block = (octave & 7) << 2 regB0 = block | other_fields
Since bit 5 (0x20) of this register is the keyon field, whenever notes are played the value must be combined with the block or the octave will unexpectedly change.
regB0 = 0x20 // note on, and set octave to zero! regB0 = block | 0x20 // note on regB0 = block // note off (0x20 not set)
Instrument data
The data in the instrument field is arranged as follows.
Data type | Name | OPL register | Description |
---|---|---|---|
UINT8 | mChar | 0x20 | Modulator characteristics |
UINT8 | cChar | 0x23 | Carrier characteristics |
UINT8 | mScale | 0x40 | Modulator scale |
UINT8 | cScale | 0x43 | Carrier scale |
UINT8 | mAttack | 0x60 | Modulator attack/decay rate |
UINT8 | cAttack | 0x63 | Carrier attack/decay rate |
UINT8 | mSust | 0x80 | Modulator sustain |
UINT8 | cSust | 0x83 | Carrier sustain |
UINT8 | mWave | 0xE0 | Modulator waveform |
UINT8 | cWave | 0xE3 | Carrier waveform |
UINT8 | nConn | 0xC0 | Feedback/connection (usually ignored and set to 0) |
UINT8 | voice | - | unknown (Muse-only) |
UINT8 | mode | - | unknown (Muse-only) |
UINT8[3] | padding | - | Pad instrument definition up to 16 bytes |
Note:
The structure if the instrument data is identical to the instrument data structures in CMF Format, IBK Format and SBI Format files, except for the voice and mode settings, which are just unused padding in those files.
Pitch data
Each byte in the pitch data contains a single value to write to OPL register 0xA0. The data runs at a constant rate of 140 Hz, therefore the IMF music rate usually is a multiple of 140 Hz (known IMF rates are 280, 560 and 700 Hz), so that the adlib sound data can be sent to the OPL chip every two, four or five IMF cycles.
If a byte in the pitch data is zero, the note should be turned off, by disabling the keyon bit (bit 5, 0x20, in OPL register 0xB0. The other bits in register 0xB0 should be left unchanged, especially the block/octave.) When the next non-zero byte is read, the keyon bit should be set again (but probably after the byte has been written into register 0xA0 as per normal.)
In pseudocode it may look like this:
block = (octave & 7) << 2 note = off while (more data) { get next byte if next byte is 0x00 { setRegister(0xB0, block) note = off } else { setRegister(0xA0, next byte) if note is off { setRegister(0xB0, block | 0x20) note = on } } wait for next 140 Hz tick }
Note: This pseudocode is not entirely true to the playback routines used in the games! The games usually only update the OPL chip if the current byte value differs from the previous byte value, so that one long note is played instead of many short notes.