;	modplayer.a
;	~~~~~~~~~~~
; The music player routine for MED V3.20 (and OctaMED V2.0 4-channel) songs.
; Written by Teijo Kinnunen.
; If you find bugs in this routine, please notify:
;	Teijo Kinnunen
;	Oksantie 19
;	SF-86300  OULAINEN
;	FINLAND
;
; THIS CODE MAY BE FREELY USED AND DISTRIBUTED!

;============================================================================

MIDI	EQU	0	;1 = include MIDI code
AUDDEV	EQU	1	;1 = allocate channels using audio.device
SYNTH	EQU	1	;1 = include synth-sound handler
CHECK	EQU	1	;1 = do range checkings (track, sample in mem etc.)
RELVOL	EQU	1	;1 = include relative volume handling code
IFF53	EQU	1	;1 = play IFF 3- and 5-octave samples correctly
HOLD	EQU	1	;1 = handle hold/decay
;****** Timing control ******
VBLANK	EQU	0	;1 = use VBlank interrupt (when absolutely necessary)
CIAB	EQU	1	;1 = use CIAB timers (default)
; Please use CIAB whenever possible to avoid problems with variable
; VBlank speeds and to allow the use of command F01 - FF0 (set tempo)
; If both are set to 0, the timing is left for you (never set both to 1!!),
; then you just call _IntHandler for each timing pulse.

;============================================================================

;If you are making a demo/game with only a single tune you'd like to
;incorporate in the code (like "easyplayer.a" of MED V3), set the following
;flag to 1. This requires an assembler with INCBIN (or equivalent) directive.
;You have to type the module name into the INCBIN statement (located near the
;end of this file, on line 2052).

EASY	EQU	0

;Call _startmusic to play the music, and _endmusic to stop it (before
;exiting). Note: don't call _startmusic twice!! This would cause the module
;to be relocated twice (= Guru). If you need to stop and continue playing,
;don't use the EASY routines, use PlayModule/StopPlayer... instead.

;============================================================================

;the MMD0 structure offsets
mmd_id		EQU	0
mmd_modlen	EQU	4
mmd_songinfo	EQU	8
mmd_songlen	EQU	12	;currently unused
mmd_blockarr	EQU	16
mmd_blockarrlen	EQU	20
mmd_smplarr	EQU	24
mmd_smplarrlen	EQU	28
mmd_expdata	EQU	32
mmd_expsize	EQU	36
mmd_pstate	EQU	40 ; <0 = play song, 0 = don't play, >0 = play block
mmd_pblock	EQU	42
mmd_pline	EQU	44
mmd_pseqnum	EQU	46
mmd_actplayline	EQU	48
mmd_counter	EQU	50
mmd_songsleft	EQU	51

;the MMD0song structure
;Instrument data here (504 bytes = 63 * 8)
msng_numblocks	EQU	504
msng_songlen	EQU	506
msng_playseq	EQU	508
msng_deftempo	EQU	764
msng_playtransp	EQU	766
msng_flags	EQU	767
msng_reserved	EQU	768
msng_tempo2	EQU	769
msng_trkvol	EQU	770
msng_mastervol	EQU	786
msng_numsamples	EQU	787

;Instrument data
inst_repeat	EQU	0
inst_replen	EQU	2
inst_midich	EQU	4
inst_midipreset	EQU	5
inst_svol	EQU	6
inst_strans	EQU	7

;Audio hardware offsets
ac_ptr	EQU	$00
ac_len	EQU	$04
ac_per	EQU	$06
ac_vol	EQU	$08

;Trackdata sizes
T03SZ	EQU	88
T415SZ	EQU	18

		section	"text",code

	IFNE	EASY

		xdef	_startmusic,_endmusic

_startmusic	lea	easymod,a0
		bsr.w	_RelocModule
		bsr.w	_InitPlayer
		lea	easymod,a0
		bra.w	_PlayModule

_endmusic	bra.w	_RemPlayer
	ENDC



_ChannelOff:	;d0 = channel #
		lea	trackdataptrs(pc),a1
		lsl.b	#2,d0
		adda.w	d0,a1
		lsr.b	#2,d0
		movea.l	(a1),a1
	IFNE	MIDI
		move.b	trk_prevmidin(a1),d1	;is it MIDI??
		beq.s	notcomidi		;not a MIDI note
choff_midi:	clr.b	trk_prevmidin(a1)
		lea	noteondata(pc),a0
		move.b	d1,1(a0)
		move.b	trk_prevmidich(a1),(a0)	;prev MIDI channel
		clr.b	2(a0)
		or.b	#$90,(a0)		;note off
		moveq	#3,d0
		bra.w	_AddMIDIData
	ENDC
notcomidi:	cmp.b	#4,d0
		bge.s	notamigatrk
	IFNE	SYNTH
		clr.l	trk_synthptr(a1)
		clr.b	trk_synthtype(a1)
	ENDC
		moveq	#1,d1
		lsl.w	d0,d1
		move.w	d1,$dff096
notamigatrk:	rts

SoundOff:	move.l	d2,-(sp)
		moveq	#15,d2
SO_loop0	move.l	d2,d0
		bsr.s	_ChannelOff
		dbf	d2,SO_loop0
		lea	_module(pc),a0
		clr.l	(a0)		;play nothing
		move.l	(sp)+,d2
		rts

_PlayNote:	;d0(w) = trk #, d1 = note #, d2 = vol, d3(w) = instr # a3 = addr of instr
		moveq	#0,d4
		bset	d0,d4	;d4 is mask for this channel
		movea.l	mmd_smplarr(a6),a0
		add.w	d3,d3			;d3 = instr.num << 2
		add.w	d3,d3
		move.l	0(a0,d3.w),d5		;get address of instrument
	IFNE	MIDI
		bne.s	inmem
		tst.b	inst_midich(a3)		;is MIDI channel set
	ENDC
	IFNE	CHECK
		beq.w	pnote_rts		; NO!!!
	ENDC
inmem:		add.b	msng_playtransp(a4),d1	;add play transpose
		add.b	inst_strans(a3),d1	;and instr. transpose
		cmp.b	#4,d0
		bge.s	nodmaoff	;track # >= 4: not an Amiga channel
		move.l	d5,a1
	IFNE	SYNTH
		tst.l	d5
		beq.s	stpdma
		tst.b	trk_synthtype(a5)
		ble.s	stpdma		;prev. type = sample/hybrid
		cmp.w	#-1,4(a1)	;type == SYNTHETIC??
		beq.s	nostpdma
	ENDC
stpdma:		move.w	d4,$dff096		;stop this channel (dmacon)
nostpdma:
	IFNE	SYNTH
		clr.l	trk_synthptr(a5)
	ENDC
nodmaoff:
	IFNE	MIDI
		move.b	trk_prevmidin(a5),d3	;get prev. midi note
		beq.s	noprevmidi
		clr.b	trk_prevmidin(a5)
		lea	noteondata+2(pc),a0
		clr.b	(a0)				;volume = 0
		move.b	d3,-(a0)
		move.b	trk_prevmidich(a5),-(a0)	;prev midi channel
		or.b	#$90,(a0)			;note off
		movem.w	d0-d1,-(sp)
		moveq	#3,d0
		bsr.w	_AddMIDIData
		movem.w	(sp)+,d0-d1
noprevmidi:	tst.b	inst_midich(a3)
		bne.w	handleMIDInote
	ENDC
	IFNE	CHECK
		cmp.w	#4,d0		;track > 3???
		bge.w	pnote_rts	;no Amiga instruments here!!!
	ENDC
; handle decay (for tracks 0 - 3 only!!)
	IFNE	HOLD
		clr.b	trk_fadespd(a5)		;no fade yet..
		move.b	trk_initdecay(a5),trk_decay(a5)	;set decay
	ENDC
		clr.b	trk_vibroffs(a5)	;clr vibrato offset
		or.w	d4,dmaonmsk
		move.l	d5,a0
		subq.b	#1,d1
	IFNE	SYNTH
		tst.w	4(a0)
		bmi.w	handleSynthnote
		clr.b	trk_synthtype(a5)
	ENDC
tlwtst0:	tst.b	d1
		bpl.s	notenot2low
		add.b	#12,d1	;note was too low, octave up
		bra.s	tlwtst0
notenot2low:	cmp.b	#62,d1
		ble.s	endpttest
		sub.b	#12,d1	;note was too high, octave down
endpttest:
		moveq	#0,d4
	IFNE	IFF53
		move.w	4(a0),d0	;Soitin-struct in a0
		bne.s	iff5or3oct	;note # in d1 (0 - ...)
	ENDC
		move.l	a0,d0
		lea	_periods(pc),a0
		move.l	a0,trk_periodtbl(a5)
		add.b	d1,d1
		move.w	0(a0,d1.w),d5 ;put period to d5
		move.l	d0,a0
		addq.l	#6,d0		;Skip structure
		move.l	(a0),d1		;length
		move.w	(a3),d4		;inst_repeat
		move.w	inst_replen(a3),d3
	IFNE	IFF53
		bra.s	end_getdata
shiftcnt	dc.b	4,3,2,1,1,0,2,2,1,1,0,0
mullencnt	dc.b	15,7,3,1,1,0,3,3,1,1,0,0
octstart	dc.b	12,12,12,12,24,24,0,12,12,24,24,36
iff5or3oct:	movem.l	d6-d7,-(sp)
		moveq	#0,d7
		move.w	d1,d7
		divu	#12,d7	;octave #
		move.l	d7,d5
		swap	d5	;note number in this oct (0-11) is in d5
		move.l	(a0),d1
		cmp.b	#2,d0
		bne.s	no3oct
		addq.l	#6,d7
		divu	#7,d1	;get length of the 1st octave
		bra.s	no5oct
