HSC Music Format
(HSC AdLib Composer / HSC Tracker, OPL2 Music Data Format)
Overview
The HSC file format stores music sequences composed for AdLib (OPL2) synthesizers. It contains 128 instruments, a pattern order list, and pattern data (note and effect pairs). Each HSC file represents a complete song that can be played by a 9-channel OPL2 synthesizer.
Section |
Description |
Size |
|---|---|---|
Instrument bank |
128 instruments × 12 bytes |
1536 bytes |
Order list |
Sequence of pattern indices (51 bytes typical) |
51 bytes |
Pattern data |
64 rows × 9 channels × 2 bytes per pattern |
1152 × N patterns |
File Layout Summary
Offset |
Size |
Description |
|---|---|---|
0x0000 |
1536 |
Instrument data (128 × 12 bytes) |
0x0600 |
51 |
Order list (pattern sequence) |
0x0633 |
… |
Pattern data (each 1152 bytes = 64 rows × 9 channels × 2 bytes) |
The number of patterns is derived as:
pattern_count = (file_size - 1587) / 1152
1. Instrument Block (1536 bytes)
Each instrument is 12 bytes and defines all OPL2 register parameters for one sound.
Byte |
Register Written |
Description |
|---|---|---|
0 |
0x23 + op |
Carrier: Tremolo/Vibrato/Sustain/KSR + Multiple |
1 |
0x20 + op |
Modulator: Tremolo/Vibrato/Sustain/KSR + Multiple |
2 |
0x43 + op |
Carrier Total Level (TL) / KSL — bit corrected |
3 |
0x40 + op |
Modulator Total Level (TL) / KSL — bit corrected |
4 |
0x63 + op |
Carrier Attack/Decay |
5 |
0x60 + op |
Modulator Attack/Decay |
6 |
0x83 + op |
Carrier Sustain/Release |
7 |
0x80 + op |
Modulator Sustain/Release |
8 |
0xC0 + chan |
Feedback + Connection |
9 |
0xE3 + op |
Carrier waveform select |
10 |
0xE0 + op |
Modulator waveform select |
11 |
(not written directly) |
Pitch slide base (upper nibble, normalized) |
A nibble is a group of 4 bits — exactly half a byte.
Bit Correction Details (ins[2], ins[3], ins[11])
The original HSC instrument format (as produced by early HSC Tracker/AdLib Composer versions) stored certain OPL2 register fields in a slightly inconsistent way compared to the actual AdLib chip layout. When AdPlug (and compatible players) load instruments, they apply the following fix-ups:
ins[2] ^= (ins[2] & 0x40) << 1 // adjust carrier TL/KSL bit overlap
ins[3] ^= (ins[3] & 0x40) << 1 // adjust modulator TL/KSL bit overlap
ins[11] >>= 4 // normalize pitch slide nibble
1. KSL/TL Bit Fix (ins[2] and ins[3])
Each operator in the OPL2 has a Total Level (TL) register:
Bits 0–5 → Volume level (0–63) Bits 6–7 → Key Scale Level (KSL)
In some HSC instrument banks, the bit 6 (value 0x40) — the lowest KSL bit — was misaligned due to the tracker’s internal byte packing. This caused the TL and KSL fields to overlap incorrectly: e.g., a KSL value of 1 might double the intended TL level.
The expression
ins[x] ^= (ins[x] & 0x40) << 1
effectively swaps the misplaced KSL bit into its proper position (bit 7). In other words, if bit 6 was set, bit 7 becomes set too, restoring the correct KSL value.
This ensures the written OPL registers (
0x40+opand0x43+op) receive the right 2-bit KSL field and a clean 6-bit TL value.
2. Pitch Slide Normalization (ins[11])
Byte 11 of each instrument stores a base frequency offset, used by the tracker’s pitch-slide effect.
The original HSC files encoded this offset in the upper nibble (bits 4–7) only. For example, an instrument might have
ins[11] = 0xA0, meaning a slide base of0x0A.AdPlug shifts it down with:
ins[11] >>= 4
so the value becomes a normal 4-bit integer (0–15) that can be directly added to the F-number during note playback.
Summary:
These adjustments correct for historical quirks in how the tracker saved instrument bytes.
They ensure that the reconstructed OPL register values match what the composer actually heard when saving the .HSC file — preserving tone and pitch consistency across players.
2. Order List (Song Sequence)
Description |
Notes |
|---|---|
Contains up to 51 bytes |
Each byte = pattern index |
|
End of song |
|
Goto pattern command (lower 7 bits = target) |
|
Invalid / treated as song end |
Out-of-range pattern indices |
Clamped to valid count (≤ 50) |
Pseudo-code to read:
orders = read(51)
for each entry:
if entry == 0xFF → end_of_song
else if entry >= 0x80 and entry <= 0xB1 → jump to entry & 0x7F
else if entry >= 0xB2 → treat as 0xFF
3. Pattern Data
Each pattern = 64 rows × 9 channels, where each cell is 2 bytes:
Byte |
Name |
Description |
|---|---|---|
1 |
|
Note number or instrument marker |
2 |
|
Effect or instrument number |
Pattern Size:
64 rows × 9 channels × 2 bytes = 1152 bytes
Note Cell Meaning
Condition |
Action |
|---|---|
|
No note change (effects may still apply) |
|
Set instrument → |
|
Play note: AdPlug decrements value by 1 before lookup |
Resulting |
Key-off (pause) |
4. Effects (by High Nibble of Effect Byte)
High Nibble |
Description |
Behavior |
|---|---|---|
|
Global control |
01: Pattern break |
|
Pitch slide down |
Decrease frequency by low nibble |
|
Pitch slide up |
Increase frequency by low nibble |
|
Set percussion instrument |
(Unused) |
|
Set feedback |
|
|
Set carrier volume |
TL of carrier = |
|
Set modulator volume |
TL of modulator = |
|
Set overall instrument volume |
Both ops’ TL set to |
|
Position jump |
Jump to order |
|
Set speed |
Speed = |
5. Timing & Playback
Parameter |
Description |
|---|---|
Refresh rate |
18.2 Hz (one tick per PIT IRQ0 interval) |
Speed |
Rows per tick counter ( |
Speed effect ( |
Sets |
Rows per pattern |
64 |
Channels |
9 total, or 6 melodic + 3 drums in rhythm mode |
Pseudo-logic per update tick:
if (--del == 0):
del = speed
advance row
else:
process effects only
6. Rhythm (6-Voice) Mode
Enabled by effect 05.
Channels 0–5 remain melodic, while channels 6–8 become drums:
Channel |
Drum |
Bit in 0xBD |
|---|---|---|
6 |
Bass Drum |
Bits 4–5 |
7 |
Hi-Hat |
Bit 0 |
8 |
Cymbal |
Bit 1 |
When rhythm mode is active, note events on channels 6–8 toggle these bits instead of normal note-on.
7. End-of-Song Behavior
When the order list hits 0xFF or an invalid pattern index:
songend = true
reset songpos to 0 (or stop playback)
If a pattern break (01 effect) occurs, the player skips remaining rows in the current pattern and proceeds to the next order.
8. Suggested Writer Implementation
When producing .HSC files programmatically:
Write 128 instruments — 12 bytes each (zeros allowed if not defined).
Write order list (51 bytes) — pattern numbers, ending with
0xFF.Write patterns — each 64×9×2 bytes.
Encode cells:
Set instrument:
note |= 0x80,effect = instrument_index.Play note:
note = pitch_number,effect = effect_byte.Empty cell:
note = 0,effect = 0.
Initial speed: place an
F0xxeffect on first row (typ.F006).Enable drums: issue
05(global) in a control channel.
9. Practical Limits
Property |
Limit |
|---|---|
Instruments |
128 |
Patterns |
50 |
Orders |
51 |
Channels |
9 |
Rows per pattern |
64 |
10. Example Pseudocode: Pattern Decoding
for each row in 64:
for each chan in 9:
note = read_byte()
eff = read_byte()
if note & 0x80:
instrument[chan] = eff
continue
if note == 0:
apply_effect(chan, eff)
continue
note = note - 1
if note == 0x7E:
key_off(chan)
continue
pitch = note_table[note % 12] + instrument[chan].pitchbase + slide[chan]
octave = ((note / 12) & 7) << 2
set_frequency(chan, pitch, octave)
apply_effect(chan, eff)
11. Implementation Notes
The fixed tick rate (18.2 Hz) matches the PC timer interrupt frequency.
Speed defines how many ticks per row; lower values = faster playback.
Instrument TL/KSL correction ensures compatibility with older HSC data banks.
Pattern arrays are capped at 50; exceeding this may cause truncation.
12. Reference
Specification derived from analysis of AdPlug’s hsc.h and hsc.cpp (licensed open source)
but rewritten as an independent, formal description for implementers of HSC readers/writers.