mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 04:31:20 +00:00
replaced deprecated cx16 ZSOUND example by new ZSMKIT examples
This commit is contained in:
parent
64ea72ed4d
commit
6fb05bdefc
@ -114,6 +114,8 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"pcmaudio/vumeter",
|
||||
"sprites/dragon",
|
||||
"sprites/dragons",
|
||||
"zsmkit/demo1",
|
||||
"zsmkit/demo2",
|
||||
"amiga",
|
||||
"audioroutines",
|
||||
"automatons",
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
44
examples/cx16/zsmkit/README.txt
Normal file
44
examples/cx16/zsmkit/README.txt
Normal 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.
|
112
examples/cx16/zsmkit/demo1.p8
Normal file
112
examples/cx16/zsmkit/demo1.p8
Normal 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()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
51
examples/cx16/zsmkit/demo2.p8
Normal file
51
examples/cx16/zsmkit/demo2.p8
Normal 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('.')
|
||||
}
|
||||
}
|
||||
}
|
BIN
examples/cx16/zsmkit/lib/zsmkit-0830.bin
Normal file
BIN
examples/cx16/zsmkit/lib/zsmkit-0830.bin
Normal file
Binary file not shown.
BIN
examples/cx16/zsmkit/lib/zsmkit-8c00.bin
Normal file
BIN
examples/cx16/zsmkit/lib/zsmkit-8c00.bin
Normal file
Binary file not shown.
BIN
examples/cx16/zsmkit/music/MUSIC.ZSM
Normal file
BIN
examples/cx16/zsmkit/music/MUSIC.ZSM
Normal file
Binary file not shown.
BIN
examples/cx16/zsmkit/music/PAUSE.ZSM
Normal file
BIN
examples/cx16/zsmkit/music/PAUSE.ZSM
Normal file
Binary file not shown.
35
examples/cx16/zsmkit/zsmkit_high.p8
Normal file
35
examples/cx16/zsmkit/zsmkit_high.p8
Normal 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)
|
||||
}
|
35
examples/cx16/zsmkit/zsmkit_low.p8
Normal file
35
examples/cx16/zsmkit/zsmkit_low.p8
Normal 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.
@ -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
|
@ -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")
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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.
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user