no3oct:		divu	#31,d1	;get length of the 1st octave (5 octaves)
no5oct:		move.l	d1,d0		;d0 and d1 = length of the 1st oct
		move.w	(a3),d4		;inst_repeat
		move.w	inst_replen(a3),d3
		moveq	#0,d6
		move.b	shiftcnt(pc,d7.w),d6
		lsl.w	d6,d4
		lsl.w	d6,d3
		lsl.w	d6,d1
		move.b	mullencnt(pc,d7.w),d6
		mulu	d6,d0		;offset of this oct from 1st oct
		add.l	a0,d0		;add base address to offset
		addq.l	#6,d0		;skip structure
		lea	_periods(pc),a1
		add.b	octstart(pc,d7.w),d5
		add.b	d5,d5
		move.w	0(a1,d5.w),d5
		movem.l	(sp)+,d6-d7
	ENDC
end_getdata	movea.l	trk_audioaddr(a5),a1 ;base of this channel's regs
		move.l	d0,(a1)+		;put it in ac_ptr (= 0)
		cmp.w	#1,d3
		bhi.s	repeat
		
		move.l	#_chipzero,trk_sampleptr(a5) ;pointer of zero word
		move.w	#1,trk_samplelen(a5)	;length: 1 word
		lsr.l	#1,d1			;shift length right
		move.w	d1,(a1)+	;and put to custom chip (ac_len)
		bra.s	retsn1

repeat:		tst.w	d4
		beq.s	begin0		;rep. start < 2
		move.w	d4,(a1)+	;move repeat to hardware
		bra.s	beginn0
begin0:		move.w	d3,(a1)+	;ac_len
beginn0:	add.l	d4,d4		;shift
		add.l	d4,d0		;d0 = starting address of repeat
		move.l	d0,trk_sampleptr(a5)	;remember rep. start
		move.w	d3,trk_samplelen(a5)	;remember rep. length
				
retsn1:		move.w	d5,(a1)+ ;getinsdata puts period to d5 (a1 = ac_per)
		move.w	d2,(a1)	;a1 = ac_vol
		move.w	d5,trk_prevper(a5)
	IFNE	SYNTH
		tst.b	trk_synthtype(a5)
		bne.w	hSn2
	ENDC
pnote_rts	rts

	IFNE	MIDI
handleMIDInote:	add.b	#23,d1		;2 octaves higher and -1
		bpl.s	hmn_not2low	;note number not too low
hmn_2low	add.b	#12,d1		;it was too low, 1 octave up
		bmi.s	hmn_2low
hmn_not2low
		add.b	d2,d2		;volume 0 - 63 => 0 - 127
		subq.b	#1,d2		;if 128 => 127
		bpl.s	hmn_notvolu0
		moveq	#0,d2
hmn_notvolu0
		moveq	#0,d5
		move.b	inst_midich(a3),d5	;get midi chan of this instrument
		bpl.s	hmn_nosmof	;bit 7 clear
		clr.b	trk_prevmidin(a5)	;suppress note off!
		and.b	#$1F,d5		;clear all flag bits etc...
		bra.s	hmn_smof
hmn_nosmof	move.b	d1,trk_prevmidin(a5)
hmn_smof	subq.b	#1,d5		;from 1-16 to 0-15
		move.b	d5,trk_prevmidich(a5)	;save to prev midi channel

		move.b	inst_midipreset(a3),d0	;get preset #
		beq.s	nochgpres	;zero = no preset
		cmp.b	prevmidicpres(pc,d5.w),d0	;is this previous preset ??
		beq.s	nochgpres	;yes...no need to change
		lea	prevmidicpres(pc,d5.w),a1
		move.b	d0,(a1)		;save preset to prevmidicpres
		subq.b	#1,d0		;sub 1 to get 0 - 127
		lea	preschgdata+1(pc),a0
		move.b	d0,(a0)		;push the number to second byte
		move.b	#$c0,-(a0)	;command: $C
		or.b	d5,(a0)		;"or" midi channel
		moveq	#2,d0
		move.w	d1,-(sp)
		bsr.w	_AddMIDIData
		move.w	(sp)+,d1
		tst.b	d2
		beq.s	hmn_suppress	;vol = 0, don't send NOTE ON

nochgpres	lea	bytesinnotebuff(pc),a0
		movea.l	a0,a1
		adda.w	(a0)+,a0
		or.b	#$90,d5		;MIDI: Note on
		move.b	d5,(a0)+	;MIDI msg Note on & channel
		move.b	d1,(a0)+	;MIDI msg note #
		move.b	d2,(a0)		;MIDI msg volume
		beq.s	hmn_suppress	;vol = 0 -> no note
		addq.w	#3,(a1)
		rts
hmn_suppress	clr.b	trk_prevmidin(a5)
		rts

prevmidicpres:	dc.l	0,0,0,0 ; 16 bytes
	ENDC

	IFNE	SYNTH
handleSynthnote:
		move.b	d1,trk_prevnote2(a5)
		move.l	a0,trk_synthptr(a5)
		cmp.w	#-2,4(a0)	;HYBRID??
		bne.s	hSn_nossn
		st	trk_synthtype(a5)
		movea.l	278(a0),a0	;yep, get the waveform pointer
		bra.w	tlwtst0		;go and play it
hSn_nossn:	move.b	#1,trk_synthtype(a5)
		lea	_synthper(pc),a1
		move.l	a1,trk_periodtbl(a5) ;save table ptr for synth periods
		add.w	d1,d1
		move.w	0(a1,d1.w),d1
		movea.l	trk_audioaddr(a5),a1
		move.w	d1,trk_prevper(a5)
		move.w	d1,ac_per(a1)
		clr.l	trk_sampleptr(a5)
hSn2:		lea	trk_arpgoffs(a5),a1
		clr.l	(a1)+
		clr.l	(a1)+
		clr.l	(a1)+
		clr.l	(a1)+
		clr.l	(a1)+
		clr.l	(a1)+
		move.l	#sinetable,(a1)+
		clr.w	(a1)+
		movea.l	trk_synthptr(a5),a0
                move.w	18(a0),(a1)+
                clr.b	(a1)
                cmp.b	#$E,trk_cmd(a5)
                bne.s	synth_start
                move.b	trk_cmdqual(a5),trk_wfcmd+1(a5)

synth_start:	move.l	a3,-(sp)
		movea.l	trk_audioaddr(a5),a3	;audio channel base address
		subq.b	#1,trk_volxcnt(a5)	;decrease execute counter..
		bgt.w	synth_wftbl		;not 0...go to waveform
		move.b	trk_initvolxspd(a5),trk_volxcnt(a5) ;reset counter
		move.b	trk_volchgspd(a5),d0	;volume change??
		beq.s	synth_nochgvol		;no.
		add.b	trk_synvol(a5),d0	;add previous volume
		bpl.s	synth_voln2l		;not negative
		moveq	#0,d0			;was negative => 0
synth_voln2l:	cmp.b	#$40,d0			;too high??
		ble.s	synth_voln2h		;not 2 high.
		moveq	#$40,d0			;was 2 high => 64
synth_voln2h:	move.b	d0,trk_synvol(a5)	;remember new...
		move.b	d0,ac_vol+1(a3)		;and change it
synth_nochgvol:	move.l	trk_envptr(a5),d1	;envelope pointer
		beq.s	synth_novolenv
		movea.l	d1,a1
		move.b	(a1)+,d0
		add.b	#128,d0
		lsr.b	#2,d0
		move.b	d0,trk_synvol(a5)
		move.b	d0,ac_vol+1(a3)
		addq.b	#1,trk_envcount(a5)
		bpl.s	synth_endenv
		clr.b	trk_envcount(a5)
		move.l	trk_envrestart(a5),a1
synth_endenv	move.l	a1,trk_envptr(a5)
synth_novolenv	move.w	trk_volcmd(a5),d0	;get table position ptr
		tst.b	trk_volwait(a5)		;WAI(t) active
		beq.s	synth_getvolcmd		;no
		subq.b	#1,trk_volwait(a5)	;yep, decr wait ctr
		ble.s	synth_getvolcmd		;0 => continue
		bra.w	synth_wftbl		;> 0 => still wait
synth_inccnt:	addq.b	#1,d0
synth_getvolcmd:
		addq.b	#1,d0			;advance pointer
		move.b	21(a0,d0.w),d1		;get command
		bmi.s	synth_cmd		;negative = command
		move.b	d1,trk_synvol(a5)	;set synthvol
		move.b	d1,ac_vol+1(a3)		;change it!!
		bra.w	synth_endvol		;end of volume executing
synth_cmd:	and.w	#$000f,d1
		add.b	d1,d1
		move.w	synth_vtbl(pc,d1.w),d1
		jmp	syv(pc,d1.w)
synth_vtbl:	dc.w	syv_f0-syv,syv_f1-syv,syv_f2-syv,syv_f3-syv
		dc.w	syv_f4-syv,syv_f5-syv,syv_f6-syv
		dc.w	synth_endvol-syv,synth_endvol-syv,synth_endvol-syv
		dc.w	syv_fa-syv,syv_ff-syv,synth_endvol-syv
		dc.w	synth_endvol-syv,syv_fe-syv,syv_ff-syv
syv:
syv_fe:		move.b	22(a0,d0.w),d0		;JMP
		bra.s	synth_getvolcmd
syv_f0:		move.b	22(a0,d0.w),trk_initvolxspd(a5) ;change volume ex. speed
		bra.s	synth_inccnt
syv_f1:		move.b	22(a0,d0.w),trk_volwait(a5)	;WAI(t)
		addq.b	#1,d0
		bra.s	synth_endvol
syv_f3:		move.b	22(a0,d0.w),trk_volchgspd(a5) ;set volume slide up
		bra.s	synth_inccnt
