Skip to content

Playing a Sample

Point Paula's audio channel 0 at a waveform in chip RAM, choose a period and volume, and let DMA repeat it — then predict the pitch you will hear.

Code status: assembled and emulator-executed. A500 OCS PAL with Kickstart 1.3, cold boot from an ADF, in Emu198x. The recorded checks identify the tested sources and observations. They establish the stated cases on that configuration; original-hardware checks are separate.

Four registers and a switch

The Amiga has four audio channels. Each one has an 8-bit digital-to-analogue converter fed by DMA: the custom chips fetch sample words from chip RAM by themselves. To start a channel, we tell it where the sample is, how long it is, how fast to play it and how loud. Then we switch its DMA on. The channel then repeats the sample until we switch it off.

This example targets a PAL Amiga 500 with Kickstart 1.3, started from its own boot disk. It uses channel 0. To change that channel safely, the program has to own it. Here, it takes over the whole machine.

The routine’s contract

Before calling play_sample, put the custom-chip base $DFF000 in A5 and the sample’s address in A0. Put its length in words in D0, the period in D1 and the volume in D2. The routine writes the four channel registers, then sets the master and channel-0 bits in DMACON. It changes no registers.

paula-sample.inc
; =============================================================================
; PAULA SAMPLE - start or stop a looping sample on audio channel 0
;
; play_sample
;   In:  a5 = $DFF000 (custom chip base)
;        a0 = sample address: chip RAM, even (word-aligned)
;        d0 = length in WORDS, 1-65535 (two 8-bit signed samples per word)
;        d1 = period, colour clocks per sample: 123-65535 PAL (124 NTSC)
;        d2 = volume, 0-64
;   Out: channel 0 plays the sample from its start and repeats it until
;        stop_sample. No registers change.
;
; The caller owns channel 0, the DMACON audio and master bits, and channel
; 0's interrupt. Channel 0 DMA must already be off, as at power-on or after
; stop_sample followed by at least two sample periods.
; =============================================================================

DMACON          equ $096        ; DMA control (write)
AUD0LC          equ $0a0        ; Channel 0 location, AUD0LCH/AUD0LCL as one long
AUD0LEN         equ $0a4        ; Channel 0 length, in words
AUD0PER         equ $0a6        ; Channel 0 period
AUD0VOL         equ $0a8        ; Channel 0 volume

DMAF_SETCLR     equ $8000       ; Write 1s: set the other 1 bits, leave the rest
DMAF_MASTER     equ $0200       ; DMAEN: no DMA happens without it
DMAF_AUD0       equ $0001       ; AUD0EN: channel 0 DMA

play_sample:
            move.l  a0,AUD0LC(a5)       ; Copied to the back-up pointer at start
            move.w  d0,AUD0LEN(a5)      ; and the length, reloaded every pass
            move.w  d1,AUD0PER(a5)      ; Colour clocks between output samples
            move.w  d2,AUD0VOL(a5)      ; 64 = 0 dB, 32 = -6 dB, 0 = silent
            move.w  #DMAF_SETCLR|DMAF_MASTER|DMAF_AUD0,DMACON(a5)
            rts

; stop_sample - clear AUD0EN, leaving the other DMA bits alone.
;   In: a5 = $DFF000. Leave the channel off for more than two sample periods
;   before play_sample, or it may continue where it stopped.
stop_sample:
            move.w  #DMAF_AUD0,DMACON(a5)
            rts

The sample must be in chip RAM, starting on an even address. Audio DMA reads only chip RAM, and it fetches one word, two 8-bit signed samples, at a time. So the length is the number of bytes divided by two. Volume runs from 0 to 64: 64 is full level, and 32 is 6 dB quieter.

AUD0LC and AUD0LEN are read once at the start of each pass. When the length runs out, the channel goes back to the same location and plays the sample again. That repeat is the normal behaviour, not something we set up.

Channel 0 must be off when we call play_sample. A channel that is switched off for less than two sample periods may carry on where it stopped instead of starting again. The demo waits a whole frame between tones.

Period is not pitch

AUDxPER is the number of colour clocks between output samples. It is not a note. On a PAL machine the colour clock runs at 3,546,895 Hz, so the period sets the sample rate:

Code
sample rate (Hz) = 3546895 / period
tone (Hz)        = 3546895 / (period × samples per cycle)

The pitch we hear depends on how many samples make up one cycle of the waveform. Our 16-byte sine at period 424 plays 8,365 samples a second, which is 523 Hz. The demo plays three tones a second apart:

Waveform Period Sample rate Tone
16 bytes per cycle 424 8,365 Hz 522.8 Hz
16 bytes per cycle 212 16,731 Hz 1,045.7 Hz
32 bytes per cycle 212 16,731 Hz 522.8 Hz

