Subversion Repositories NedoOS

Rev

Rev 2286 | Blame | Compare with Previous | Last modification | View Log | Download

  1. ; ProTracker modules player for AY8910/TurboSound
  2. ; Player for MIDI UART connected to AY port A.2 (e.g. ZX MultiSound)
  3.  
  4.         DEVICE ZXSPECTRUM128
  5.         include "../_sdk/sys_h.asm"
  6.         include "playerdefs.asm"
  7.  
  8. MDLADDR = 0x8000
  9. TITLELENGTH = 64
  10. MEMORYSTREAMMAXPAGES = 210
  11. MEMORYSTREAMERRORMASK = 255
  12. MIDMAXTRACKS = 64
  13. MIDCHANNELS = 16
  14.  
  15.         struct MIDTRACK
  16. lastcommand ds 1
  17. nexteventtick ds 4
  18. streamoffset ds 4
  19.         ends
  20.  
  21.         struct MIDPLAYER
  22. filetype ds 1
  23. trackcount ds 1
  24. tickcounter ds 4
  25. ticksperupdate ds 4
  26. ticksperqnoteXupdatelen ds 4
  27. tracks ds MIDTRACK*MIDMAXTRACKS
  28.         ends
  29.  
  30.         org PLAYERSTART
  31.  
  32. begin   PLAYERHEADER
  33.  
  34. isfilesupported
  35. ;cde = file extension
  36. ;out: zf=1 if this player can handle the file and the sound hardware is available, zf=0 otherwise
  37.         call ismidfile
  38.         jr nz,.checkts
  39.         ld hl,0
  40.         ld (MUSICTITLEADDR),hl
  41.         ld hl,musicprogress+1
  42.         ld (MUSICPROGRESSADDR),hl
  43.         jp initprogress
  44. .checkts
  45.         ld a,c
  46.         cp 't'
  47.         jr nz,.checkpt
  48.         ld a,d
  49.         cp 's'
  50.         jr nz,.checkpt
  51.         ld a,e
  52.         or a
  53.         jr z,.tsmodule
  54. .checkpt
  55.         ld a,c
  56.         cp 'p'
  57.         ret nz
  58.         ld a,d
  59.         cp 't'
  60.         ret nz
  61.         ld a,e
  62.         cp '2'
  63.         jr nz,.checkpt3
  64. ;prepare local variables
  65.         ld hl,0
  66.         ld (MUSICTITLEADDR),hl
  67.         ld (MUSICPROGRESSADDR),hl
  68.         ret
  69. .checkpt3
  70.         cp '3'
  71.         ret nz
  72. ;prepare local variables
  73. .tsmodule
  74.         ld hl,0
  75.         ld (MUSICTITLEADDR),hl
  76.         ld hl,musicprogress+1
  77.         ld (MUSICPROGRESSADDR),hl
  78.         jp initprogress
  79.  
  80. ismidfile
  81. ;cde = file extension
  82. ;out: zf=1 if .mod, zf=0 otherwise
  83.         ld a,'m'
  84.         cp c
  85.         ret nz
  86.         ld a,'i'
  87.         cp d
  88.         ret nz
  89.         ld a,'d'
  90.         cp e
  91.         ret
  92.  
  93. playerinit
  94. ;hl,ix = GPSETTINGS
  95. ;a = player page
  96. ;out: zf=1 if init is successful, hl=init message
  97.         ld (playerpage),a
  98.         ld a,(hl)
  99.         ld (page8000),a
  100.         inc hl
  101.         ld a,(hl)
  102.         ld (pageC000),a
  103.         call setuartdelay
  104.         ld a,(waitspincount)
  105.         or a
  106.         call z,setautouartdelay
  107.         ld hl,initokstr
  108.         xor a
  109.         ret
  110.  
  111. setuartdelay
  112. ;ix = GPSETTINGS
  113.         ld de,(ix+GPSETTINGS.midiuartdelayoverride)
  114.         ld a,d
  115.         or e
  116.         ret z
  117.         ld bc,3*256
  118. .loop   ld a,(de)
  119.         sub '0'
  120.         jr c,.done
  121.         cp 10
  122.         jr nc,.done
  123.         ld h,a
  124.         ld a,c
  125.         add a,a
  126.         ld c,a
  127.         add a,a
  128.         add a,a
  129.         add a,c
  130.         add a,h
  131.         ld c,a
  132.         inc de
  133.         djnz .loop
  134. .done   ld a,c
  135.         ld (waitspincount),a
  136.         ret
  137.  
  138. setautouartdelay
  139. ;ix = GPSETTINGS
  140.         ld de,(ix+GPSETTINGS.framelength)
  141.         ld bc,CC2
  142.         call uintmul16
  143.         xor a
  144.         rl h
  145.         adc a,e
  146.         ld (waitspincount),a
  147.         ret
  148.  
  149. playerdeinit
  150.         ret
  151.  
  152. musicload
  153. ;cde = file extension
  154. ;hl = input file name
  155. ;out: hl = device mask, zf=1 if the file is ready for playing, zf=0 otherwise
  156.         call ismidfile
  157.         jr nz,.ptfile
  158.         call midloadfile
  159.         ret nz
  160.         ld a,255
  161.         ld (isplayingmidfile),a
  162.         ld hl,DEVICE_MIDI_UART_MASK
  163.         xor a
  164.         ret
  165. .ptfile ex de,hl
  166.         call openstream_file
  167.         or a
  168.         ret nz
  169. page8000=$+1
  170.         ld a,0
  171.         SETPG8000
  172. pageC000=$+1
  173.         ld a,0
  174.         SETPGC000
  175.         ld de,MDLADDR
  176.         ld hl,de
  177.         call readstream_file
  178.         push hl
  179.         call closestream_file
  180.         pop ix
  181.         call getconfig
  182.         ld (SETUP),a
  183.         ld de,MDLADDR
  184.         add hl,de
  185.         ex hl,de
  186.         call INIT
  187. playerpage=$+1
  188.         ld a,0
  189.         ld hl,PLAY
  190.         OS_SETMUSIC
  191.         xor a
  192.         ld (isplayingmidfile),a
  193.         ld a,(is_ts)
  194.         or a
  195.         ld hl,DEVICE_AY_MASK
  196.         jr z,$+5
  197.         ld hl,DEVICE_TURBOSOUND_MASK
  198.         xor a
  199.         ret
  200.  
  201. musicunload
  202.         ld a,(isplayingmidfile)
  203.         or a
  204.         jp nz,midunload
  205.         ld a,(playerpage)
  206.         ld hl,play_reter
  207.         OS_SETMUSIC
  208.         jp MUTE
  209.  
  210. play_reter
  211.         ret
  212.  
  213. musicplay
  214. ;out: zf=0 if still playing, zf=1 otherwise
  215.         ld a,(isplayingmidfile)
  216.         or a
  217.         jp nz,midplay
  218.         YIELD
  219.         YIELD
  220.         YIELD
  221.         YIELD
  222.         YIELD
  223.         ld a,(SETUP)
  224.         and 2
  225.         ld a,(VARS1+VRS.CurPos)
  226.         call z,updateprogress
  227.         ld a,(SETUP)
  228.         cpl
  229.         and 128
  230.         ret
  231.  
  232. findts
  233. ;ix = file size
  234. ;out: zf = 1 if TS data is found, hl = offset to the second module if available
  235.         ld de,MDLADDR
  236.         add ix,de ;past-the-end address of the data buffer
  237.         ld a,'0'
  238.         cp (ix-4)
  239.         ret nz
  240.         ld a,'2'
  241.         cp (ix-3)
  242.         ret nz
  243.         ld a,'T'
  244.         cp (ix-2)
  245.         ret nz
  246.         ld a,'S'
  247.         cp (ix-1)
  248.         ret nz
  249.         ld hl,(ix-12)
  250.         ret
  251.  
  252. getconfig
  253. ;ix = file size
  254. ;out: a = player config bits, hl = offset to the second module if available
  255.         ld a,(MDLADDR)
  256.         cp 'V'
  257.         jr z,.ispt3
  258.         cp 'P'
  259.         jr z,.ispt3
  260.         ld a,%00000011 ;PT2
  261.         ret
  262. .ispt3
  263.         ld a,(MDLADDR+101)
  264.         call setprogressdelta
  265. ;set title
  266.         ld hl,titlestr
  267.         ld (MUSICTITLEADDR),hl
  268.         ld de,titlestr+1
  269.         ld bc,TITLELENGTH-1
  270.         ld (hl),' '
  271.         ldir
  272.         xor a
  273.         ld (de),a
  274.         ld hl,titlestr-1
  275.         ld de,MDLADDR+30
  276.         ld bc,68*256+TITLELENGTH
  277. .copytitleloop
  278.         ld a,(de)
  279.         inc de
  280.         cp ' '
  281.         jr nz,$+5
  282.         cp (hl)
  283.         jr z,$+7
  284.         inc hl
  285.         ld (hl),a
  286.         dec c
  287.         jr z,$+4
  288.         djnz .copytitleloop
  289.         call findts
  290.         ld a,%00010001 ;2xPT3
  291.         ret z
  292.         ld a,%00100001 ;PT3
  293.         ret
  294.  
  295.         include "../_sdk/file.asm"
  296.         include "ptsplay/ptsplay.asm"
  297.         include "common/memorystream.asm"
  298.         include "common/muldiv.asm"
  299.         include "progress.asm"
  300.  
  301. VSYNC_FREQ = 49
  302. BAUD_RATE = 31250
  303. WAIT_LOOP_TSTATES = 14
  304. BENCHMARK_LOOP_TSTATES = 42
  305. DEFAULT_QNOTE_DURATION_MCS = 500000
  306.  
  307. VSYNC_MCS = 1000000/VSYNC_FREQ
  308. CC1 = WAIT_LOOP_TSTATES*BAUD_RATE
  309. CC2 = (VSYNC_FREQ*BENCHMARK_LOOP_TSTATES*65536+CC1/2)/CC1
  310.  
  311. SSG_REG = 0xfffd
  312. SSG_DAT = 0xbffd
  313.  
  314.         macro wait_32us reducebytstates
  315.         ld a,(waitspincount)
  316.         add a,-(20+reducebytstates+7)/14
  317. .loop   dec a
  318.         jp z,.done
  319.         dec a
  320.         jp z,.done
  321.         dec a
  322.         jp z,.done
  323.         dec a
  324.         jp nz,.loop
  325. .done   ;that's all, folks
  326.         endm
  327.  
  328. midinitport
  329.         ld bc,SSG_REG
  330.         ld a,0xff
  331.         out (c),a
  332.         ld a,7
  333.         out (c),a
  334.         ld bc,SSG_DAT
  335.         ld a,0xfc
  336.         out (c),a
  337.         ret
  338.  
  339. midsend3
  340. ;dhl = data
  341.         call midsendbyte
  342. midsend2
  343. ;hl = data
  344.         ld d,h
  345.         call midsendbyte
  346.         ld d,l
  347. midsendbyte
  348. ;d = data
  349.         di
  350.         ld bc,SSG_REG
  351.         ld a,14
  352.         out (c),a
  353.         ld bc,SSG_DAT
  354.         ld a,%11111010
  355.         out (c),a
  356.         wait_32us 42
  357.         scf
  358.         rr d
  359. .loop   sbc a,a
  360.         and %00000100
  361.         add a,%11111010
  362.         out (c),a
  363.         wait_32us 48
  364.         srl d
  365.         jp nz,.loop
  366.         nop
  367.         nop
  368.         nop
  369.         ld a,%11111110
  370.         out (c),a
  371.         wait_32us 0
  372.         ei
  373.         ret
  374.  
  375. midloadfile
  376. ;hl = input file name
  377. ;out: zf=1 if loaded, zf=0 otherwise
  378.         call midinitport
  379. ;reset the reciever
  380.         ld d,255
  381.         call midsendbyte
  382. ;load and parse the file
  383.         ex de,hl
  384.         call memorystreamloadfile
  385.         ret nz
  386.         ld hl,midplayer
  387.         ld de,midplayer+1
  388.         ld bc,MIDPLAYER-1
  389.         ld (hl),0
  390.         ldir
  391.         call memorystreamstart
  392.         ld b,midheadersigsize
  393.         ld de,midheadersig
  394.         call midchecksignature
  395.         jp nz,memorystreamfree ;sets zf=0
  396.         memory_stream_read_2 c,a
  397.         ld (midplayer.filetype),a
  398.         memory_stream_read_2 c,a
  399.         ld (midplayer.trackcount),a
  400.         add a,-MIDMAXTRACKS-1
  401.         sbc a,a
  402.         ret nz
  403.         memory_stream_read_2 b,c
  404.         ld de,VSYNC_MCS
  405.         call uintmul16
  406.         add hl,hl : rl de
  407.         add hl,hl : rl de
  408.         ld (midplayer.ticksperqnoteXupdatelen+0),hl
  409.         ld (midplayer.ticksperqnoteXupdatelen+2),de
  410.         call midloadtracks
  411.         jp nz,memorystreamfree ;sets zf=0
  412.         call midsetprogressdelta
  413.         ld de,0
  414.         ld hl,14
  415.         call memorystreamseek
  416.         call midloadtracks
  417.         jp nz,memorystreamfree ;sets zf=0
  418.         ld hl,DEFAULT_QNOTE_DURATION_MCS%65536
  419.         ld de,DEFAULT_QNOTE_DURATION_MCS/65536
  420.         call setticksperupdate
  421.         xor a
  422.         ret
  423.  
  424. midchecksignature
  425. ;b = byte count
  426. ;de = signature
  427. ;out: zf=1 if ok, zf=0 otherwise
  428.         ld hl,(memorystreamcurrentaddr)
  429. .loop   memory_stream_read_byte c
  430.         ld a,(de)
  431.         cp c
  432.         ret nz
  433.         inc de
  434.         djnz .loop
  435.         ld (memorystreamcurrentaddr),hl
  436.         ret
  437.  
  438. midloadtracks
  439.         ld ix,midplayer.tracks
  440.         ld iy,(midplayer.trackcount)
  441. .loop   ld b,midtracksigsize
  442.         ld de,midtracksig
  443.         call midchecksignature
  444.         ret nz
  445.         call memorystreamread4
  446.         ld l,b
  447.         ld h,c
  448.         push hl
  449.         ld e,a
  450.         push de
  451.         call memorystreamgetpos
  452.         push de
  453.         push hl
  454.         ld hl,(memorystreamcurrentaddr)
  455.         call midreadvarint
  456.         ld (memorystreamcurrentaddr),hl
  457.         ld b,0
  458.         sla de : rl bc
  459.         sla de : rl bc
  460.         ld (ix+MIDTRACK.nexteventtick+0),de
  461.         ld (ix+MIDTRACK.nexteventtick+2),c
  462.         call memorystreamgetpos
  463.         ld (ix+MIDTRACK.streamoffset+0),hl
  464.         ld (ix+MIDTRACK.streamoffset+2),de
  465.         pop hl
  466.         pop de
  467.         pop bc
  468.         add hl,bc
  469.         ex de,hl
  470.         pop bc
  471.         adc hl,bc
  472.         ex de,hl
  473.         call memorystreamseek
  474.         ld bc,MIDTRACK
  475.         add ix,bc
  476.         dec iyl
  477.         jp nz,.loop
  478.         ret
  479.  
  480. midmute
  481.         ld e,0xb0
  482.         ld b,MIDCHANNELS
  483. .loop   push bc
  484.         ld d,e
  485.         ld hl,123*256
  486.         call midsend3 ;notes off
  487.         ld d,e
  488.         ld hl,120*256
  489.         call midsend3 ;sounds off
  490.         ld d,e
  491.         ld hl,121*256
  492.         call midsend3 ;controllers off
  493.         pop bc
  494.         inc e
  495.         djnz .loop
  496.         ret
  497.  
  498. midunload
  499.         call midmute
  500.         jp memorystreamfree
  501.  
  502. midplay
  503.         YIELD
  504. ;advance tick counter
  505.         ld hl,(midplayer.tickcounter+0)
  506.         ld de,(midplayer.ticksperupdate+0)
  507.         add hl,de
  508.         ld (midplayer.tickcounter+0),hl
  509.         ex de,hl
  510.         ld hl,(midplayer.tickcounter+2)
  511.         ld bc,(midplayer.ticksperupdate+2)
  512.         adc hl,bc
  513.         ld (midplayer.tickcounter+2),hl
  514.         ex de,hl
  515.         call midgetprogress
  516.         call updateprogress
  517. ;iterate through the tracks
  518.         ld ix,midplayer.tracks
  519.         ld a,(midplayer.trackcount)
  520.         ld b,a
  521.         ld c,0
  522. .trackloop
  523.         bit 7,(ix+MIDTRACK.streamoffset+3)
  524.         jr nz,.skiptrack
  525.         ld c,255
  526.         ld hl,(midplayer.tickcounter+0)
  527.         ld de,(ix+MIDTRACK.nexteventtick+0)
  528.         sub hl,de
  529.         ld hl,(midplayer.tickcounter+2)
  530.         ld de,(ix+MIDTRACK.nexteventtick+2)
  531.         sbc hl,de
  532.         jr c,.skiptrack
  533.         push bc
  534.         call midhandletrackevent
  535.         pop bc
  536.         jr .trackloop
  537. .skiptrack
  538.         ld de,MIDTRACK
  539.         add ix,de
  540.         djnz .trackloop
  541.         ld a,c
  542.         or a
  543.         ret
  544.  
  545. midreadvarint
  546. ;hl = memory stream addr
  547. ;out: cde = number, hl = memory stream addr
  548.         ld de,0
  549.         ld c,0
  550. .loop   memory_stream_read_byte b
  551.         ld a,e
  552.         rrca
  553.         xor b
  554.         and 0x80
  555.         xor b
  556.         rr c
  557.         rr de
  558.         ld c,d
  559.         ld d,e
  560.         ld e,a
  561.         bit 7,b
  562.         jr nz,.loop
  563.         ret
  564.  
  565.         macro process_midi_event call_send_byte,call_send_2,call_send_3
  566. ;ix = track
  567. ;hl = memory stream address
  568.         memory_stream_read_byte b
  569.         bit 7,b
  570.         jr z,.gotdatabyte
  571.         ld (ix+MIDTRACK.lastcommand),b
  572.         memory_stream_read_byte d
  573.         jr .handlecommand
  574. .gotdatabyte
  575.         ld d,b
  576.         ld b,(ix+MIDTRACK.lastcommand)
  577. .handlecommand
  578.         ld a,b
  579.         rrca
  580.         rrca
  581.         rrca
  582.         rrca
  583.         and 7
  584.         ld c,a
  585.         add a,a
  586.         add a,c
  587.         ld (.commandtable),a
  588. .commandtable=$+1
  589.         jr $
  590.         jp .send3 ; 8 Note Off
  591.         jp .send3 ; 9 Note On
  592.         jp .send3 ; A Polyphonic Pressure
  593.         jp .send3 ; B Control Change   
  594.         jp .send2 ; C Program Change
  595.         jp .send2 ; D Channel Pressure
  596.         jp .send3 ; E Pitch Bend
  597. ;;;;;;;;;;;;;;;;;;; F System
  598.         ld a,b
  599.         cp 0xff
  600.         jp z,.handlemeta
  601.         cp 0xf0
  602.         jp nz,.finalize
  603.         call midreadvarint
  604.         ld d,0xf0
  605.         call_send_byte
  606. .sendloop
  607.         memory_stream_read_byte e
  608.         ld d,e
  609.         call_send_byte
  610.         ld a,e
  611.         cp 0xf7
  612.         jr nz,.sendloop
  613.         ld (memorystreamcurrentaddr),hl
  614.         jr .finalize
  615. .handlemeta
  616.         ld a,d
  617.         cp 0x2f
  618.         jr z,.markdone
  619.         cp 0x51
  620.         jr z,.setduration
  621.         call midreadvarint
  622.         ld a,e
  623.         or d
  624.         jr z,.finalize
  625.         ld a,e
  626.         dec de
  627.         inc d
  628.         ld c,d
  629.         ld b,a
  630. .skiploop
  631.         bit 6,h
  632.         call nz,memorystreamnextpage
  633.         inc hl
  634.         djnz .skiploop
  635.         dec c
  636.         jr nz,.skiploop
  637.         ld (memorystreamcurrentaddr),hl
  638.         jr .finalize
  639. .markdone
  640.         set 7,(ix+MIDTRACK.streamoffset+3)
  641.         ret
  642. .setduration
  643.         call midreadvarint
  644.         memory_stream_read_byte a
  645.         memory_stream_read_byte d
  646.         memory_stream_read_byte e
  647.         ld (memorystreamcurrentaddr),hl
  648.         ex de,hl
  649.         ld e,a
  650.         ld d,0
  651.         push ix
  652.         call setticksperupdate
  653.         pop ix
  654.         jr .finalize
  655. .send2  ld (memorystreamcurrentaddr),hl
  656.         ld l,d
  657.         ld h,b
  658.         call_send_2
  659.         jr .finalize
  660. .send3  memory_stream_read_byte e
  661.         ld (memorystreamcurrentaddr),hl
  662.         ex de,hl
  663.         ld d,b
  664.         call_send_3
  665. .finalize
  666.         ld hl,(memorystreamcurrentaddr)
  667.         call midreadvarint
  668.         ld (memorystreamcurrentaddr),hl
  669.         ld b,0
  670.         sla de : rl bc
  671.         sla de : rl bc
  672.         ld hl,(ix+MIDTRACK.nexteventtick+0)
  673.         add hl,de
  674.         ld (ix+MIDTRACK.nexteventtick+0),hl
  675.         ld hl,(ix+MIDTRACK.nexteventtick+2)
  676.         adc hl,bc
  677.         ld (ix+MIDTRACK.nexteventtick+2),hl
  678.         endm
  679.  
  680. midhandletrackevent
  681. ;ix = track
  682.         ld hl,(ix+MIDTRACK.streamoffset+0)
  683.         ld de,(ix+MIDTRACK.streamoffset+2)
  684.         call memorystreamseek
  685.         process_midi_event <call midsendbyte>,<call midsend2>,<call midsend3>
  686.         call memorystreamgetpos
  687.         ld (ix+MIDTRACK.streamoffset+0),hl
  688.         ld (ix+MIDTRACK.streamoffset+2),de
  689.         ret
  690.  
  691. midgetprogress
  692. ;dehl = ticks
  693. ;out: a = progress
  694.         ld a,e
  695.         add hl,hl : rla
  696.         add hl,hl : rla
  697.         add hl,hl : rla
  698.         add hl,hl : rla
  699.         ret
  700.  
  701. midsetprogressdelta
  702.         ld ix,midplayer.tracks
  703.         ld a,(midplayer.trackcount)
  704.         ld b,a
  705.         ld c,0
  706. .trackloop
  707.         push bc
  708.         ld hl,(ix+MIDTRACK.streamoffset+0)
  709.         ld de,(ix+MIDTRACK.streamoffset+2)
  710.         call memorystreamseek
  711. .eventloop
  712.         call midadvancetrack
  713.         bit 7,(ix+MIDTRACK.streamoffset+3)
  714.         jr z,.eventloop
  715.         ld hl,(ix+MIDTRACK.nexteventtick+0)
  716.         ld de,(ix+MIDTRACK.nexteventtick+2)
  717.         call midgetprogress
  718.         pop bc
  719.         cp c
  720.         jr c,$+3
  721.         ld c,a
  722.         ld de,MIDTRACK
  723.         add ix,de
  724.         djnz .trackloop
  725.         ld a,c
  726.         jp setprogressdelta
  727.  
  728. midadvancetrack
  729. ;ix = track
  730.         ld hl,(memorystreamcurrentaddr)
  731.         process_midi_event < >,< >,< >
  732.         ret
  733.  
  734. setticksperupdate
  735. ;dehl = qnote duration in mcs
  736.         exx
  737.         ld hl,(midplayer.ticksperqnoteXupdatelen+0)
  738.         ld de,(midplayer.ticksperqnoteXupdatelen+2)
  739.         call uintdiv32
  740.         ld (midplayer.ticksperupdate+0),hl
  741.         ld (midplayer.ticksperupdate+2),de
  742.         ret
  743.  
  744. midheadersig
  745.         db "MThd",0,0,0,6
  746. midheadersigsize = $-midheadersig
  747. midtracksig
  748.         db "MTrk"
  749. midtracksigsize = $-midtracksig
  750. initokstr
  751.         db "OK\r\n",0
  752. playernamestr
  753.         db "ProTracker/MIDI UART",0
  754. end
  755.  
  756. titlestr ds TITLELENGTH+1
  757. isplayingmidfile ds 1
  758. waitspincount ds 1
  759. midplayer MIDPLAYER
  760.  
  761.         assert $ <= PLAYEREND ;ensure everything is within the player page
  762.  
  763.         savebin "pt3.bin",begin,end-begin
  764.