syv_f2:		move.b	22(a0,d0.w),d1
		neg.b	d1
		move.b	d1,trk_volchgspd(a5) ;set volume slide down
		bra.s	synth_inccnt
syv_fa:		move.b	22(a0,d0.w),trk_wfcmd+1(a5) ;JWS (jump wform sequence)
		clr.b	trk_wfwait(a5)
		bra.s	synth_inccnt
syv_f4:		move.b	22(a0,d0.w),d1
		bsr.s	synth_getwf
		clr.l	trk_envrestart(a5)
syv_f4end	move.l	a1,trk_envptr(a5)
		clr.b	trk_envcount(a5)
		bra.w	synth_inccnt
syv_f5:		move.b	22(a0,d0.w),d1
		bsr.s	synth_getwf
		move.l	a1,trk_envrestart(a5)
		bra.s	syv_f4end
syv_f6		clr.l	trk_envptr(a5)
		bra.w	synth_getvolcmd
synth_getwf	ext.w	d1	;d1 = wform number, returns ptr in a1
		add.w	d1,d1	;create index
		add.w	d1,d1
		lea	278(a0),a1
		adda.w	d1,a1
		movea.l	(a1),a1		;get wform address
		addq.l	#2,a1		;skip length
		rts
syv_ff:		subq.b	#1,d0
synth_endvol:	move.w	d0,trk_volcmd(a5)
synth_wftbl:	move.b	trk_synvol(a5),trk_prevvol(a5)
		adda.w	#158,a0
		subq.b	#1,trk_wfxcnt(a5)	;decr. wf speed counter
		bgt.w	synth_arpeggio		;not yet...
		move.b	trk_initwfxspd(a5),trk_wfxcnt(a5) ;restore speed counter
		move.w	trk_wfcmd(a5),d0	;get table pos offset
		move.w	trk_wfchgspd(a5),d1	;CHU/CHD ??
		beq.s	synth_tstwfwai		;0 = no change
wytanwet:	add.w	trk_perchg(a5),d1	;add value to current change
		move.w	d1,trk_perchg(a5)	;remember amount of change
		add.w	trk_prevper(a5),d1	;add initial period to it
		cmp.w	#113,d1			;overflow??
		bge.s	synth_pern2h
		moveq	#113,d1
synth_pern2h:	move.w	d1,ac_per(a3)		;push the changed period
synth_tstwfwai:	tst.b	trk_wfwait(a5)		;WAI ??
		beq.s	synth_getwfcmd		;not waiting...
		subq.b	#1,trk_wfwait(a5)	;decr wait counter
		beq.s	synth_getwfcmd		;waiting finished
		bra.w	synth_arpeggio		;still sleep...
synth_incwfc:	addq.b	#1,d0
synth_getwfcmd:	addq.b	#1,d0			;advance position counter
		move.b	-9(a0,d0.w),d1		;get command
		bmi.s	synth_wfcmd		;negative = command
		ext.w	d1
		add.w	d1,d1
		add.w	d1,d1
		movea.l	120(a0,d1.w),a1
		move.w	(a1)+,ac_len(a3)	;push waveform length
		move.l	a1,(a3) 		;and the new pointer (0 = ac_ptr)
		bra.w	synth_wfend		;no new commands now...
synth_wfcmd:	and.w	#$000f,d1		;get the right nibble
		add.b	d1,d1			;* 2
		move.w	synth_wfctbl(pc,d1.w),d1
		jmp	syw(pc,d1.w)		;jump to command
synth_wfctbl:	dc.w	syw_f0-syw,syw_f1-syw,syw_f2-syw,syw_f3-syw,syw_f4-syw
		dc.w	syw_f5-syw,syw_f6-syw,syw_f7-syw,synth_wfend-syw
		dc.w	synth_wfend-syw,syw_fa-syw,syw_ff-syw
		dc.w	syw_fc-syw,synth_getwfcmd-syw,syw_fe-syw,syw_ff-syw
syw:
syw_f7:		move.b	-8(a0,d0.w),d1
		ext.w	d1
		add.w	d1,d1
		add.w	d1,d1
		movea.l	120(a0,d1.w),a1
		addq.l	#2,a1
		move.l	a1,trk_synvibwf(a5)
		bra.s	synth_incwfc
syw_fe:		move.b	-8(a0,d0.w),d0		;jump (JMP)
		bra.s	synth_getwfcmd
syw_fc:		move.w	d0,trk_arpsoffs(a5)	;new arpeggio begin
		move.w	d0,trk_arpgoffs(a5)
synth_findare:	addq.b	#1,d0
		tst.b	-9(a0,d0.w)
		bpl.s	synth_findare
		bra.s	synth_getwfcmd
syw_f0:		move.b	-8(a0,d0.w),trk_initwfxspd(a5)	;new waveform speed
		bra	synth_incwfc
syw_f1:		move.b	-8(a0,d0.w),trk_wfwait(a5)	;wait waveform
		addq.b	#1,d0
		bra.s	synth_wfend
syw_f4:		move.b	-8(a0,d0.w),trk_synvibdep+1(a5)	;set vibrato depth
		bra.w	synth_incwfc
syw_f5:		move.b	-8(a0,d0.w),trk_synthvibspd+1(a5) ;set vibrato speed
		addq.b	#1,trk_synthvibspd+1(a5)
		bra.w	synth_incwfc
syw_f2:		moveq	#0,d1			;set slide down
		move.b	-8(a0,d0.w),d1
synth_setsld:	move.w	d1,trk_wfchgspd(a5)
		bra.w	synth_incwfc
syw_f3:		move.b	-8(a0,d0.w),d1		;set slide up
		neg.b	d1
		ext.w	d1
		bra.s	synth_setsld
syw_f6:		clr.w	trk_perchg(a5)		;reset period
		move.w	trk_prevper(a5),ac_per(a3)
		bra.w	synth_getwfcmd
syw_fa:		move.b	-8(a0,d0.w),trk_volcmd+1(a5) ;JVS (jump volume sequence)
		clr.b	trk_volwait(a5)
		bra.w	synth_incwfc
syw_ff:		subq.b	#1,d0		;pointer = END - 1
synth_wfend:	move.w	d0,trk_wfcmd(a5)
synth_arpeggio:	move.w	trk_arpgoffs(a5),d0
		beq.s	synth_vibrato
		moveq	#0,d1
		move.b	-8(a0,d0.w),d1
		add.b	trk_prevnote2(a5),d1
		movea.l	trk_periodtbl(a5),a1	;get period table
		add.w	d1,d1
		move.w	0(a1,d1.w),d1
		add.w	trk_perchg(a5),d1
		move.w	d1,trk_prevper(a5)
		move.w	d1,ac_per(a3)
		addq.b	#1,d0
		tst.b	-8(a0,d0.w)
		bpl.s	synth_noarpres
		move.w	trk_arpsoffs(a5),d0
synth_noarpres:	move.w	d0,trk_arpgoffs(a5)
synth_vibrato:	move.w	trk_synvibdep(a5),d1	;get vibrato depth
		beq.s	synth_rts		;0 => no vibrato
		move.w	trk_synviboffs(a5),d0	;get offset
		lsr.w	#4,d0			;/ 16
		and.w	#$1f,d0			;sinetable offset (0-31)
		movea.l trk_synvibwf(a5),a0
		move.b	0(a0,d0.w),d0   	;get a byte
		ext.w	d0			;to word
		muls	d1,d0			;amplify (* depth)
		asr.w	#8,d0			;and divide by 64
		move.w	trk_prevper(a5),d1	;get the old period
		add.w	d0,d1			;add vibrato...
		add.w	trk_perchg(a5),d1	;and pitch change...
		move.w	d1,ac_per(a3)		;change.
		move.w	trk_synthvibspd(a5),d0	;vibrato speed
		add.w	d0,trk_synviboffs(a5)	;add to offset
synth_rts:	move.l	(sp)+,a3
		rts
	ENDC
sinetable:	dc.b	0,25,49,71,90,106,117,125,127,125,117,106,90,71,49
		dc.b	25,0,-25,-49,-71,-90,-106,-117,-125,-127,-125,-117
		dc.b	-106,-90,-71,-49,-25,0

_Wait1line:	move.l	d0,-(sp)	;d1 = vsync counters to wait - 1
wl0:		move.b	$dff007,d0
wl1:		cmp.b	$dff007,d0
		beq.s	wl1
		dbf	d1,wl0
		move.l	(sp)+,d0
		rts
pushnewvals:	movea.l	(a1)+,a5
		lsr.b	#1,d0
		bcc.s	rpnewv
		move.l	trk_sampleptr(a5),d1
		beq.s	rpnewv
		movea.l	trk_audioaddr(a5),a0
		move.l	d1,(a0)+		;0 = ac_ptr
		move.w	trk_samplelen(a5),(a0)	;4 = ac_len
rpnewv:		rts
_StartDMA:		;This small routine turns on audio DMA
		move.w	dmaonmsk(pc),d0	;dmaonmsk contains the mask of
	IFNE MIDI
		beq.s	sdma_nodmaon	;the channels that must be turned on
	ENDC
	IFEQ MIDI
		beq.s	rpnewv
	ENDC
		bset	#15,d0	;DMAF_SETCLR: set these bits in dmacon
		moveq	#80,d1
	IFNE	SYNTH
		add.w	d1,d1	;sometimes double wait time is required
	ENDC
		bsr.s	_Wait1line
		move.w	d0,$dff096	;do that!!!
		moveq	#79,d1
		bsr.s	_Wait1line
		lea	trackdataptrs(pc),a1
		bsr.s	pushnewvals
		bsr.s	pushnewvals
		bsr.s	pushnewvals
	IFNE MIDI
		bsr.s	pushnewvals
sdma_nodmaon	lea	bytesinnotebuff(pc),a0
		move.w	(a0)+,d0
		beq.s	rpnewv
		bra.w	_AddMIDIData
	ENDC
	IFEQ MIDI
		bra.s	pushnewvals
	ENDC

	IFNE MIDI
