Skip to content

Four-Channel Music

Drive all four of Paula's channels from a note sequence, stepping the pattern on a timer interrupt and turning note numbers into periods — the skeleton of every Amiga tracker and MOD player.

paulamusictrackermodinterruptaudio

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.

Patterns: One-Shot Samples, Playing a Sample

Vault: Paula | Commodore Amiga