pcmaudio: bugfix corrupted audio, loop mode, adjust examples

This commit is contained in:
slederer 2026-01-02 22:56:39 +01:00
parent 79baf3cef5
commit 11814cd24f
5 changed files with 363 additions and 46 deletions

View file

@ -2,6 +2,6 @@ type SndBuf = string[32768];
type SndBufPtr = ^SndBuf;
procedure PlaySample(buf:SndBufPtr;sampleRate:integer); external;
procedure SampleQStart(buf:SndBufPtr;sampleRate:integer); external;
procedure SampleQStart(buf:SndBufPtr;loop:boolean;sampleRate:integer); external;
procedure SampleQStop; external;
function SampleQSize:integer; external;

View file

@ -1,25 +1,25 @@
.EQU AUDIO_BASE $A00
.EQU IRQC_REG $980
.EQU IRQC_EN $80
.EQU CPU_FREQ 77000000
; args: sample rate
START_PCMAUDIO:
; calculate clock divider
LOADCP 77000000
LOADCP CPU_FREQ
SWAP
LOADCP _DIV
CALL
LOADC AUDIO_BASE + 1
SWAP ; put clock divider on ToS
; LOADCP 4812 ; clock divider for 16KHz sample rate
; LOADCP 2406 ; clock divider for 32KHz sample rate
STOREI 1
LOADCP 32768 ; set amplitude to biased 0
STOREI
DROP
LOADC AUDIO_BASE
LOADC 17 ; enable channel, enable interrupt
LOADC 1 ; enable channel
STOREI
DROP
RET
@ -101,18 +101,14 @@ PLAY1_L0:
DROP
RET
; start interrupt-driven sample playback
; args: pointer to pascal string, sample rate
SAMPLEQSTART:
LOADCP START_PCMAUDIO
CALL
; set sample queue count and pointer from string header
; args: pointer to string/SndBufPtr
_STR2SMPLQPTR:
LOADCP SMPLQ_COUNT
OVER
LOADI ; get string size from header
SHR ; divide by 4 to get word count
SHR
STOREI
DROP
@ -121,6 +117,38 @@ SAMPLEQSTART:
INC 8 ; skip rest of header
STOREI ; store sample data pointer
DROP
RET
; start interrupt-driven sample playback
; args: pointer to pascal string, loop flag, sample rate
SAMPLEQSTART:
LOADCP START_PCMAUDIO ; sample rate is on ToS as arg to subroutine
CALL
SWAP ; swap loop flag and buf ptr
LOADCP _STR2SMPLQPTR
CALL
; loop flag is now on ToS
CBRANCH.Z SQ_S_1
; if nonzero, set loop ptr
LOADCP SMPLQ_PTR
LOADI
DEC 8 ; subtract offset for string header again
BRANCH SQ_S_0
SQ_S_1:
LOADC 0
SQ_S_0:
LOADCP SMPLQ_NEXT
SWAP
STOREI
DROP
LOADC AUDIO_BASE
LOADC 17 ; enable channel, enable interrupt
STOREI
DROP
LOADCP SMPLQ_ISR ; set interrupt handler
STOREREG IV
@ -154,6 +182,7 @@ SAMPLEQSIZE:
SMPLQ_PTR: .WORD 0
SMPLQ_COUNT: .WORD 0
SMPLQ_NEXT: .WORD 0
SMPLQ_ISR:
LOADC IRQC_REG
@ -170,7 +199,7 @@ SMPLQ_I_L:
DROP
BRANCH SMPLQ_I_XT ; if null, end interrupt routine
SMPLQ_I_B:
LOADI ; load next word
LOADI ; load next word which contains two samples
DUP
BROT ; get high half-word
@ -205,23 +234,42 @@ SMPLQ_I_B:
STOREI
DROP
; check if fifo is full
LOADC AUDIO_BASE
LOADI
LOADC 8 ; fifo_full
; put up to 16 samples into the sample queue
LOADCP SMPLQ_COUNT
LOADI ; load word counter again
LOADC 7 ; check if count modulo 7 = 0
AND
CBRANCH.Z SMPLQ_I_L ; next sample if not full
CBRANCH.NZ SMPLQ_I_L ; if not, next two samples
LOADC AUDIO_BASE
LOADC 17 ; re-enable channel interrupt
STOREI
; check if fifo is full
; does not work reliably when running in DRAM,
; maybe because at least one sample has already played
; since start of ISR?
; LOADC AUDIO_BASE
; LOADI
; LOADC 8 ; fifo_full
; AND
; CBRANCH.Z SMPLQ_I_L ; next sample if not full
BRANCH SMPLQ_I_XT
; end of sample buffer, check for next
SMPLQ_I_END:
DROP
DROP
LOADCP SMPLQ_NEXT ; skip to end
LOADI ; if NEXT ptr is zero
DUP
CBRANCH.Z SMPLQ_I_END1
LOADCP _STR2SMPLQPTR
CALL
BRANCH SMPLQ_I_XT
; end playback, set ptr and counter to zero
SMPLQ_I_END:
DROP
SMPLQ_I_END1:
DROP
LOADCP SMPLQ_PTR
LOADC 0
@ -238,7 +286,16 @@ SMPLQ_I_END:
STOREI
DROP
; exit without enabling interrupts for this channel
BRANCH SMPLQ_I_XT2
SMPLQ_I_XT:
LOADC AUDIO_BASE
LOADC 17 ; re-enable channel interrupt
STOREI
DROP
SMPLQ_I_XT2:
LOADC IRQC_REG ; re-enable interrupts
LOADC IRQC_EN
STOREI