prevmidipbend:	dc.w	$2000,$2000,$2000,$2000,$2000,$2000,$2000,$2000
		dc.w	$2000,$2000,$2000,$2000,$2000,$2000,$2000,$2000
	ENDC
; TRACK-data structures (see definitions at the end of this file)
t03d:		ds.b	20
		dc.l	$dff0a0
		ds.b	64+20
		dc.l	$dff0b0
		ds.b	64+20
		dc.l	$dff0c0
		ds.b	64+20
		dc.l	$dff0d0
		ds.b	64
t415d:		ds.b	4*T415SZ
t815d:		ds.b	8*T415SZ	;8 bytes * 12 tracks = 96 bytes
trackdataptrs:	dc.l	t03d,t03d+T03SZ,t03d+2*T03SZ,t03d+3*T03SZ
		dc.l	t415d,t415d+T415SZ,t415d+2*T415SZ,t415d+3*T415SZ
		dc.l	t815d,t815d+T415SZ,t815d+2*T415SZ,t815d+3*T415SZ
		dc.l	t815d+4*T415SZ,t815d+5*T415SZ,t815d+6*T415SZ
		dc.l	t815d+7*T415SZ
numtracks:	dc.w	0
numlines:	dc.w	0
nextblock:	dc.b	0,0


_IntHandler:	movem.l	d2-d7/a2-a5,-(sp)
		movea.l	_module(pc),a6	;a6 = pointer of MMD0
		move.l	a6,d0
		beq.w	plr_exit
		tst.w	mmd_pstate(a6)
		beq.w	plr_exit
	IFNE	MIDI
		clr.l	dmaonmsk
	ENDC
	IFEQ	MIDI
		clr.w	dmaonmsk
	ENDC
		movea.l	mmd_songinfo(a6),a4
		move.l	a4,d0
		beq.w	plr_exit
		moveq	#0,d3
		move.b	mmd_counter(a6),d3
		addq.b	#1,d3
		cmp.b	msng_tempo2(a4),d3
		bge.s	plr_pnewnote	;play new note
		move.b	d3,mmd_counter(a6)
		bne.w	nonewnote	;do just fx
; --- new note!! first get address of current block
plr_pnewnote:	clr.b	mmd_counter(a6)
; --- now start to play it
		move.w	mmd_pblock(a6),d0
		movea.l	mmd_blockarr(a6),a0
		add.w	d0,d0
		add.w	d0,d0
		movea.l	0(a0,d0.w),a2	;block...
		move.b	(a2)+,numtracks+1
		move.b	(a2)+,numlines+1
		move.w	mmd_pline(a6),d0
		move.w	d0,d1
		add.w	d0,d0	;d0 * 2
		add.w	d1,d0	;+ d0 = d0 * 3
		mulu	numtracks(pc),d0
		adda.w	d0,a2		;a2 => pointer of curr. note
		moveq	#0,d7		;number of track
		pea	trackdataptrs(pc)
plr_loop0:	moveq	#0,d5
		move.l	(sp),a1
		movea.l	(a1)+,a5	;get address of this track's struct
		move.l	a1,(sp)
; ---------------- get the note numbers
		move.b	(a2)+,d5	;get the number of this note
		move.b	(a2)+,d6	;and the 4 numbers containing fx
		move.b	(a2)+,trk_cmdqual(a5)	;get & save the fx numbers
; ---------------- clear some instrument # flags
		moveq	#0,d4		;d4 is a flag: if set, instr. is
		moveq	#0,d3		;in range G-V. If clr, it's 1-F.
; ---------------- and set them, if needed
		bclr	#7,d5		;d3 is also a flag. If it's set,
		sne	d4		;the instr. is in range 10 - 1V
		bclr	#6,d5
		sne	d3
; ---------------- check if there's an instrument number
		move.b	d6,d0
		and.w	#$f0,d0		;d0 now contains only the # of instr
		bne.s	instnum		;instrument number is not 0
		tst.b	d4		;maybe it's G (instr. #0, d4 set)
		bne.s	instnum		;yes, it really was G!!
		tst.b	d3
		beq.s	noinstnum	;it wasn't 10 - 1V either..
; ---------------- if there was, get it
instnum:	lsr.b	#4,d0		;shift it right to get number 0-F
		tst.b	d4
		beq.s	nogtov2
		add.w	#16,d0		;if G-V, add 16 to the number
nogtov2:	tst.b	d3
		beq.s	no10to1v
		add.w	#32,d0
; ---------------- finally, save the number
no10to1v:	subq.b	#1,d0
		move.b	d0,trk_previnstr(a5) ;remember instr. number!
	IFNE	HOLD
; ---------------- remember hold/decay values
		lea	holdvals(pc),a0
		move.b	0(a0,d0.w),trk_inithold(a5)
		move.b	63(a0,d0.w),trk_initdecay(a5)
	ENDC
; ---------------- get the pointer of data's of this sample in Song-struct
		asl.w	#3,d0
		lea	0(a4,d0.w),a3	;a3 contains now address of it
		move.l	a3,trk_previnstra(a5)
		moveq	#0,d0
; ---------------- get volume and make it relative (1 - 100 %)
	IFNE	RELVOL
		move.b	inst_svol(a3),d0
		mulu	trk_trackvol(a5),d0
		lsr.w	#8,d0
		move.b	d0,trk_prevvol(a5) ;vol of this instr
	ENDC
	IFEQ	RELVOL
		move.b	inst_svol(a3),trk_prevvol(a5)
	ENDC
; ---------------- remember transpose
		move.b	inst_strans(a3),trk_stransp(a5)
; ---------------- check the commands
noinstnum	and.b	#$0f,d6		;now check only the effect part
		move.b	d6,trk_cmd(a5)	;save the effect number
		beq.w	plr_nocmd	;no effect
		move.b	d6,d0
		move.b	trk_cmdqual(a5),d6	;get qualifier...
; ---------------- there was a command (effect), but which one??
		cmp.b	#$0f,d0		;yes effect...is it Tempo???
		bne.w	not0f		;not Tempo
