; This program can stream a simple uncompressed PCM file from the sdcard. ; ; 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 ; 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 } }