replaced deprecated cx16 ZSOUND example by new ZSMKIT examples

This commit is contained in:
Irmen de Jong 2024-11-01 23:13:43 +01:00
parent 64ea72ed4d
commit 6fb05bdefc
19 changed files with 279 additions and 489 deletions

View File

@ -114,6 +114,8 @@ class TestCompilerOnExamplesCx16: FunSpec({
"pcmaudio/vumeter",
"sprites/dragon",
"sprites/dragons",
"zsmkit/demo1",
"zsmkit/demo2",
"amiga",
"audioroutines",
"automatons",

View File

@ -5,11 +5,6 @@ merge problem: if 2 library modules both have merge, stuff breaks (math & prog8_
for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed.
replace zsound example by a zsmkit example
contribute a short how-to to the zsmkit repo for building a suitable blob
write a howto for integrating third party library code like zsmkit and vtui
regenerate symbol dump files

View File

@ -0,0 +1,44 @@
Prog8 ZSMKIT music player library integration
---------------------------------------------
ZSMKIT: https://github.com/mooinglemur/zsmkit
(evolution of Zerobyte's ZSOUND, by MooingLemur). Read the README there!
DEMO1: LOW-RAM ZSMKIT + STREAMING
----------------------------------
demo1.p8 shows a way to embed the zsmkit blob into the program itself (at $0830).
It then uses the streaming support in zsmkit to load the song from disk as it is played.
It shows some other features such as callbacks and pausing as well.
The way the zsmkit blob is embedded into the program is done by telling prog8 that
the 'main' block of the program has to start at $0830, and the very first command
in that block is not the usual 'start' subroutine but a %asmbinary command to load
and embed the zsmkit blob right there and then.
DEMO2: HI-RAM ZSMKIT + PRELOADING
----------------------------------
demo2.p8 shows a simpler program that loads a zsmkit blob into upper memory at $8c00
and then also preloads the whole music file into hi-ram. No streaming is used to
play it, it plays everything from ram.
Note that the zsmkit blob used for this is a smaller build as the $0830 one because
this one was configured without streaming support enabled.
CUSTOMIZING ZSMKIT
------------------
Read the README on the zsmkit github repo. It contains a lot of important information,
about how zsmkit works, but also about the various things you have to configure to build a
new library blob to your liking. The example here includes two recently built variants of the blob,
so you don't immediately have to build something yourself, but if you want to enable or disable
streaming support or change the load address you'll have to build one yourself.
See the "alternative builds" chapter.
FUTURE: ZSMKIT V2
-----------------
If all goes well, there will be a zsmkit v2 in the future that has some important changes
that will make it much easier to integrate it into prog8 programs. Less RAM usage and
a fixed jump table location, among other changes.

View File

@ -0,0 +1,112 @@
%import textio
%import palette
%import zsmkit_low
;; Proof Of Concept ZSM player using a binary blob version of zsmkit by MooingLemur
;; This version shows how you could integrate the zsmkit blob located in lower memory at $0830.
;; It embeds the blob into the prg itself, no separate loading is necessary.
;; It uses zsmkit's streaming ability to load the song data from disk on the fly.
main $0830 {
; this has to be the first statement to make sure it loads at the specified module address $0830
%asmbinary "lib/zsmkit-0830.bin"
const ubyte zsmkit_bank = 1
bool loopchanged = false
bool beat = false
uword loop_number = 0
sub start() {
txt.print("zsmkit demo program (drive 8)!\n")
txt.print("during playback, press return to pause/unpause\n")
zsmkit.zsm_init_engine(zsmkit_bank)
play_music()
}
asmsub callback_handler(ubyte prio @X, ubyte type @Y, ubyte arg @A) {
%asm {{
brk
cpy #1
beq _loop
cpy #2
beq _sync
rts
_loop:
inc p8v_loopchanged
rts
_sync:
inc p8v_beat
rts
}}
}
sub play_music() {
uword next_free
zsmkit.zsm_setfile(0, iso:"music/MUSIC.ZSM")
cx16.rambank(2)
txt.print(iso:"STARTING IN BANK 2, ADDRESS $A000")
txt.nl()
txt.print(iso:"NOW LOADING PCM FROM MUSIC.ZSM")
txt.nl()
next_free = zsmkit.zsm_loadpcm(0, $a000)
txt.print(iso:"NOW AT BANK ")
txt.print_ub(cx16.getrambank())
txt.print(iso:", ADDRESS ")
txt.print_uwhex(next_free, true)
txt.nl()
zsmkit.zsm_play(0)
zsmkit.zsm_setcb(0, &callback_handler)
bool paused = false
uword oldjoy = $ffff
ubyte intensity = 0
repeat {
uword newjoy
newjoy, void = cx16.joystick_get(0)
if (newjoy != oldjoy and (newjoy & $10) == 0) {
if (paused) {
zsmkit.zsm_play(0)
paused = false
} else {
zsmkit.zsm_stop(0)
paused = true
}
zsmkit.zsm_close(1)
zsmkit.zsm_setfile(1, iso:"PAUSE.ZSM")
zsmkit.zsm_play(1)
}
oldjoy = newjoy
sys.waitvsync()
zsmkit.zsm_tick(0)
if (loopchanged) {
txt.nl()
loop_number += 1
loopchanged = false
txt.print(iso:"LOOP NUMBER: ")
txt.print_uw(loop_number)
txt.nl()
zsmkit.zsm_setrate(0, 60+(loop_number << 2))
}
if (beat) {
intensity = 16
beat = false
} else if (not paused) {
txt.print(".")
}
if (intensity > 0) {
intensity -= 1
palette.set_color(0, (intensity >> 1) * $111)
}
zsmkit.zsm_fill_buffers()
}
}
}

View File

@ -0,0 +1,51 @@
%import diskio
%import textio
%import zsmkit_high
;; Proof Of Concept ZSM player using a binary blob version of zsmkit by MooingLemur
;; This version is a bit simpler as "demo1".
;; This one shows how you could integrate the zsmkit blob located in higher memory at $8c30.
;; It loads the blob separately. It also doesn't use streaming,
;; it simply loads the whole ZSM file into high memory (bank 2 onwards) at the start too.
main {
const ubyte zsmkit_bank = 1
sub start() {
txt.print("zsmkit demo program (dynamic loading)\n")
load_and_init_zsmkit(iso:"lib/zsmkit-8c00.bin")
load_music_file(iso:"music/MUSIC.ZSM")
play_music()
}
sub load_and_init_zsmkit(str blobname) {
if diskio.load_raw(blobname, $8c00)>0
zsmkit.zsm_init_engine(zsmkit_bank)
else {
txt.print("error loading zsmkit blob\n")
sys.exit(1)
}
}
sub load_music_file(str filename) {
cx16.rambank(zsmkit_bank+1)
if diskio.load_raw(filename, $a000)==0 {
txt.print("error loading music file\n")
sys.exit(1)
}
}
sub play_music() {
cx16.rambank(zsmkit_bank+1)
zsmkit.zsm_setmem(0, $a000)
zsmkit.zsm_play(0)
repeat {
sys.waitvsync()
zsmkit.zsm_tick(0)
txt.chrout('.')
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,35 @@
; romsubs for zsmkit loaded at $8c00
zsmkit {
romsub $8C00 = zsm_init_engine(ubyte bank @A) clobbers(A, X, Y)
romsub $8C03 = zsm_tick(ubyte type @A) clobbers(A, X, Y)
romsub $8C06 = zsm_play(ubyte prio @X) clobbers(A, X, Y)
romsub $8C09 = zsm_stop(ubyte prio @X) clobbers(A, X, Y)
romsub $8C0C = zsm_rewind(ubyte prio @X) clobbers(A, X, Y)
romsub $8C0F = zsm_close(ubyte prio @X) clobbers(A, X, Y)
romsub $8C12 = zsm_fill_buffers() clobbers(A, X, Y)
romsub $8C15 = zsm_setlfs(ubyte prio @X, ubyte lfn_sa @A, ubyte device @Y) clobbers(A, X, Y)
romsub $8C18 = zsm_setfile(ubyte prio @X, str filename @AY) clobbers(A, X, Y)
romsub $8C1B = zsm_loadpcm(ubyte prio @X, uword data_ptr @AY) clobbers(X) -> uword @AY
romsub $8C1E = zsm_setmem(ubyte prio @X, uword data_ptr @AY) clobbers(A, X, Y)
romsub $8C21 = zsm_setatten(ubyte prio @X, ubyte value @A) clobbers(A, X, Y)
romsub $8C24 = zsm_setcb(ubyte prio @X, uword func_ptr @AY) clobbers(A, X, Y)
romsub $8C27 = zsm_clearcb(ubyte prio @X) clobbers(A, X, Y)
romsub $8C2A = zsm_getstate(ubyte prio @X) clobbers(X) -> bool @Pc, bool @Pz, uword @AY
romsub $8C2D = zsm_setrate(ubyte prio @X, uword rate @AY) clobbers(A, X, Y)
romsub $8C30 = zsm_getrate(ubyte prio @X) clobbers() -> uword @AY
romsub $8C33 = zsm_setloop(ubyte prio @X, bool loop @Pc) clobbers(A, X, Y)
romsub $8C36 = zsm_opmatten(ubyte prio @X, ubyte channel @Y, ubyte value @A) clobbers(A, X, Y)
romsub $8C39 = zsm_psgatten(ubyte prio @X, ubyte channel @Y, ubyte value @A) clobbers(A, X, Y)
romsub $8C3C = zsm_pcmatten(ubyte prio @X, ubyte value @A) clobbers(A, X, Y)
romsub $8C3F = zsm_set_int_rate(ubyte value @A, ubyte frac @Y) clobbers(A, X, Y)
romsub $8C4B = zcm_setmem(ubyte slot @X, uword data_ptr @AY) clobbers(A)
romsub $8C4E = zcm_play(ubyte slot @X, ubyte volume @A) clobbers(A, X)
romsub $8C51 = zcm_stop() clobbers(A)
romsub $8C54 = zsmkit_setisr() clobbers(A)
romsub $8C57 = zsmkit_clearisr() clobbers(A)
}

View File

@ -0,0 +1,35 @@
; romsubs for zsmkit loaded at $0830
zsmkit {
romsub $0830 = zsm_init_engine(ubyte bank @A) clobbers(A, X, Y)
romsub $0833 = zsm_tick(ubyte type @A) clobbers(A, X, Y)
romsub $0836 = zsm_play(ubyte prio @X) clobbers(A, X, Y)
romsub $0839 = zsm_stop(ubyte prio @X) clobbers(A, X, Y)
romsub $083c = zsm_rewind(ubyte prio @X) clobbers(A, X, Y)
romsub $083f = zsm_close(ubyte prio @X) clobbers(A, X, Y)
romsub $0842 = zsm_fill_buffers() clobbers(A, X, Y)
romsub $0845 = zsm_setlfs(ubyte prio @X, ubyte lfn_sa @A, ubyte device @Y) clobbers(A, X, Y)
romsub $0848 = zsm_setfile(ubyte prio @X, str filename @AY) clobbers(A, X, Y)
romsub $084b = zsm_loadpcm(ubyte prio @X, uword data_ptr @AY) clobbers(X) -> uword @AY
romsub $084e = zsm_setmem(ubyte prio @X, uword data_ptr @AY) clobbers(A, X, Y)
romsub $0851 = zsm_setatten(ubyte prio @X, ubyte value @A) clobbers(A, X, Y)
romsub $0854 = zsm_setcb(ubyte prio @X, uword func_ptr @AY) clobbers(A, X, Y)
romsub $0857 = zsm_clearcb(ubyte prio @X) clobbers(A, X, Y)
romsub $085A = zsm_getstate(ubyte prio @X) clobbers(X) -> bool @Pc, bool @Pz, uword @AY
romsub $085D = zsm_setrate(ubyte prio @X, uword rate @AY) clobbers(A, X, Y)
romsub $0860 = zsm_getrate(ubyte prio @X) clobbers() -> uword @AY
romsub $0863 = zsm_setloop(ubyte prio @X, bool loop @Pc) clobbers(A, X, Y)
romsub $0866 = zsm_opmatten(ubyte prio @X, ubyte channel @Y, ubyte value @A) clobbers(A, X, Y)
romsub $0869 = zsm_psgatten(ubyte prio @X, ubyte channel @Y, ubyte value @A) clobbers(A, X, Y)
romsub $086C = zsm_pcmatten(ubyte prio @X, ubyte value @A) clobbers(A, X, Y)
romsub $086F = zsm_set_int_rate(ubyte value @A, ubyte frac @Y) clobbers(A, X, Y)
romsub $087B = zcm_setmem(ubyte slot @X, uword data_ptr @AY) clobbers(A)
romsub $087E = zcm_play(ubyte slot @X, ubyte volume @A) clobbers(A, X)
romsub $0881 = zcm_stop() clobbers(A)
romsub $0884 = zsmkit_setisr() clobbers(A)
romsub $0887 = zsmkit_clearisr() clobbers(A)
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,80 +0,0 @@
;...........
; init_pcm :
; ===========================================================================
; Arguments: (none)
; Returns: (none)
; Affects: A
; ---------------------------------------------------------------------------
; Call this before using any of the other routines.
;
; Initializes the memory locations used by PCMM player to a stopped playback
; state, sets VERA playback rate to 0 (disabled) and clears the PCM FIFO
; also sets the volume to 15 (F).
;...........
; play_pcm :
; ===========================================================================
; Arguments: (none)
; Returns: (none)
; Affects: A,X,Y
; ---------------------------------------------------------------------------
; Call this once per frame to ensure that the PCM samples are being fed
; into VERA's PCM FIFO. While this routine is technically IRQ-safe, it is
; recommended that it not be executed during the VSYNC IRQ, as higher
; quality sample streams can consume up to ~30k CPU cycles to process.
;
; Consistent, jitter-free low-latency playback can be assured using a line IRQ
;
; If using ZSM player module, it is recommended that this call happen after
; the ZSM player's update function (playmusic) so that any PCM events will
; be processed on the same frame.
;...........
; stop_pcm :
; ===========================================================================
; Arguments: (none)
; Returns: (none)
; Affects: A
; ---------------------------------------------------------------------------
; Disables PCM playback in VERA_audio_rate register, clears the FIFO, and
; sets the PCM player module's status to clear.
;.............
; start_digi :
; ===========================================================================
; Arguments: Pointer to digi parameter table in memory.
; .A = RAM bank
; .XY = Memory address
; Returns: none
; Affects: none
; ---------------------------------------------------------------------------
; Documentation and terminology in Zsound uses the term "digi" to refer to
; a digital audio clip, as the term "sample" also refers to an individual
; PCM sample. Thus "digi" disambiguates between the two.
;
; start_digi expects a pointer to a digi parameter table in memory (low or
; high memory are both acceptable locations. If in Hi memory, the table
; must exist entirely within the same bank.
;
; The table's contents are the same as the DIGITAB struct at the
; beginning of this inc file.
;
; note that start_digi is a one-shot call to trigger a digi. There is
; currently no infrastructure to make callbacks at the end of a digi's
; playback, or any granular controls to modify a playback in progress
; such as volume changes, etc.
;
; In the future, more granular controls are planned, but the exact
; nature and functionality are as yet to be determined.
;.................
; set_pcm_volume :
; ===========================================================================
; Arguments: Pointer to digi parameter table in memory.
; .A = new volume level (only the 4 LSB are used)
;
; Returns: none
; Affects: none
; ---------------------------------------------------------------------------
; Sets the PCM volume level 0..F

View File

@ -1,118 +0,0 @@
%import textio
%import diskio
%import palette
%zeropage basicsafe
%zpreserved $22,$2d ; zsound lib uses this region
;; Proof Of Concept Zsound player using a binary blob version of zsound library by ZeroByte relocated to something usable here.
;; Can play ZSM (music) and ZCM (pcm samples).
; NOTE: the ZSound library is DEPRECATED and ZSMKIT is its successor.
; find prog8 examples with ZSMKIT here: https://github.com/mooinglemur/zsmkit/tree/main/p8demo
; "issues":
; - prog8 (or rather, 64tass) cannot "link" other assembly object files so we have to incbin a binary blob.
; - zsound player ZP usage is only known after compilation of zsound lib. And then this has to be blocked off in the prog8 program.
; - zsound lib BSS section has to be expanded into real zeros as part of the included binary.
; - zsound binary starts with 2 byte prg header that shifts the jump table 2 bytes (not a big issue but a bit untidy)
; - prog8 always sticks main module at the beginning so can't create a container module to stick zsound in (if you want to load it at the beginning)
; - prog8 always has some bootstrap code after the basic launcher so we can only start loading stuff after that
; - prog8 main has to be set to a fixed address (in this case $0830) to which the special zsound binary has been compiled as well.
main $0830 {
zsound_lib:
; this has to be the first statement to make sure it loads at the specified module address $0830
%asmbinary "zsound_combo-0830.bin"
; note: jump table is offset by 2 from the load address (because of prg header)
romsub $0832 = zsm_init() clobbers(A)
romsub $0835 = zsm_play() clobbers(A, X, Y)
romsub $0838 = zsm_playIRQ() clobbers(A, X, Y)
romsub $083b = zsm_start(ubyte bank @A, uword song_address @XY) clobbers(A, X, Y) -> bool @Pc
romsub $083e = zsm_stop()
romsub $0841 = zsm_setspeed(uword hz @XY) clobbers(A, X, Y)
romsub $0844 = zsm_setloop(ubyte count @A)
romsub $0847 = zsm_forceloop(ubyte count @A)
romsub $084a = zsm_noloop()
romsub $084d = zsm_setcallback(uword address @XY)
romsub $0850 = zsm_clearcallback() clobbers(A)
romsub $0853 = zsm_get_music_speed() clobbers(A) -> uword @XY
romsub $0856 = pcm_init() clobbers(A)
romsub $0859 = pcm_trigger_digi(ubyte bank @A, uword song_address @XY)
romsub $085c = pcm_play() clobbers(A, X, Y)
romsub $085f = pcm_stop() clobbers(A)
romsub $0862 = pcm_set_volume(ubyte volume @A)
const ubyte song_bank = 1
const uword song_address = $a000
const ubyte digi_bank = 6
const uword digi_address = $a000
const ubyte zcm_DIGITAB_size = 8 ; header size
sub start() {
txt.print("zsound demo program (drive 8)!\n")
cbm.SETMSG(%10000000) ; enable kernal status messages for load
cx16.rambank(song_bank)
if diskio.load_raw("colony.zsm", song_address)==0 {
txt.print("?can't load\n")
return
}
cx16.rambank(digi_bank)
if diskio.load_raw("terminator2.zcm", digi_address)==0 {
txt.print("?can't load\n")
return
} else {
; initialize header pointer of the zcm to point to actual sample data
; this will be set correcly by zsound lib itself if left at zero
; poke(digi_address+2, digi_bank)
; pokew(digi_address, digi_address+zcm_DIGITAB_size)
}
cbm.SETMSG(0)
txt.nl()
cx16.rambank(song_bank)
play_music()
}
sub play_music() {
zsm_init()
pcm_init()
zsm_setcallback(&end_of_song_cb)
if zsm_start(song_bank, song_address)==false {
txt.print("\nmusic speed: ")
txt.print_uw(zsm_get_music_speed())
txt.print(" hz\nplaying song! hit enter to also play a digi sample!\n")
repeat {
cx16.r0, void = cx16.joystick_get(0)
if cx16.r0!=$ffff
pcm_trigger_digi(digi_bank, digi_address)
sys.waitvsync()
repeat 1400 {
; artificially delay calling the play routine so we can see its raster time
%asm {{
nop
}}
}
palette.set_color(0, $84c)
pcm_play()
palette.set_color(0, $f25)
zsm_play()
palette.set_color(0, $000)
}
zsm_stop()
pcm_stop()
} else {
txt.print("?song start error\n")
}
}
sub end_of_song_cb() {
txt.print("end of song!\n")
}
}

View File

@ -1,91 +0,0 @@
%import textio
%import diskio
%zpreserved $22,$2d ; zsound lib uses this region
; NOTE: the ZSound library is DEPRECATED and ZSMKIT is its successor.
; find prog8 examples with ZSMKIT here: https://github.com/mooinglemur/zsmkit/tree/main/p8demo
; NOTE: this is a proof of concept to stream ZCM digi from disk while playing.
; currently there's no real streaming API / circular buffer in zsound,
; so it simply loads the whole ZCM file in chunks in memory sequentially.
; But it does so while the playback is going on in the background.
; It seems fast enough to stream + play 16khz 16bit stereo samples. (around 64 Kb/sec)
; Maybe we can go faster but 22 Khz seemed too much to keep up with.
main $0830 {
zsound_lib:
; this has to be the first statement to make sure it loads at the specified module address $0830
%asmbinary "zsound_combo-0830.bin"
; note: jump table is offset by 2 from the load address (because of prg header)
romsub $0832 = zsm_init() clobbers(A)
romsub $0835 = zsm_play() clobbers(A, X, Y)
romsub $0838 = zsm_playIRQ() clobbers(A, X, Y)
romsub $083b = zsm_start(ubyte bank @A, uword song_address @XY) clobbers(A, X, Y) -> bool @Pc
romsub $083e = zsm_stop()
romsub $0841 = zsm_setspeed(uword hz @XY) clobbers(A, X, Y)
romsub $0844 = zsm_setloop(ubyte count @A)
romsub $0847 = zsm_forceloop(ubyte count @A)
romsub $084a = zsm_noloop()
romsub $084d = zsm_setcallback(uword address @XY)
romsub $0850 = zsm_clearcallback() clobbers(A)
romsub $0853 = zsm_get_music_speed() clobbers(A) -> uword @XY
romsub $0856 = pcm_init() clobbers(A)
romsub $0859 = pcm_trigger_digi(ubyte bank @A, uword song_address @XY)
romsub $085c = pcm_play() clobbers(A, X, Y)
romsub $085f = pcm_stop() clobbers(A)
romsub $0862 = pcm_set_volume(ubyte volume @A)
const ubyte digi_bank = 1
const uword digi_address = $a000
const ubyte zcm_DIGITAB_size = 8 ; header size
const uword ram_bank_size = $2000
bool load_ok = false
sub prebuffer() {
txt.print("prebuffering...")
void diskio.f_read(digi_address, ram_bank_size*4)
}
sub start() {
txt.print("\nzsound digi streaming (drive 8)!\n")
if not diskio.f_open("thriller.zcm") {
txt.print("?no file\n")
return
}
cx16.rambank(digi_bank)
prebuffer()
pcm_init()
pcm_trigger_digi(digi_bank, digi_address)
cx16.enable_irq_handlers(true)
cx16.set_vsync_irq_handler(&zsm_playroutine_irq)
txt.print("\nstreaming from file, playback in irq!\n")
uword size = 1
while size!=0 {
size = diskio.f_read(digi_address, ram_bank_size) ; load next bank
txt.print_ub(cx16.getrambank())
txt.spc()
sys.wait(5) ; artificial delay
}
txt.print("sound file end reached.\n")
diskio.f_close()
repeat {
}
pcm_stop() ;unreached
cx16.disable_irq_handlers()
}
sub zsm_playroutine_irq() -> bool {
pcm_play()
return true
}
}

View File

@ -1,195 +0,0 @@
;..............
; init_player :
; ===========================================================================
; Arguments: (none)
; Returns: (none)
; Affects: A
; ---------------------------------------------------------------------------
; Call this before using any of the other routines.
;
; Initializes the memory locations used by ZSM player to a stopped playback
; state, with the data pointer pointing at a dummy "end-of-data" command frame.
;............
; playmusic :
; ===========================================================================
; Arguments: none
; Returns: none
; Affects: See stepmusic
; ---------------------------------------------------------------------------
; 60hz frontend for stepmusic.
; Call it once per frame to play ZSM music.
;
; Playmusic calls stepmusic however many times is determined from the ZSM tick
; rate header information. This function does not "smooth" time - i.e. it
; just calls stepmusic N times in a row. For more accurate timing
; you should generate your own timing source and call stepmusic directly.
;
; NOTE: playmusic calls stepmusic, WHICH IS NOT IRQ SAFE! Use playmusic_IRQ instead
; if you wish to run the music update during the IRQ handler, or else your
; handler should save and restore the VERA ctrl register and the address
; registers for the data0 data port.
;................
; playmusic_IRQ :
; ===========================================================================
; IRQ-safe version of playmusic, which restores VERA registers and the active
; HiRAM bank.
;
; It does NOT save or restore the CPU registers. Your IRQ handler should
; pull those from the stack prior to RTI as usual if not jumping into the
; Kernal's once per frame routine.
;
; Alternatively, you can use this interface as a VERA-safe call from outside
; of IRQ handlers. A,X,and Y are still clobbered by stepmusic.
;............
; stepmusic :
; ===========================================================================
; Arguments: (none)
; Returns: Carry flag: (currently broken) 0=playing, 1=stopped or looped
; Affects: A,X,Y, VERA CTRL and data port 0 address registers
; ---------------------------------------------------------------------------
; Advances the music by one tick.
; Music must be initialized by startmusic before this will have any effect.
;
; This routine may be removed from the API in future revisions!
;
; Call as many times per frame as required by the ZSM's playback rate.
; (usually 60Hz - once per frame)
; THIS ROUTINE IS NOT SAFE to call directly during IRQ, as it clobbers VERA
; registers w/o fixing them (for speed reasons). If your program
; is designed to run the music player during an IRQ, use one of the IRQ-safe
; wrapper functions that save and restore VERA before calling this
; core routine, or else be sure to save the states of the affected VERA registers
;-----------------------------------------------------[Music Control]--------
;.............
; startmusic :
; ===========================================================================
; Arguments:
; A : HIRAM bank of tune
; XY : Memory address of beginning of ZSM header
; Returns: Carry flag: 0=success, 1=fail
; Affects: A,X,Y
; ---------------------------------------------------------------------------
; Uses the ZSM header to determine the default playback parameters.
; Copies them into the active data structures, and adjusts header pointers
; such as loop and PCM (future) sample bank offsets relative to where the
; ZSM was actually loaded into memory.
;
; Calls setmusicspeed with the song's default play speed from header.
; Defaults the looping behavior based on the ZSM being played. If the tune
; contains a loop, the playback is set for infinite loop mode. If it does
; not, the loop pointer is set to the beginning of the tune, but looping
; remains disabled.
; You may call set_loop, force_loop, or disable_loop to modify these defaults
; afterwards.
;............
; stopmusic :
; ===========================================================================
; Arguments: (none)
; Returns: (none)
; Affects: (none)
; ---------------------------------------------------------------------------
; Halts music playback, silences all voices used by the current tune,
; and clears music channel mask
; ------------------------------------------------[On-The-Fly Controls]------
;................
; setmusicspeed :
; ===========================================================================
; Arguments:
; X/Y : Playback speed in Hz. (x=lo, y=hi)
; Returns: (none)
; Affects: A,X,Y
; ---------------------------------------------------------------------------
; Converts Hz into ticks/frame, and adjusts playback speed to the new rate.
; Setting a 60hz song to play back at 120hz will play at double speed and
; setting it to 30hz will play at 1/2 speed.
; (I suppose I should make an accessor function to return the native speed
; of a ZSM so that you don't have to guess in order to make calculated speed
; changes)
;.............
; force_loop :
; ===========================================================================
; Arguments: .A = number of times to loop the music. 0 = infinite
; Returns: none
; Affects: none
; ---------------------------------------------------------------------------
; Note that if the music was intended to be a one-shot playback (i.e. contains
; no loop), this will force looping play of the entire song. Use set_loop
; to modify the looper's behavior without forcing looping play of non-looped
; songs.
;...........
; set_loop :
; ===========================================================================
; Arguments: .A = number of times to loop the music. 0 = infinite
; Returns: none
; Affects: none
; ---------------------------------------------------------------------------
; Sets the number of repeats that loopback mode will perform after the current
; pass through the song is finished. If the playback mode is one-shot (no looping)
; then this will have no effect.
;...............
; disable_loop :
; ===========================================================================
; Arguments: none
; Returns: none
; Affects: none
; ---------------------------------------------------------------------------
; When music reaches EOF, playback will stop, regardless of loops in the tune.
; Has no effect if looping was not already enabled.
;-----------------------------------------------------[Information]----------
;...............
; set_callback :
; ===========================================================================
; Arguments:
; .XY = address of callback function
; Returns: none
; Affects: none
; ---------------------------------------------------------------------------
; Sets a notification callback whenever a tune finishes or loops.
; The callback passes the following parameters:
; Z=0 if music is stopped, Z=1 if music is playing
; .A = number of remaining loops
;.................
; clear_callback :
; ===========================================================================
; Arguments: none
; Returns: none
; Affects: A
; ---------------------------------------------------------------------------
; Disables the EOF notification callback
;..................
; get_music_speed :
; ===========================================================================
; Arguments: none
; Returns: .XY = song's play rate in Hz
; Affects: none
; ---------------------------------------------------------------------------
; This function returns the default playback rate in Hz of the active song.
; This is NOT the current playback speed, but the speed in the song's header.