; ---------------- it was tempo (F)
		tst.b	d6		;test effect qual..
		beq.s	fx0fchgblck	;if effect qualifier (last 2 #'s)..
		cmp.b	#$f0,d6		;..is zero, go to next block
		bhi.s	fx0fspecial	;if it's F1-FF something special
; ---------------- just an ordinary "change tempo"-request
	IFNE	CIAB
		moveq	#0,d0		;will happen!!!
		move.b	d6,d0
		bsr	_SetTempo	;change The Tempo
		move.b	d6,msng_deftempo(a4)
	ENDC
		bra.w	plr_nocmd
; ---------------- it was FFx
fx0fspecial:	cmp.b	#$f2,d6
		bne.s	isfxfe
; ---------------- it was FF2, nothing to do now
		move.b	d5,(a5)
		moveq	#0,d5
		bra.w	plr_nocmd
isfxfe:		cmp.b	#$fe,d6
		bne.s	notcmdfe
; ---------------- it was FFE, stop playing
		clr.w	mmd_pstate(a6)
	IFNE	CIAB
		movea.l	craddr(pc),a0
		bclr	#0,(a0)
	ENDC
		bsr.w	SoundOff
		addq.l	#4,sp
		bra.w	plr_exit
notcmdfe:	cmp.b	#$fd,d6 ;change period
		bne.s	isfxff
; ---------------- FFD, change the period, don't replay the note
	IFNE	CHECK
		cmp.w	#4,d7 ;no tracks 4 - 15
		bge.w	plr_nocmd
	ENDC
		movea.l	trk_periodtbl(a5),a0	;period table
		subq.b	#1,d5    ;sub 1 to make "real" note number
		bmi.w	plr_endloop0	;under zero, do nothing
		add.b	d5,d5
		move.w	0(a0,d5.w),d0 ;get the period
		movea.l	trk_audioaddr(a5),a0
		move.w	d0,ac_per(a0) ;push the period
		moveq	#0,d5 ;and clear it so that it won't be replayed
		bra.w	plr_nocmd	;done!!
isfxff:		cmp.b	#$ff,d6		;note off??
		bne.w	plr_nocmd
		move.w	d7,d0
		bsr.w	_ChannelOff
		bra.w	plr_nocmd
; ---------------- F00, called Pattern Break in ST
fx0fchgblck:	addq.b	#1,nextblock	;next block... (F00)
		bra.w	plr_nocmd
; ---------------- was not Fxx, then it's something else!!
not0f:		cmp.b	#$0e,d0
		bne.s	not0e
		move.b	d6,trk_wfcmd+1(a5) ;set waveform command position ptr
		bra.w	plr_nocmd
not0e:		cmp.b	#$0c,d0		;new volume???
		bne.s	not0c		;NO
; ---------------- change volume
		move.b	d6,d0
		bpl.s	plr_nosetdefvol
		and.b	#$7F,d0
	IFNE	CHECK
		cmp.b	#64,d0
		bgt.s	go_nocmd
	ENDC
		moveq	#0,d1
		move.b	trk_previnstr(a5),d1
		asl.w	#3,d1
		move.b	d0,inst_svol(a4,d1.w)	;set new svol
		bra.s	plr_setvol
plr_nosetdefvol	btst	#4,msng_flags(a4)	;look at flags
		bne.s	volhex
		lsr.b	#4,d0		;get number from left
		mulu	#10,d0		;number of tens
		move.b	d6,d1		;get again
		and.b	#$0f,d1		;this time don't get tens
		add.b	d1,d0		;add them
volhex:
	IFNE	CHECK
		cmp.b	#64,d0
		bhi.s	go_nocmd
	ENDC
plr_setvol
	IFNE	RELVOL
		mulu	trk_trackvol(a5),d0
		lsr.w	#8,d0
	ENDC
		move.b	d0,trk_prevvol(a5)
go_nocmd	bra.w	plr_nocmd
; ---------------- tempo2 change??
not0c:		cmp.b	#$09,d0
		bne.s	not09
	IFNE	CHECK
		and.b	#$1F,d6
		bne.s	fx9chk
		moveq	#$20,d6
	ENDC
fx9chk:		move.b	d6,msng_tempo2(a4)
		bra.s	plr_nocmd
; ---------------- note off time set??
	IFNE	HOLD
not09:		cmp.b	#$08,d0
		bne.s	not08
		move.b	d6,d0
		lsr.b	#4,d6		;extract left  nibble
		and.b	#$0f,d0		; "   "  right  "  "
		move.b	d6,trk_initdecay(a5)	;left = decay
		move.b	d0,trk_inithold(a5)	;right = hold
		bra.s	plr_nocmd
	ENDC
	IFEQ	HOLD
not09
	ENDC
; ---------------- cmd Bxx, "position jump", like Goto, yäk!!
not08:		cmp.b	#$0b,d0
		bne.s	not0b
		move.w	d6,d0
		and.w	#$00ff,d0
	IFNE	CHECK
		cmp.w	msng_songlen(a4),d0	;test the song length
		bhi.s	plr_nocmd
	ENDC
		move.w	d0,mmd_pseqnum(a6)
		st	nextblock	; = -1
		bra.s	plr_nocmd
; ---------------- try portamento (3)
not0b:		cmp.b	#$03,d0
		bne.s	plr_nocmd
		subq.b	#1,d5		;subtract note number
		bpl.s	plr_fx3note	;there's a note...
		tst.b	d6		;qual??
		beq.s	plr_endloop0	;0 -> do nothing
		bra.s	plr_setfx3spd	;not 0 -> set new speed
plr_fx3note:
	IFNE	CHECK
		cmp.w	#4,d7
		bge.s	plr_endloop0	;hey, what are you trying to do??
	ENDC
		movea.l	trk_periodtbl(a5),a0
		add.b	msng_playtransp(a4),d5	;play transpose
		add.b	trk_stransp(a5),d5 ;and instrument transpose
		bmi.s	plr_endloop0	;again.. too low
		add.w	d5,d5
		move.w	0(a0,d5.w),trk_porttrgper(a5) ;period of this note is the target
plr_setfx3spd:	move.b	d6,trk_prevportspd(a5)	;remember size
		moveq	#0,d5	;don't play this one
; ---------------- everything is checked now: play or not to play??
plr_nocmd:	tst.b	d5	;Now we'll check if we have to play a note
		beq.s	plr_endloop0	;no.
; ---------------- we decided to play
		move.b	d5,(a5)
		move.w	d7,d0
		move.w	d5,d1
		moveq	#0,d2
		move.b	trk_prevvol(a5),d2	;get volume
		moveq	#0,d3
		move.b	trk_previnstr(a5),d3	;instr #
		movea.l	trk_previnstra(a5),a3	;instr data address
; ---------------- does this instrument have holding??
	IFNE	HOLD
		move.b	trk_inithold(a5),trk_noteoffcnt(a5) ;initialize hold
		bne.s	plr_holdok	;not 0 -> OK
		st	trk_noteoffcnt(a5)	;0 -> hold = 0xff (-1)
	ENDC
; ---------------- and finally:
plr_holdok:	bsr	_PlayNote	;play it!!!!!!!!!!!
; ---------------- end of loop: handle next track, or quit
plr_endloop0:	addq.b	#1,d7
		cmp.w	numtracks(pc),d7
		blt.w	plr_loop0
		addq.l	#4,sp		;trackdataptrs

; and advance song pointers
		lea	nextblock(pc),a2
		move.w	mmd_pline(a6),d1	;pline
		addq.w	#1,d1			;advance line
		cmp.w	numlines(pc),d1		;advance block?
		bgt.s	plr_chgblock		;yes
		tst.b	(a2)			;command F00 ??
		beq.s	plr_nochgblock		;no, don't change block
plr_chgblock:	moveq	#0,d1			;clear the line number
		tst.w	mmd_pstate(a6)		;play block or play song
		bpl.s	plr_nonewseq		;play block only...
		move.w	mmd_pseqnum(a6),d0	;get play sequence number
		tst.b	(a2)
		bmi.s	plr_noadvseq	;Bxx sets nextblock to 0xff (= neg)
		addq.w	#1,d0			;advance sequence number
plr_noadvseq:	cmp.w	msng_songlen(a4),d0	;is this the highest seq number??
		blt.s	plr_notagain		;no.
		moveq	#0,d0			;yes: play song again
		moveq	#0,d1			;...forever
plr_notagain:	move.b	d0,mmd_pseqnum+1(a6)	;remember new playseq-#
		lea	msng_playseq(a4),a0	;offset of sequence table
		move.b	0(a0,d0.w),d0		;get number of the block
	IFNE	CHECK
		cmp.b	msng_numblocks+1(a4),d0	;beyond last block??
		blt.s	plr_nolstblk		;no..
		moveq	#0,d0			;play block 0
	ENDC
plr_nolstblk:	move.b	d0,mmd_pblock+1(a6)	;store pblock
plr_nonewseq:	clr.b	(a2)			;clear this if F00 set it
plr_nochgblock:	move.w	d1,mmd_pline(a6)	;set new pline

	IFNE	HOLD
		movea.l	mmd_blockarr(a6),a0
		move.w	mmd_pblock(a6),d0
		add.w	d0,d0
		add.w	d0,d0
		movea.l	0(a0,d0.w),a2
		move.b	(a2),d7			;# of tracks
		move.w	mmd_pline(a6),d0	;play line
		move.w	d0,d1
		add.w	d0,d0	;d0 * 2
		add.w	d1,d0	;+ d0 = d0 * 3
		mulu	d7,d0
		lea	2(a2,d0.w),a2
		move.b	msng_tempo2(a4),d3	;interrupts/note
		lea	trackdataptrs(pc),a0
		subq.b	#1,d7
plr_chkhold:	movea.l	(a0)+,a1		;track data
		tst.b	trk_noteoffcnt(a1)	;hold??
		bmi.s	plr_holdend		;no.
		move.b	(a2),d1			;get the 1st byte..
		bne.s	plr_hold1
		move.b	1(a2),d1
		and.b	#$f0,d1
		beq.s	plr_holdend		;don't hold
		bra.s	plr_hold2
plr_hold1:	and.b	#$3f,d1			;note??
		beq.s	plr_hold2		;no, cont hold..
		move.b	1(a2),d1
		and.b	#$0f,d1			;get cmd
		subq.b	#3,d1			;is there command 3 (slide)
		bne.s	plr_holdend		;no -> end holding
plr_hold2:	add.b	d3,trk_noteoffcnt(a1)	;continue holding...
plr_holdend:	addq.l	#3,a2		;next note
		dbf	d7,plr_chkhold
	ENDC
		btst	#5,msng_flags(a4)	;FLAG_STSLIDE??
		bne.w	plr_endfx		;yes, no effects this time...
		moveq	#0,d3			;counter = 0
nonewnote:
;	*********************** This code produces the effects **
		moveq	#0,d7	;clear track count
		moveq	#0,d6
		lea	trackdataptrs(pc),a2
plr_loop1:	movea.l	(a2)+,a5
		moveq	#0,d5
		moveq	#0,d4
		move.b	trk_cmd(a5),d6	;get the fx number
		move.b	trk_cmdqual(a5),d4	;and the last 2 #'s
	IFNE	MIDI
		tst.b	trk_prevmidin(a5)	;first: is it MIDI??
		bne.w	midicmds
	ENDC
		cmp.w	#4,d7
		bge.w	endl	;no non-MIDI effects in tracks 4 - 15
	IFNE	HOLD
		tst.b	trk_noteoffcnt(a5)
		bmi.s	plr_nowaitoff
		subq.b	#1,trk_noteoffcnt(a5)
		bpl.s	plr_nowaitoff
		IFNE	SYNTH
		tst.b	trk_synthtype(a5)	;synth/hybrid??
		beq.s	plr_nosyndec
		move.b	trk_decay(a5),trk_volcmd+1(a5)	;set volume command pointer
		clr.b	trk_volwait(a5)	;abort WAI
		move.l	trk_synthptr(a5),d0
		bra.s	plr_gosynth
		ENDC
plr_nosyndec:	move.b	trk_decay(a5),trk_fadespd(a5)	;set fade...
		bne.s	plr_nowaitoff	;if > 0, don't stop sound
		bset	d7,d5
		move.w	d5,$dff096	;shut DMA...
		moveq	#0,d5
	ENDC
plr_nowaitoff:
	IFNE	SYNTH
		move.l	trk_synthptr(a5),d0
		beq.s	plr_nosynth
plr_gosynth:	move.l	d0,a0
		bsr.w	synth_start
	ENDC
plr_nosynth:
	IFNE	HOLD
		move.b	trk_fadespd(a5),d0	;fade??
		beq.s	plr_nofade	;no.
		sub.b	d0,trk_prevvol(a5)
		bpl.s	plr_nofade2low
		clr.b	trk_prevvol(a5)
		clr.b	trk_fadespd(a5)		;fade no more
plr_nofade2low:
	ENDC
plr_nofade:	movea.l	trk_audioaddr(a5),a1
		add.b	d6,d6	;* 2
		move.w	fx_table(pc,d6.w),d0
		jmp	fxs(pc,d0.w)
fx_table:	dc.w	fx_00-fxs,fx_01-fxs,fx_02-fxs,fx_03-fxs,fx_04-fxs
		dc.w	fx_05-fxs,fx_xx-fxs,fx_xx-fxs,fx_xx-fxs,fx_xx-fxs
		dc.w	fx_0a-fxs,fx_xx-fxs,fx_xx-fxs,fx_0d-fxs,fx_xx-fxs
		dc.w	fx_0f-fxs
fxs:
;	**************************************** Effect 01 ******
fx_01:		sub.w	d4,trk_prevper(a5)	;slide up
		move.w	trk_prevper(a5),d5
		cmp.w	#113,d5		;too high?
		bge.s	fx_01_pushper
		move.w	#113,d5		;too high
		move.w	d5,trk_prevper(a5)
fx_01_pushper	move.w	d5,ac_per(a1)
		bra.w	fx_xx
;	**************************************** Effect 02 ******
fx_02:		add.w	d4,trk_prevper(a5)	;slide down
		move.w	trk_prevper(a5),d5
		bra.s	fx_01_pushper
;	**************************************** Effect 00 ******
fx_00:		tst.b	d4	;both fxqualifiers are 0s: no arpeggio
		beq.w	fx_xx
		move.l	d3,d0
		divu	#3,d0
		swap	d0
		tst.w	d0
		bne.s	fx_arp12
		and.b	#$0f,d4
		add.b	(a5),d4
		bra.s	fx_doarp
fx_arp12:	subq.b	#1,d0
		bne.s	fx_arp2
		lsr.b	#4,d4
		add.b	(a5),d4
		bra.s	fx_doarp
fx_arp2:	move.b	(a5),d4
fx_doarp:	subq.b	#1,d4		;-1 to make it 0 - 127
		add.b	msng_playtransp(a4),d4	;add play transpose
		add.b	trk_stransp(a5),d4	;add instrument transpose
		add.b	d4,d4		;shift to make index for UWORD
		movea.l	trk_periodtbl(a5),a0
		move.w	0(a0,d4.w),ac_per(a1)
		bra.w	fx_xx
;	**************************************** Effect 0D/0A ***
fx_0a:
fx_0d:		move.b	d4,d1
		move.b	trk_prevvol(a5),d0	;move previous vol to d0
		and.b	#$f0,d1
		bne.s	crescendo
		sub.b	d4,d0	;sub from prev. vol
		bpl.s	fx_0d_pushvol
		moveq	#0,d0	;volumes under zero not accepted
		bra.s	fx_0d_pushvol
crescendo:	lsr.b	#4,d1
		add.b	d1,d0
		cmp.b	#64,d0
		ble.s	fx_0d_pushvol
		moveq	#64,d0
fx_0d_pushvol	move.b	d0,trk_prevvol(a5)
		move.b	d0,ac_vol+1(a1)
		bra.w	endl
;	**************************************** Effect 05 ******
fx_05:		move.w	trk_prevper(a5),d5 ;this is very simple: get the old period
		cmp.b	#3,d3		;and..
		bge.w	fx_05b		;if counter < 3
		sub.w	d4,d5	;subtract effect qualifier
fx_05b		move.w	d5,ac_per(a1)
		bra.w	fx_xx
;	**************************************** Effect 03 ******
fx_03:		move.w	trk_porttrgper(a5),d0	;d0 = target period
		beq.w	fx_xx		;no target period specified
		move.w	trk_prevper(a5),d1	;d1 = curr. period
		move.b	trk_prevportspd(a5),d4	;get prev. speed
		cmp.w	d0,d1
		bhi.s	subper	;curr. period > target period
		add.w	d4,d1	;add the period
		cmp.w	d0,d1
		bge.s	targreached
		bra.s	targnreach
subper:		sub.w	d4,d1	;subtract
		cmp.w	d0,d1	;compare current period to target period
		bgt.s	targnreach
targreached:	move.w	trk_porttrgper(a5),d1 ;eventually push target period
		clr.w	trk_porttrgper(a5) ;now we can forget everything
targnreach:	move.w	d1,trk_prevper(a5)
		move.w	d1,ac_per(a1)
		bra.w	fx_xx
;	**************************************** Effect 04 ******
fx_04:		tst.b	d4
		beq.s	nonvib
		move.b	d4,trk_vibrspdsz(a5)
nonvib:		move.b	trk_vibroffs(a5),d0
		lsr.b	#2,d0
		and.w	#$1f,d0
		moveq	#0,d1
		lea	sinetable(pc),a0
		move.b	0(a0,d0.w),d5
		ext.w	d5
		move.b	trk_vibrspdsz(a5),d0
		and.w	#$000f,d0
		muls	d0,d5
		asr.w	#5,d5
		add.w	trk_prevper(a5),d5
		move.b	trk_vibrspdsz(a5),d0
		lsr.b	#3,d0
		and.b	#$3e,d0
		add.b	d0,trk_vibroffs(a5)
		move.w	d5,ac_per(a1)
		bra.s	fx_xx
;	**************************************** Effect 0F ******
fx_0f:		cmp.b	#$f1,d4
		bne.s	no0ff1
		cmp.b	#3,d3
		bne.w	fx_xx
		bra.s	playfxnote
no0ff1:		cmp.b	#$f2,d4
		bne.s	no0ff2
		cmp.b	#3,d3
		bne.w	fx_xx
		bra.s	playfxnote
no0ff2:		cmp.b	#$f3,d4
		bne.s	no0ff3
		move.b	d3,d0
		and.b	#2+4,d0		;is 2 or 4
		beq.s	fx_xx
playfxnote:	move.w	d7,d0		;track # to d0...
		moveq	#0,d1
		move.b	(a5),d1		;get note # of previous note
		beq.s	fx_xx
		moveq	#0,d2
		move.b	trk_prevvol(a5),d2	;get previous volume
		move.l	d3,-(sp)
		moveq	#0,d3
		move.b	trk_previnstr(a5),d3	;and prev. sample #
		movea.l	trk_previnstra(a5),a3
		bsr	_PlayNote
		move.l	(sp)+,d3
		bra.s	fx_xx
no0ff3:		cmp.b	#$f8,d4		;f8 = filter off
		beq.s	plr_filteroff
		cmp.b	#$f9,d4		;f9 = filter on
		bne.s	fx_xx
		bclr	#1,$bfe001
		bra.s	fx_xx
plr_filteroff:	bset	#1,$bfe001
;	*********************************************************
fx_xx:		move.b	trk_prevvol(a5),ac_vol+1(a1)
endl:		addq.b	#1,d7	;increment channel number
		cmp.w	numtracks(pc),d7	;all channels done???
		blt.w	plr_loop1	;not yet!!!
plr_endfx	bsr	_StartDMA	;turn on DMA
plr_exit	movem.l	(sp)+,d2-d7/a2-a5
	IFNE	VBLANK
		moveq	#0,d0
	ENDC
		rts

_SetTempo:
	IFNE	CIAB
		cmp.b	#10,d0	;If tempo <= 10, use SoundTracker tempo
		bhi.s	calctempo
		subq.b	#1,d0
		add.w	d0,d0
		move.w	sttempo+2(pc,d0.w),d1
		bra.s	pushtempo
calctempo:	move.l	timerdiv(pc),d1
		divu	d0,d1
pushtempo:	movea.l	craddr+4(pc),a0
		move.b	d1,(a0)		;and set the CIA timer
		lsr.w	#8,d1
		movea.l	craddr+8(pc),a0
		move.b	d1,(a0)
	ENDC
		rts ;   vv-- These values are the SoundTracker tempos (approx.)
sttempo:	dc.w	$0f00
	IFNE	CIAB
		dc.w	2417,4833,7250,9666,12083,14500,16916,19332,21436,24163
timerdiv	dc.l	470000
	ENDC

	IFNE	MIDI
midicmds
	IFNE	HOLD
		tst.b	trk_noteoffcnt(a5)
		bmi.s	midi_nowaitoff
		subq.b	#1,trk_noteoffcnt(a5)
		bpl.s	midi_nowaitoff
		move.l	a5,a1
		move.b	trk_prevmidin(a5),d1
		beq.s	midi_nowaitoff	;no note
		bsr.w	choff_midi
midi_nowaitoff:
	ENDC
		add.b	d6,d6	;* 2
		move.w	midicmd_table(pc,d6.w),d0
		jmp	midifx(pc,d0.w)
midicmd_table:	dc.w	mfx_00-midifx,mfx_01-midifx,mfx_02-midifx,mfx_03-midifx,mfx_04-midifx
		dc.w	mfx_05-midifx,endl-midifx,endl-midifx,endl-midifx,endl-midifx
		dc.w	mfx_0a-midifx,endl-midifx,endl-midifx,mfx_0d-midifx,mfx_0e-midifx
		dc.w	mfx_0f-midifx
midifx		
mfx_01		lea	prevmidipbend(pc),a0
		moveq	#0,d1
		move.b	trk_prevmidich(a5),d1	;get previous midi channel
		add.b	d1,d1		;UWORD index
		tst.b	d4		;x100??
		beq.s	resetpbend
		move.w	0(a0,d1.w),d0	;get previous pitch bend
		lsl.w	#3,d4		;multiply bend value by 8
		add.w	d4,d0
		cmp.w	#$3fff,d0
		bls.s	bendpitch
		move.w	#$3fff,d0
bendpitch:	move.w	d0,0(a0,d1.w)	;save current pitch bend
		lsr.b	#1,d1		;back to UBYTE
		or.b	#$e0,d1
		lea	noteondata(pc),a0
		move.b	d1,(a0)		;midi command & channel
		move.b	d0,1(a0)	;lower value
		and.b	#$7f,1(a0)	;clear bit 7
		lsr.w	#7,d0
		and.b	#$7f,d0		;clr bit 7
		move.b	d0,2(a0)	;higher 7 bits
		moveq	#3,d0
		bsr.w	_AddMIDIData
		bra.w	endl

mfx_02		lea	prevmidipbend(pc),a0
		moveq	#0,d1
		move.b	trk_prevmidich(a5),d1
		add.b	d1,d1
		tst.b	d4
		beq.s	resetpbend	;x200??
		move.w	0(a0,d1.w),d0
		lsl.w	#3,d4
		sub.w	d4,d0
		bpl.s	bendpitch	;not under 0
		moveq	#0,d0
		bra.s	bendpitch
resetpbend:	tst.b	d3		;d3 = counter (remember??)
		bne.w	endl
		move.w	#$2000,d0
		bra.s	bendpitch

mfx_03		tst.b	d3
		bne.w	endl
		lea	prevmidipbend(pc),a0
		moveq	#0,d1
		move.b	trk_prevmidich(a5),d1
		add.b	d1,d1
		move.b	d4,d0
		add.b	#128,d0
		lsl.w	#6,d0
		bra.s	bendpitch

mfx_0d		tst.b	d3
		bne.w	endl
		lea	noteondata+1(pc),a0	;CHANNEL AFTERTOUCH
		move.b	d4,(a0)	;value
		bmi.w	endl
		move.b	trk_prevmidich(a5),-(a0)
		or.b	#$d0,(a0)
		moveq	#2,d0
		bsr.w	_AddMIDIData
		bra.w	endl

mfx_0a		tst.b	d3
		bne.w	endl
		lea	noteondata+2(pc),a0	;POLYPHONIC AFTERTOUCH
		and.b	#$7f,d4
		move.b	d4,(a0)
		move.b	trk_prevmidin(a5),-(a0)
		beq.w	endl
		move.b	trk_prevmidich(a5),-(a0)
		or.b	#$A0,(a0)
		moveq	#3,d0
		bsr.w	_AddMIDIData
		bra.w	endl

mfx_04		moveq	#$01,d0
		bra.s	pushctrldata

mfx_0e		moveq	#$0a,d0
pushctrldata	tst.b	d3		;do it only once in a note
		bne.w	endl		;(when counter = 0)
		lea	noteondata+2(pc),a0 ;push "control change" data,
		move.b	d4,(a0)		;second databyte
		bmi.w	endl	;I said 0 - $7f!!! (for future compability)
		move.b	d0,-(a0)	;1st databyte
		move.b	trk_prevmidich(a5),-(a0)	;MIDI channel
		or.b	#$b0,(a0)	;command (B)
		moveq	#3,d0
		bsr.w	_AddMIDIData
		bra.w	endl

mfx_05		and.b	#$7f,d4		;set contr. value of curr. MIDI ch.
		move.b	trk_prevmidich(a5),d6
		lea	midicontrnum(pc,d6.w),a0
		move.b	d4,(a0)
		bra.w	endl

mfx_0f		cmp.b	#$fa,d4		;hold pedal ON
		bne.s	nomffa
		moveq	#$40,d0
		moveq	#$7f,d4
		bra.s	pushctrldata
nomffa		cmp.b	#$fb,d4		;hold pedal OFF
		bne.s	nomffb
		moveq	#$40,d0
		moveq	#$00,d4
		bra.s	pushctrldata
nomffb		bra.w	fx_0f

mfx_00		tst.b	d4
		beq.w	endl
		and.b	#$7f,d4
		move.b	trk_prevmidich(a5),d6
		move.b	midicontrnum(pc,d6.w),d0
		bra.s	pushctrldata

midicontrnum	ds.b	16

_ResetMIDI:	movem.l	d2/a2,-(sp)
		lea	prevmidicpres(pc),a0
		clr.l	(a0)+	;force presets to be set again
		clr.l	(a0)+	;(clear prev. preset numbers)
		clr.l	(a0)+
		clr.l	(a0)
		clr.b	lastcmdbyte
		lea	midiresd(pc),a2
		move.b	#$e0,(a2)	;reset pitchbenders & mod. wheel
		move.b	#$b0,3(a2)
		moveq	#15,d2
respbendl:	movea.l	a2,a0
		moveq	#6,d0
		bsr.w	_AddMIDIData
		addq.b	#1,(a2)
		addq.b	#1,3(a2)
		dbf	d2,respbendl
		lea	prevmidipbend(pc),a2
		moveq	#15,d2
resprevpbends:	move.w	#$2000,(a2)+
		dbf	d2,resprevpbends
		movem.l	(sp)+,d2/a2
		rts
midiresd:	dc.b	$e0,$00,$40,$b0,$01,$00
	ENDC

; *************************************************************************
; *************************************************************************
; ***********          P U B L I C   F U N C T I O N S          ***********
; *************************************************************************
; *************************************************************************

	IFEQ	EASY
		xdef	_InitModule,_PlayModule,_PlayModule2
		xdef	_InitPlayer,_RemPlayer,_StopPlayer
		xdef	_SetTempo,_ContModule
	ENDC
	IFNE	EASY

; This is the Reloc function, included only if EASY flag is set to 1.

reloci		move.l	24(a2),d0
		beq.s	xloci
		movea.l	d0,a0
		moveq   #0,d0
		move.b  787(a1),d0	;number of samples
		subq.b  #1,d0
relocs:		bsr.s   relocentr
		move.l	-4(a0),d3	;sample ptr
		beq.s	nosyn
		move.l	d3,a3
		tst.w	4(a3)
		bpl.s	nosyn		;type >= 0
		move.w	20(a3),d2	;number of waveforms
		lea	278(a3),a3	;ptr to wf ptrs
		subq.w	#1,d2
relsyn:		add.l	d3,(a3)+
		dbf	d2,relsyn
nosyn:		dbf     d0,relocs
xloci		rts
norel		addq.l	#4,a0
		rts
relocentr:
		tst.l   (a0)
		beq.s   norel
		add.l   d1,(a0)+
		rts
_RelocModule:
		movem.l	a2-a3/d2-d3,-(sp)
		movea.l a0,a2
		move.l  a2,d1		;d1 = ptr to start of module
		bsr.s	relocp
		movea.l 8(a2),a1
		bsr.s	reloci
rel_lp		bsr.s	relocb
		move.l	32(a2),d0	;extension struct
		beq.s	rel_ex
		move.l	d0,a0
		bsr.s	relocentr	;ptr to next module
		bsr.s	ritting).

