2024-07-23 00:10:05 +00:00
|
|
|
; This program can stream a simple uncompressed PCM file from the sdcard.
|
|
|
|
;
|
2024-07-23 19:24:24 +00:00
|
|
|
; It is hardwired to play a 16 bit stereo PCM file,
|
|
|
|
; but you can type in the sample rate you want to play it in.
|
|
|
|
;
|
|
|
|
; It uses a AFLOW irq handler to refill the vera's PCM fifo buffer.
|
|
|
|
;
|
|
|
|
; The irq handler sets a flag that signals the main program to load the next block of
|
2024-07-23 00:10:05 +00:00
|
|
|
; 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.
|
|
|
|
}
|
|
|
|
|
2024-10-27 12:49:00 +00:00
|
|
|
inline asmsub wait() {
|
2024-07-23 00:10:05 +00:00
|
|
|
%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
|
|
|
|
}
|
|
|
|
}
|