; 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 simple polling on the AFLOW flag rather than actually using a IRQ hander,
; to see when to refill the FIFO buffer.
;
; 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.start()
            while music.stream() {
                txt.chrout('.')
            }
        } else {
            txt.print("\nio error")
        }
    }
}


music {
    uword audio_buffer = memory("audiobuffer", 1024, 0)

    uword vera_rate_hz
    ubyte vera_rate

    sub start() {
        cx16.VERA_AUDIO_RATE = 0            ; halt playback
        cx16.VERA_AUDIO_CTRL = %10111011    ; stereo 16 bit, volume 11
        sys.memset(audio_buffer, 1024, 0)
        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
    }

    sub stream() -> bool {
        while cx16.VERA_ISR & %00001000 == 0 {
            ; poll until AFLOW is raised ("fifo is less than 25% full, feed me more data soon!")
        }

        ; Reading directly from disk into the audio fifo is possible, but can result in hick ups.:
        ; diskio.reset_read_channel()
        ; void, cx16.r14 = cx16.MACPTR(0, &cx16.VERA_AUDIO_DATA, true)
        ; void, cx16.r15 = cx16.MACPTR(0, &cx16.VERA_AUDIO_DATA, true)
        ; return cx16.r14+cx16.r15 == 1024

        ; so instead: copy current buffer to Vera audio FIFO and immediately read the next 1 KB block from disk into the buffer.
        fill_fifo()
        return diskio.f_read(audio_buffer, 1024) == 1024
    }

    asmsub fill_fifo() {
        ; 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
            rts
        }}
    }
}