Halving the period raises the tone by an octave. Doubling the samples per cycle at the same period brings it back down. The Hardware Reference Manual’s note table uses the same arrangement: with a 16-byte cycle, PAL period 212 gives a C at 1,045.7 Hz.

A recorded sound works the same way. Play it at the rate it was recorded and it sounds at its original pitch. A tracker’s note names are a separate convention: they depend on how each instrument sample was tuned.

The period has limits. Below 123 on PAL (124 on NTSC), DMA cannot fetch the next word in time, and the channel reuses old samples. The manual recommends 124–256 for single-cycle waveforms like these, using a different sample for each octave, to keep aliasing noise down.

A 32-byte single-cycle waveform plays at period 424. What tone should we hear?

Show the explanation

3546895 / (424 × 32) is about 261.4 Hz, an octave below the demo’s first tone. Doubling the samples per cycle halves the tone, just as doubling the period does.

What it costs

Each channel fetches at most one word per scan line, in one of four time slots set aside for audio in every line. That word holds two samples, so a channel can output at most two samples per line. The 68000 uses only the even-numbered bus cycles, and Abacus’s System Programmer’s Guide places the audio slots among the odd cycles reserved for DMA. So audio DMA does not take the processor’s time.

The verification check measures this in Emu198x. A polling loop running from chip RAM completed the same number of passes in ten frames with no DMA as with all four channels fetching at period 124. As a control, six low-resolution bitplanes cut the count by about a quarter. That is an emulator observation, not a measurement on hardware.

The CPU still does some work. It writes the registers to start each sound. Anything beyond a steady repeat needs more: changing sounds, stopping after one pass, or joining waveforms. That means responding to the channel’s interrupt. Paula can also be fed a word at a time without DMA, but the manual describes that as processor-intensive and does not recommend it.

Who owns channel 0

The demo begins like this:

demo.asm, excerpt
            ; --- Take over: no OS interrupts, no other DMA ---
            move.w  #$7fff,INTENA(a5)   ; Disable all interrupts
            move.w  #$7fff,INTREQ(a5)   ; Clear pending requests
            move.w  #$7fff,DMACON(a5)   ; Disable all DMA, audio included
            move.w  #$0035,COLOR00(a5)  ; Blue screen: the program is running

With every interrupt and DMA channel off, nothing else can change channel 0, its DMACON bit or its interrupt. The demo never returns to AmigaDOS. The manual’s own audio example makes the same assumption of exclusive access. It says that example will not work directly in a multitasking system.

A program that keeps the operating system running should instead allocate channels through audio.device, which shares them between programs, and use only the ones it is given.

Looping, and playing once

Each time the channel copies AUD0LC and AUD0LEN into its working registers, it requests the channel-0 audio interrupt (INTREQ bit 7). The first request comes as soon as DMA starts, before the first sample is heard. After that, a request comes as the last word of each pass starts playing, not at the start of the next pass. At each request, the values just copied are in use, so we can write the next location and length without disturbing the current pass.

The check measures this with the demo’s 16-byte sample at period 424. One pass is 8 words × 2 samples × 424 = 6,784 colour clocks. The first request arrived 164 colour clocks after the DMACON write. The second arrived 5,908 later, close to the 5,936 that seven words take. Later requests came about 6,784 apart, within one scan line.

To play a sound once, write a silent one-word sample’s location and a length of 1 when the first request arrives. The sample plays through, then the channel repeats silence. In the check, an eight-cycle burst played exactly eight cycles this way. One-Shot Samples builds this into an interrupt handler.

Left and right

Paula’s four channels are mixed onto two stereo outputs, two channels each. Primary sources disagree about which pair reaches which jack. The Hardware Reference Manual’s audio chapter gives one assignment, while its own connector description and Abacus’s guide give the reverse. Until a recording from a real machine settles it, this pattern makes no left/right claim.

Run the complete example

Assemble and master a boot disk with the maintained files side by side:

Shell
asm198x --dialect vasm --exe demo.asm -o demo
build198x adf demo -o demo.adf --volume Sample --name demo

Boot demo.adf on a PAL A500 configuration with Kickstart 1.3. After AmigaDOS starts the program, the screen turns blue and the three tones play for about a second each. The screen turns green when they finish. Amiga emulators take a few seconds to boot from disk.

demo.asm
; =============================================================================
; PLAYING A SAMPLE - AMIGA (Paula)
; Three one-second tones on channel 0, then silence.
;   1. 16-byte sine cycle, period 424: 3546895 / (424 * 16) = 522.8 Hz
;   2. the same 16 bytes, period 212:  1045.7 Hz, an octave higher
;   3. a 32-byte sine cycle, period 212: 522.8 Hz again
; Target: A500 PAL, Kickstart 1.3, started from the boot disk. The program
; takes over the whole machine and never returns to AmigaDOS.
; =============================================================================