On  the  left  there are gadgets for changing the screen colors.  No need to
explain??   All right, I won't.  But just remember that the colors are saved
when you save your song (they can give some "feel and expression").  You can
also set the default colors of MED by clicking Save prefs.

Jumping: Off 8th Smp
         When you don't want to see the "funny" mouse pointer, select Off.
         When you want him to jump every 8th note, select 8th.
         When you want him to jump when some sample is played, select
         Smp. The instruments which cause him to jump are selected from
         "Instr"-panel (gadget name: Jump).

         The name of the pointer is Topi (according to my cousin).

Clear All, Clear song only, Quit
         These are very dangerous functions, in fact they're so dangerous
         that you must click 'Confirm' after you've selected one of them:

         "Clear All" does just as it says. Clears the song, removes all
         instruments from memory. In two words: clears everything.
         Note: This clears ALL songs.

         "Clear song only" is not as destructive. While it destroys the
         song, it leaves the instruments (if you want to create a new
         song without reloading the instruments).
         Note: This clears only the current song.

         "Quit" is the most destructive. It does everything "Clear
         All" does, but even worse, it quits MED!!!!!!
         This function was made for program testing and it probably
         doesn't have any real use ;-)

Paths: Add/Rem
         These affect the sample names when loading songs/samples.

          Add        Adds the complete path of the sample when you
                     load e.g. if you load sample "PopSnare2" from
                     "ST-01:", the sample name will be "ST-01:PopSnare2".

          Rem        Usually songs made with Trackers have sample names
                     like "ST-02:AhhVox". If you want that the samples are
                     loaded using the path list instead of absolute names
                     (if your samples are in hard disk, for example) you
                     can remove "ST-xx:" from sample names and reload them,
                     but when you "Rem paths" is selected, any paths before
                     the sample names are removed quickly and easily during
                     loading.

         Both of these can be active at the same time. In that case the
         original path is removed, instrument is loaded using the path list,
         and the path name from the path list will be added.
         Note that the maximum length of a instrument name is 39 chars,
         so if the path name is very long, something may be lost.

