Overview
One channel plays one sample; a one-shot fires it once. Music is the next step up: all four channels at once, each handed a fresh note on a regular beat. A tracker is little more than that — a table of notes per channel, a timer that steps through it, and the period table turning note numbers into Paula periods. Set the four channels running, and on every tick repoint whichever ones have a new note. That loop is the beating heart of the MOD format and every Amiga music routine built on it.
Code
; =============================================================================
; FOUR-CHANNEL MUSIC - AMIGA (Paula)
; A vertical-blank tick steps a pattern; each channel gets its note's sample+period
; Assumes master interrupts on, and the level-3 (VERTB) autovector -> music_tick
; =============================================================================
CUSTOM equ $dff000
DMACON equ $096
AUD0LC equ $0a0 ; channel n block is at $a0 + n*$10
AUDLEN equ $04 ; +$4 length (words)
AUDPER equ $06 ; +$6 period (pitch)
AUDVOL equ $08 ; +$8 volume
ALL4DMA equ $820f ; SET | DMAEN | AUD0..3 ($8000|$200|$f)
ROWS equ 64 ; a classic tracker pattern is 64 rows
section code,code_c
; --- Kick every channel off once; volumes up, DMA on for all four ---
music_init:
lea CUSTOM,a5
move.w #64,AUD0VOL(a5)
move.w #64,$10+AUDVOL(a5) ; ch1
move.w #64,$20+AUDVOL(a5) ; ch2
move.w #64,$30+AUDVOL(a5) ; ch3
clr.w row
move.w #ALL4DMA,DMACON(a5) ; all four channels live
rts
; --- VERTB tick: advance one row, feed each channel that has a new note ---
music_tick:
lea CUSTOM,a5
move.w row,d7
; four channels: base offset d6 = 0,$10,$20,$30 ; data ptr a0
lea pattern,a0 ; row-major: 4 notes per row
mulu #4*2,d7 ; each note is a word
adda.w d7,a0 ; a0 -> this row's four notes
moveq #0,d6 ; channel base offset
moveq #4-1,d5 ; channel counter
.chan:
move.w (a0)+,d0 ; note number for this channel
beq.s .next ; 0 = "hold" — leave it playing
add.w d0,d0 ; note*2 -> period-table index
lea periods,a1
move.w -2(a1,d0.w),d1 ; period for this note
lea 0(a5,d6.w),a2 ; a2 -> this channel's registers
move.l #inst,AUD0LC-CUSTOM(a2) ; same one-cycle instrument...
move.w #inst_len,AUDLEN(a2) ; ...for every voice here
move.w d1,AUDPER(a2) ; pitch = period from the table
.next:
add.w #$10,d6 ; next channel's register block
dbf d5,.chan
addq.w #1,row ; step the pattern
cmp.w #ROWS,row
blo.s .done
clr.w row ; loop back to row 0
.done:
rts
section data,data_c
even
row: dc.w 0
; period table: one entry per note (C-1..). 428 = C-3; here just a few notes.
periods: dc.w 856,808,762,720,678,640,604,570,538,508,480,453 ; octave 2
dc.w 428,404,381,360,339,320,302,285,269,254,240,226 ; octave 3
; pattern: ROWS rows x 4 channels, note number or 0=hold (one row shown)
pattern: dc.w 13, 0,20, 0 ; row 0: ch0 C-3, ch2 G-3
; ... 63 more rows ...
inst: dc.b 0,60,110,127,100,50,-10,-70,-120,-127,-90,-40,10,55,30,0
inst_len equ (*-inst)/2
Trade-offs
| Aspect | Cost |
|---|---|
| CPU | One tick a frame — cheap; the mixing is Paula’s, not yours |
| Memory | Samples + pattern + period table, all in chip RAM |
| Limitation | Four voices, full stop — more needs software mixing into fewer channels |
When to use: Any in-game music or jingle. This is how Amiga music works.
When to avoid: A single sound effect — that’s a one-shot, not a sequencer.
The tick is the tempo
Trackers keep time in ticks, not seconds: the routine runs once per vertical blank (50 Hz PAL), and a “speed” value says how many ticks make one row — speed 6 is the classic default, giving 50/6 ≈ 8.3 rows a second. Most ticks do nothing but run effects; only every speed-th one steps to the next row. The skeleton above steps every frame for clarity; a real player counts down a speed counter first, and that same per-tick call is where vibrato, slides and volume-ramps live.
Notes become periods
A channel doesn’t understand “C-3” — it understands a period. The bridge is the period table: notes are numbered in semitones, the note number indexes the table, and the entry is the Paula period for that pitch. It’s why the table above steps by the twelfth root of two each row (856 → 808 → 762 …), each value about 94% of the last — one equal-tempered semitone. Standard MOD replay tables cover three octaves this way; transpose by adding or subtracting twelve from the note index.
Why four, and the stereo pairing
Paula gives four hardware voices and no more, wired 0 and 3 left, 1 and 2 right. The four-channel MOD grew straight out of that hardware: two voices a side, typically bass and a lead against a drum and a counter-melody. Wanting a fifth voice means software mixing — summing several samples into one buffer on the CPU and feeding that to a single channel — which is exactly how later “multichannel” formats and the 14-bit playback tricks got more out of the same four DACs.
From here to a real player
This skeleton holds the shape; a full MOD player adds three things on top:
- Instruments — a sample per note-row entry, not one shared
inst; the row carries a sample number as well as a note. - Effects — the second word per note in the MOD format: arpeggio, portamento, vibrato, volume slides, pattern jumps — all applied on the non-row ticks.
- Order list — patterns are sequenced by an order table, so a song reuses a handful of 64-row patterns rather than storing every bar.
Add those and the one-shot interrupt for one-shot instruments, and you have ProTracker.
Related
Patterns: One-Shot Samples, Playing a Sample
Vault: Paula | Commodore Amiga