CUSTOM          equ $dff000
INTREQR         equ $01e        ; Interrupt requests (read)
INTENA          equ $09a        ; Interrupt enable (write)
INTREQ          equ $09c        ; Interrupt requests (write)
COLOR00         equ $180
INTF_VERTB      equ $0020       ; Set by the display at the start of each frame

TONE_FRAMES     equ 50          ; One second per tone on PAL

            section code,code_c

start:
            lea     CUSTOM,a5

            ; --- Take over: no OS interrupts, no other DMA ---
            move.w  #$7fff,INTENA(a5)   ; Disable all interrupts
            move.w  #$7fff,INTREQ(a5)   ; Clear pending requests
            move.w  #$7fff,DMACON(a5)   ; Disable all DMA, audio included
            move.w  #$0035,COLOR00(a5)  ; Blue screen: the program is running

            ; --- 1. 16 samples per cycle at period 424 ---
            lea     wave16,a0
            moveq   #(wave16_end-wave16)/2,d0
            move.w  #424,d1
            moveq   #64,d2
            bsr     play_sample
            bsr     wait_tone

            ; --- 2. Same waveform, half the period: an octave up ---
            lea     wave16,a0
            moveq   #(wave16_end-wave16)/2,d0
            move.w  #212,d1
            moveq   #64,d2
            bsr     play_sample
            bsr     wait_tone

            ; --- 3. Twice the samples per cycle at period 212: back down ---
            lea     wave32,a0
            moveq   #(wave32_end-wave32)/2,d0
            move.w  #212,d1
            moveq   #64,d2
            bsr     play_sample
            bsr     wait_tone

            move.w  #$0050,COLOR00(a5)  ; Green screen: finished
hold:
            bra.s   hold

; wait_tone - let the tone play for TONE_FRAMES frames, stop it, then wait
; one more frame so the channel is off for well over two sample periods.
wait_tone:
            moveq   #TONE_FRAMES-1,d7
.frame:     bsr.s   wait_frame
            dbra    d7,.frame
            bsr     stop_sample
            ; fall through for the gap frame

; wait_frame - poll the vertical-blank request. Interrupts are disabled,
; but Paula still records the request in INTREQR.
wait_frame:
            move.w  #INTF_VERTB,INTREQ(a5)
.poll:      move.w  INTREQR(a5),d6
            and.w   #INTF_VERTB,d6
            beq.s   .poll
            rts

            include "paula-sample.inc"

; --- 8-bit signed samples. Audio DMA reads chip RAM only, a word at a time ---
            section data,data_c
            even
wave16:     dc.b    0,49,90,117,127,117,90,49,0,-49,-90,-117,-127,-117,-90,-49
wave16_end:
            even
wave32:     dc.b    0,25,49,71,90,106,117,125,127,125,117,106,90,71,49,25
            dc.b    0,-25,-49,-71,-90,-106,-117,-125,-127,-125,-117,-106,-90,-71,-49,-25
wave32_end:

Change the first tone’s 424 to 428 and it drops to about 518 Hz. Change any moveq #64,d2 to moveq #32,d2 and that tone plays 6 dB quieter.

The sample’s source and verification recipe include a Makefile and an executable check. It cold-boots the demo and a probe in Emu198x and checks:

  • each register write;
  • each tone’s frequency against the formula above;
  • the CPU-cost comparison;
  • the interrupt timing;
  • the one-shot.

These establish emulator behaviour. Listening and original-hardware checks are separate.

One-Shot Samples stops a sample after one pass. Four-Channel Music drives all four channels. The VBlank Game Loop shows where sound triggers sit in a frame. See also Paula and the Commodore Amiga.

Sources

Commodore-Amiga, Amiga Hardware Reference Manual, third edition (Addison-Wesley). Chapter 5, Audio Hardware: forming and playing a sound, the location, length, volume and period registers, period limits, stopping audio DMA, the audio example’s exclusive-access note, joining tones, direct output, the equal-tempered table and the audio state machine. Chapter 6, Blitter Hardware: DMA time-slot allocation and 68000 bus sharing. Amiga ROM Kernel Reference Manual: Devices, third edition, Audio Device: channel allocation. Abacus, Amiga System Programmer’s Guide, chapter 1: even bus cycles for the processor, odd cycles for DMA including audio. The frequencies and timings in the tables are calculated from the routine. The measured values are Emu198x observations recorded in the sample’s verification results.