Subversion Repositories NedoOS

Rev

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

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