H->B:
         This selects the name of the note between A# and C.
         In some countries, it's H. In some other countries it's B.

WB: Open/Close
         These gadgets allow you to easily close/reopen the Workbench
         screen to give you extra memory space.

Save prefs
         This will save the default settings of MED to S:med.config.
         When you start MED next time, these settings will be loaded.
         The following things will be saved:
                Current screen colors as default colors
                H->B
                State of E1, E2 and E3 switches
                Screen depth (Dep2)
                NO16
                Space = Del
                Keypad mode
                Automatic advance settings
                Current programmable key definitions
                Jump: Off
                PTKey

Dep2    This changes the screen depth from 8-colors to 4 colors. This
        frees some chip memory, but most important: it releases lots of
        DMA-time. In plain English: MED will run faster (but looks
        less pretty).

NO16    This gadget disables the 12/16 track display on OctaMED. On MED,
        it does nothing.

PTKEY   This switch changes the keyboard operation so, that the most common
        Protracker keyboard options will be recognized. This is helpful for
        those who have got used to Protracker keyboard layout. The following
        PT shortcuts are recognized:

        Right shift     Turns editing on, starts playing the block (= record)
        Right alt       Continue song
        Right Amiga     Play block
        L-Alt+curs.L/R  Previous/next block
        Shift+curs.L/R  Increase/decrease playseq position
        Space           Stops playing/toggles editing.
        TAB             Next track
        Shift-TAB       Previous track
        Shift-F3 - F5   Cut/Copy/Paste track
        Alt-F3 - F5     Cut/Copy/Paste block

        If there are any other Protracker options you'd like MED to recognize,
        please let me (Teijo) know.

+=========================================================+
|         Vol
+=========================================================+

