tweaks cx16 sample streaming example, also added a new one

This commit is contained in:
Irmen de Jong 2024-07-23 02:10:05 +02:00
parent 966b017670
commit 17f7b11148
5 changed files with 163 additions and 30 deletions

View File

@ -1195,6 +1195,7 @@ asmsub set_sprcol_irq_handler(uword address @AY) clobbers(A) {
asmsub set_aflow_irq_handler(uword address @AY) clobbers(A) { asmsub set_aflow_irq_handler(uword address @AY) clobbers(A) {
; Sets the AFLOW irq handler to use with enable_irq_handlers(). Also enables AFLOW irqs. ; Sets the AFLOW irq handler to use with enable_irq_handlers(). Also enables AFLOW irqs.
; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes. ; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes.
; NOTE: the handler itself must fill the audio fifo buffer to at least 25% full again (1 KB) or the aflow irq will keep triggering!
%asm {{ %asm {{
php php
sei sei

View File

@ -108,6 +108,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
"vtui/testvtui", "vtui/testvtui",
"pcmaudio/play-adpcm", "pcmaudio/play-adpcm",
"pcmaudio/stream-wav", "pcmaudio/stream-wav",
"pcmaudio/stream-pcm-simple",
"pcmaudio/vumeter", "pcmaudio/vumeter",
"sprites/dragon", "sprites/dragon",
"sprites/dragons", "sprites/dragons",

View File

@ -224,6 +224,8 @@ Here they are, all available in ``cx16``:
``set_aflow_irq_handler (uword address)`` ``set_aflow_irq_handler (uword address)``
Sets the audio buffer underrun interrupt handler routine. Also enables AFLOW interrupts. Sets the audio buffer underrun interrupt handler routine. Also enables AFLOW interrupts.
Note: the handler must fill the Vera's audio fifo buffer by itself with at least 25% worth of data (1 kb)
otherwise the aflow irq keeps triggering.
``disable_irq_handlers ()`` ``disable_irq_handlers ()``
Hand control back to the system default IRQ handler. Hand control back to the system default IRQ handler.

View File

@ -0,0 +1,124 @@
; This program can stream a simple uncompressed PCM file from the sdcard.
; Currently it has been set up to play a 16 bit stereo PCM file,
; and it plays it in the given sample frequency typed in by the user.
;
; It uses a AFLOW irq handler to refill the vera's PCM fifo buffer,
; and sets a flag that signals the main program to load the next block of
; data from disk. It is problematic to call kernal I/O routines inside an irq handler,
; otherwise the aflow handler itself could have loaded the pcm data straight into
; the vera's fifo buffer. But this could lead to race conditions so we need explicit buffering.
;
; The IRQ handlers are installed using Prog8's support routines for irq handlers.
; The sample data is simply read using diskio.f_read() routine, but that uses MACPTR internally for fast loads.
%import diskio
%import textio
%option no_sysinit
main {
sub start() {
str MUSIC_FILENAME = "?"*32
txt.print("what sample rate (hz) do you want to play at: ")
void txt.input_chars(MUSIC_FILENAME)
music.vera_rate_hz = conv.str2uword(MUSIC_FILENAME)
if music.vera_rate_hz==0
music.vera_rate_hz=44100
music.calculate_vera_rate(music.vera_rate_hz)
txt.print("\nname of raw .pcm file to play on drive 8: ")
while 0==txt.input_chars(MUSIC_FILENAME) {
; until user types a name...
}
if diskio.f_open(MUSIC_FILENAME) {
cx16.rombank(0)
void diskio.fastmode(1)
music.init()
interrupts.setup()
music.start()
} else {
txt.print("\nio error")
}
repeat {
interrupts.wait()
if interrupts.aflow {
; read the next 1024 bytes of audio data into the buffer
txt.chrout('.')
if diskio.f_read(interrupts.audio_buffer, 1024) != 1024
break
interrupts.aflow = false
}
}
}
}
interrupts {
bool aflow
uword audio_buffer = memory("audiobuffer", 1024, 0)
sub setup() {
aflow = false
sys.memset(audio_buffer, 1024, 0)
cx16.enable_irq_handlers(true)
cx16.set_aflow_irq_handler(&aflow_handler)
; no other irqs in this example.
}
asmsub wait() {
%asm {{
wai
}}
}
sub aflow_handler() -> bool {
; copy 1024 bytes of audio data from the buffer into vera's fifo, quickly!
%asm {{
lda p8v_audio_buffer
sta _loop+1
sta _lp2+1
lda p8v_audio_buffer+1
sta _loop+2
sta _lp2+2
ldx #4
ldy #0
_loop lda $ffff,y
sta cx16.VERA_AUDIO_DATA
iny
_lp2 lda $ffff,y
sta cx16.VERA_AUDIO_DATA
iny
bne _loop
inc _loop+2
inc _lp2+2
dex
bne _loop
}}
aflow = true ; signal main program to read the next block of audio data: it is unsafe to to I/O in a irq handler!
return false
}
}
music {
uword vera_rate_hz
ubyte vera_rate
sub init() {
cx16.VERA_AUDIO_RATE = 0 ; halt playback
cx16.VERA_AUDIO_CTRL = %10111011 ; stereo 16 bit, volume 11
}
sub start() {
cx16.VERA_AUDIO_RATE = vera_rate ; start playback
}
sub calculate_vera_rate(uword sample_rate) {
const uword vera_freq_factor = 25_000_000 / 65536
vera_rate = (sample_rate / vera_freq_factor) as ubyte + 1
vera_rate_hz = vera_rate * vera_freq_factor
}
}

View File

@ -128,10 +128,6 @@ main {
repeat { repeat {
interrupts.wait() interrupts.wait()
if interrupts.vsync {
interrupts.vsync=false
; ...do something triggered by vsync irq...
}
if interrupts.aflow { if interrupts.aflow {
interrupts.aflow=false interrupts.aflow=false
if not music.load_next_block(block_size) if not music.load_next_block(block_size)
@ -158,13 +154,12 @@ interrupts {
sub set_handler() { sub set_handler() {
sys.set_irqd() sys.set_irqd()
cx16.CINV = &handler ; handles both AFLOW and VSYNC cx16.CINV = &handler ; handler for AFLOW
cx16.VERA_IEN = %00001001 ; enable AFLOW + VSYNC cx16.VERA_IEN = %00001000 ; enable AFLOW only
sys.clear_irqd() sys.clear_irqd()
} }
bool aflow bool aflow
bool vsync
asmsub wait() { asmsub wait() {
%asm {{ %asm {{
@ -173,6 +168,8 @@ interrupts {
} }
sub handler() { sub handler() {
; we only handle aflow in this example.
if cx16.VERA_ISR & %00001000 !=0 { if cx16.VERA_ISR & %00001000 !=0 {
; Filling the fifo is the only way to clear the Aflow irq. ; Filling the fifo is the only way to clear the Aflow irq.
; So we do this here, otherwise the aflow irq will keep triggering. ; So we do this here, otherwise the aflow irq will keep triggering.
@ -183,10 +180,6 @@ interrupts {
cx16.restore_virtual_registers() cx16.restore_virtual_registers()
aflow = true aflow = true
} }
if cx16.VERA_ISR & %00000001 !=0 {
cx16.VERA_ISR = %00000001
vsync = true
}
%asm {{ %asm {{
ply ply
@ -233,23 +226,30 @@ music {
} }
asmsub uncompressed_block_8() { asmsub uncompressed_block_8() {
; optimized loop to put 1024 bytes of data into the fifo as fast as possible ; copy 1024 bytes of audio data from the buffer into vera's fifo, quickly!
; converting unsigned wav 8 bit samples to signed 8 bit on the fly ; converting unsigned wav 8 bit samples to signed 8 bit on the fly.
%asm {{ %asm {{
lda p8v_buffer lda p8v_buffer
sta cx16.r0L sta _loop+1
sta _lp2+1
lda p8v_buffer+1 lda p8v_buffer+1
sta cx16.r0H sta _loop+2
sta _lp2+2
ldx #4 ldx #4
- ldy #0 ldy #0
- lda (cx16.r0),y _loop lda $ffff,y
eor #$80 ; convert to signed 8-bit eor #$80 ; convert to signed
sta cx16.VERA_AUDIO_DATA sta cx16.VERA_AUDIO_DATA
iny iny
bne - _lp2 lda $ffff,y
inc cx16.r0H eor #$80 ; convert to signed
sta cx16.VERA_AUDIO_DATA
iny
bne _loop
inc _loop+2
inc _lp2+2
dex dex
bne -- bne _loop
rts rts
}} }}
@ -264,22 +264,27 @@ music {
} }
asmsub uncompressed_block_16() { asmsub uncompressed_block_16() {
; optimized loop to put 1024 bytes of data into the fifo as fast as possible ; copy 1024 bytes of audio data from the buffer into vera's fifo, quickly!
%asm {{ %asm {{
lda p8v_buffer lda p8v_buffer
sta cx16.r0L sta _loop+1
sta _lp2+1
lda p8v_buffer+1 lda p8v_buffer+1
sta cx16.r0H sta _loop+2
sta _lp2+2
ldx #4 ldx #4
- ldy #0 ldy #0
- lda (cx16.r0),y _loop lda $ffff,y
sta cx16.VERA_AUDIO_DATA sta cx16.VERA_AUDIO_DATA
iny iny
bne - _lp2 lda $ffff,y
inc cx16.r0H sta cx16.VERA_AUDIO_DATA
iny
bne _loop
inc _loop+2
inc _lp2+2
dex dex
bne -- bne _loop
rts
}} }}
; original prog8 code: ; original prog8 code:
; uword @requirezp ptr = main.start.buffer ; uword @requirezp ptr = main.start.buffer