This  panel  allows  you  to  set the proportional volume of each track.  In
addition  you can also set the master volume.  The proportional value can be
1  -  64.   If both the master volume and the volume of a track are 64, that
track  is  played  on  full volume.  If the master volume was 32 and a track
volume was 32, the volume of that track would be 1/4 of the full volume.

There  are  two  sets  of  «  < 64 > » gadgets.  The one on the right is the
master  volume.  The left one changes the volume of the selected track.  You
can select the track, by clicking a number above the gadgets.  The currently
selected number is highlighted.

This  is  quite  easy,  so no problems should appear.  The track volumes and
the master volume are saved with songs.

+=========================================================+
|           MIDI
+=========================================================+
MED  can output notes and some commands to external MIDI-devices (keyboards,
synthesizers...).   The  MIDI-panel  contains the MIDI-stuff which is needed
for that.

When you decide that some instrument should be a MIDI-instrument, you select
the  MIDI  channel  for  it.   "MIDI  ch"-gadgets are used for that.  If you
select e.g.  MIDI channel 2, the notes of the current instrument are sent in
MIDI  channel  2  (simple,  isn't  it?).  Remember to set the default volume
(Sample panel).

Also,  you  need to activate MIDI (click "MIDI active").  If the serial port
is  used by some other program, you can't use MIDI.  Then you must terminate
the other program and try again.

After  you have activated MIDI, MED starts sending "active sensing" messages
($FE).  Active sensing can be turned off with the "Act.sensing"-gadget.

By  default  MED uses the default preset of that channel (which you have set
on your MIDI device).  If you want to use other presets, you can select them
using  the "MIDI pres #"-gadgets, in that case the program change message is
transmitted  when  a note is played.  Preset number 0 means that the default
preset  is  used  (the preset is never changed for this instrument).  If you
want to send a preset change command, but don't want to play a note yet, use
the  command  C00  with any note.  In that case, the preset will be changed,
but no note will be transmitted.

You  can have instruments with differents preset numbers, but which will use
the  same  MIDI  channel.   In  that  case,  MED will automatically send the
program  change message whenever the instrument with different preset number
is used.

You can use MIDI-instruments on all 16 tracks (if there are 16 tracks on the
current block).  You can also freely mix samples and MIDI-instruments on the
same track (tracks 0 - 3 only).

One  helpful  keyboard  command  is  Ctrl-Space.  This command resets pitch-
benders  and  modulation  wheels on all channels.  Also, if you have changed
presets  on  your  MIDI-device,  MED  can't  detect this and thinks that the
preset  is  not  changed.   Ctrl-Space  causes  MED to resend program change
commands for all channels, when playing.  There's a gadget:  "Reset pb+pres"
that does the same thing.

The "MIDI reset"-gadget sends a MIDI reset command ($FF).

If  you  turn "Send sync" on, MED will send MIDI sync information, and allow
other  devices  to  synchronize with MED.  When "Ext sync" is on, MED can be
synced  externally.  You must also have input active when this is turned on.
The  synchronization  works  only  when you play song, when you're playing a
single block, the syncing is disabled.

L.Ctrl  on/off  gadgets  allow  you  to  easily  send  Local  control on/off
messages.

Suppr.N.Off  suppresses  Note  Off messages of the current instrument.  Some
devices  may  have  some  instruments  (e.g.   one-shot  drum  sounds) which
actually  ignore  the Note Off messages.  When Suppr.N.Off is selected, Note
Offs are not sent for the current instrument.  This reduces some unnecessary
MIDI output (slightly faster).

There  are  commands  which  control  the pitchbender, modulation wheel etc.
Detailed documetation later (again??).

------- Entering notes with MIDI
You  can  use  your  keyboard, synthesizer or whatever, to insert notes (you
don't need to use your Amiga's keyboard to entering).  You must activate the
"Input"-gadget.   If  editing  is  on and your MIDI-device is sending notes,
they  will be entered.  You can also enter Amiga-samples with MIDI.  Next to
the  Input-gadget, there's a integer gadget, that contains the number of the
input  MIDI  channel.   If  it's 0, then MED will accept input from all MIDI
channels.

"Read volume" gadget may be sometimes quite useful.  If you have a velocity-
sensitive  keyboard, the volume will be entered in the command-part when you
enter  notes  and  this gadget is selected.  "Read key-up" is not as useful.
When  it's  active,  MED records key-up events.  It can be used in real-time
recording (and mostly for long notes).

You  may  have noticed that you can't directly use all the octaves which are
available  on  the MIDI device.  "Instrument transpose" helps this.  You can
have two instruments with identical settings except for different instrument
transpose.  With instrument transpose you can reach nearly the full range of
notes.

NOTE: When you have a MIDI-instrument, its name doesn't really have any use.
      However it's good practice to type in the names of the presets. This
      way you can easily see the presets, and, if you give the song to
      somebody else who may have different MIDI-equipment, he/she can
      easily change the preset numbers to use the correct presets on his/her
      MIDI-device.

+=========================================================+
|        Trans
+=========================================================+

Starting  from  the  bottom,  there's  a long string gadget that can hold an
annotation  text  for  the  song.   When you save the song, the text in this
gadget will be saved, and it will be displayed when loading the song.

The  other  functions  of  this  panel  has something to do with transposing
notes.

When  you're doing some transposing, you should first decide which areas the
transposing  should  affect.   At  the top of the panel there are gadgets to
select this:
    Affect/song     the functions of this panel will affect the whole song
    Affect/block    the functions will affect the current block
    Affect/track    current track will be affected
    Affect/sel.trks selected tracks in this block will be affected
    Affect/range    the range selected with mouse will be affected

    Instr/all       all instruments affected
    Instr/curr      only the current instrument affected

First there are some simple transposing operations:
    1/2 ^ (<-arrow up) transposes the affection area up one half-step
    1/2 v (<-arrow down) transposes down one half-step
    oct ^ transposes one octave up
    oct v transposes one octave down

------- Note (ex)change
Suppose  you want to change all D-3's of instr 2 in your song to C-1.  First
you  set  Affect/song  (affect the whole song).  The you go to instrument #2
and select Instr/curr (to affect only this instrument).

Now  you  click the box at the right of the text "Note", hold down the mouse
button,  and  enter note D-3 from keyboard (D-3 will be displayed).  This is
the  source  note  name.   Then you click the box at the right of the source
note  box, hold the mouse button again, and enter C-1.  Now you have set the
destination note.

Finally  you just click the "->" gadget at the right of the dest.  note box.
If  you  had  wanted  to exchange the notes (D-3 => C-1 AND C-1 => D-3), you
would have clicked the "<->" gadget instead.

------- Instrument deletion/changing
The left instrument number is the source number and the right one is
the destination number. To set them, select the instrument (e.g. Shift-
cursor left/right) and click on the number.
Del        Deletes the notes with source instrument number.
->         Changes the source instrument number to dest. instrument number
<->        Exchanges the source and instrument numbers

------- Play transpose
This gadgets allows you transpose the whole song quickly.  It doesn't change
the notes, it only affects playing.

+=========================================================+
|       Range
+=========================================================+

This  panel  contains  many  of  the  range-editing options.  The range is a
rectangular  area of notes in a block.  It is selected by dragging using the
right  mouse  button and (then moving the mouse).  It only works on vertical
note display.

The  numbers  at the right bottom corner show you the starting/ending tracks
and starting/ending lines of the block.

At  the left side of the panel, there are gadgets "Select track" and "Select
block".   These allow you to select a single track or block quickly.  To the
right  of  them,  there  are  gadgets for range operations.  The simplest is
"Clear".   It  clears  all  notes in the range.  Above it is "Cut".  It also
clears  the  notes,  but  copies  them to the copy buffer for later pasting.
"Copy"  doesn't  clear  the notes, just copies them to the copy buffer.  You
can  paste  the  contents  of the copy buffer with "Paste".  The copy buffer
will  then  be  copied  starting at the cursor.  If the copy buffer contains
e.g.   3  tracks, and you don't want to copy them to consecutive tracks, but
e.g.   tracks  2,  7,  and  9,  You  would  select  tracks  2, 7, and 9 with
track-selection  gadgets  (at  the top), and then click "Paste select".  The
notes  will  be  pasted only to selected tracks.  The "Cancel" gadget can be
used to forget the range (e.g.  if you've accidentaly hit the RMB).  You can
achieve the same effect with moving to another block.

Spread  2/3/4  allow you to spread notes on one track to consecutive tracks.
For  example,  get  an  empty block, fill track 0 with notes, select track 0
(with "Select track" for example), and try these gadgets.

S1, S2          These gadgets can perform the quite tedious task of
                creating slides. Here's how it works (example):

                You want to make a slide from C-2 to G-2:

                    000 C-2 1000
                    001 --- 0000
                    002 --- 0000
                    003 --- 0000
                    004 --- 0000
                    005 G-2 1000
                    006 --- 0000

                Move the cursor to the line where you want the
                slide to begin (in this example, line 002). Then click
                S1 or S2. Result:

                If you click S1         If you click S2
                   000 C-2 1000            000 C-2 1000
                   001 --- 0000            001 --- 0000
                   002 G-2 1306            002 --- 0108
                   003 --- 0306            003 --- 0108
                   004 --- 0306            004 --- 0108
                   005 --- 0306            005 G-2 1000
                   006 --- 0000            006 --- 0000

                The better result is usually achieved using S1. S1 and S2
                use the different sliding commands (that's the difference).
                With S2, the second note is also replayed. The commands
                itself are documented later.
                        Keyboard: S1 = Amiga-T, S2 = Shift-Amiga-T

VOL             Creates a volume slide. With this option, it's possible
                to create easily fades etc. Example:

                009 --- 0C50 <-start volume   \    009 --- 0C50
                010 --- 0000  ========