;

; player.inc: 24ppq player tick,
;			  called from hi-pri irq
;
; scale		length of one step (48 ppqn)
; -----		------------------
; 0			1/16 (12 ticks)			shuffle 0...7
; 1			1/32 (6 ticks)			shuffle 0...6
; 2			1/8 triplet	(16 ticks)	shuffle 0...7
; 3			1/16 triplet (8 ticks)	shuffle 0...7
;
; author: Marko Kanala, raato ]a t[ mulletronic.com
;
; LICENSE
; Creative Commons Attribution-NonCommercial-ShareALike 2.5
;
; You are free:
;
; * to copy, distribute, display, and perform the work
; * to make derivative works
;
; Under the following conditions:
;	
; 1. Attribution. You must attribute the work in the manner specified by the 
;    author or licensor.
; 2. Noncommercial. You may not use this work for commercial purposes.
; 3. Share Alike. If you alter, transform, or build upon this work, you may 
;    distribute the resulting work only under a license identical to this one.
;
; * For any reuse or distribution, you must make clear to others the license 
;   terms of this work.
; * Any of these conditions can be waived if you get permission from the 
;   copyright holder.
;
; Your fair use and other rights are in no way affected by the above.
;
; This is a human-readable summary of the Legal Code.
; See full license at http://creativecommons.org/licenses/by-nc-sa/2.5/legalcode
;
; Copyright: Marko Kanala (raato@mulletronic.com).
;

player_tick:
	; are we running ?
	IFRUNNING	player_running
	return

; starts the player
player_start:
	IFNOTINMODE	MODE_RUN, player_start_stopped
	; we're running, send stop to sync to get
	;  synced machines to reset too
	call	player_stop
player_start_stopped:
	; reset current step(s)
	clrf	currentstep
	clrf	currentstep_bdrum
	clrf	currentstep_sdrum
	clrf	currentstep_ltom
	clrf	currentstep_mtom
	clrf	currentstep_htom
	clrf	currentstep_rshot
	clrf	currentstep_hclap
	clrf	currentstep_chihat
	clrf	currentstep_ohihat
	clrf	currentstep_crash
	clrf	currentstep_ride
	clrf	currentstep_accent
	clrf	bpmtick
	; set the running mode on
	bsf		mode, MODE_RUN
	; send midi start
	IFSETTINGONCALL SETTING_MIDIS_OUT, midi_out_start
	;call	midi_out_start
	return

player_continue:
	clrf	bpmtick
	; set the running mode on
	bsf		mode, MODE_RUN
	; send midi continue
	IFSETTINGONCALL SETTING_MIDIS_OUT, midi_out_continue
	;call	midi_out_continue
	return

; stops the player, make's sure the sync stop gets sent
player_stop:
	CLEARMODE	MODE_RUN
	clrf	triggers_a
	clrf	triggers_b
	call	tr_set_trigger_latches
	; send midi stop
	IFSETTINGONCALL SETTING_MIDIS_OUT, midi_out_stop
;	call	midi_out_stop
	; clear event bits to prevent stucking on syncs
	CLEAREVENT EVENT_DINRUN
	CLEAREVENT EVENT_MIDIRUN
	return

player_running:
	; resolve current scale:
	;  "bpmscaler" variable gets set
	;  with current tick count depending
	;  on the scale setting
	SCALETOW 2
	call	get_scaleticks
	movwf	bpmscaler

	incf	bpmtick	
	movlw	1
	cpfsgt	bpmtick
	call	player_trig		; play triggers on first tick

	decf	flamcounter
	bn		player_flamneg
	; flam positive or zero
	bnz		player_flamdone
	; flam zero, play flams
	call	player_flams
player_flamneg:
	; flamcounter was negative, flam must be 0
	clrf	flamcounter

player_flamdone:
	; note: currentstep values from 1 - 16
	btfsc	currentstep, 0	
	bra	currentstep_odd	
	; even step
	;  load shuffle to W
	movf	currentshuffle, W

	subwf	bpmscaler, W	; even steps take bpmscaler - shuffle ticks
	bnn		shufflescaleok
	clrf	WREG			; reset W if shuffle value too big
shufflescaleok:
	cpfslt	bpmtick			; should we reset bpmtick ?
	clrf	bpmtick
	return

currentstep_odd:
	; odd step, if this is the last step
	;  ignore shuffle
	movf	currentlength, W

	cpfslt	currentstep
	bra	player_laststep_odd
	; odd steps take bpmscaler + shuffle ticks

;	movf	bpmscaler, W		; min 6	
	incf	bpmscaler, W		; bpmscaler + 1, min of 7

	; if bpmscaler is less than currentshuffle
	;  we need to correct the step duration by -1
	cpfslt	currentshuffle		; 0 ... 7
	decf	WREG

	decf	WREG
	addwf	currentshuffle, W	; bpmscaler + shuffle

	cpfslt	bpmtick
	clrf	bpmtick
	return

; currentstep is the last step
;  and the step is odd, ignore shuffle
player_laststep_odd:
	movf	bpmscaler, W
	cpfslt	bpmtick
	clrf	bpmtick
	return

;
; triggers the current step
;
; note: we can do some processing before
;  triggering the trigger circuit as the
;  lcd bus needs to settle down anyway
;
player_trig:
	; we should increment the step counters
	incf	currentstep
	incf	currentstep_bdrum
	incf	currentstep_sdrum
	incf	currentstep_ltom
	incf	currentstep_mtom
	incf	currentstep_htom
	incf	currentstep_rshot
	incf	currentstep_hclap
	incf	currentstep_chihat
	incf	currentstep_ohihat
	incf	currentstep_crash
	incf	currentstep_ride
	incf	currentstep_accent		; ... ~1.3us

	movf	currentlength, W

	movwf	player_temp				; store for INSTRCHECKPOS
	cpfseq	currentstep
	bra		player_not_last_step		
	; last step => mark as an event
	SETEVENT	EVENT_LASTSTEP
player_not_last_step:
	cpfsgt	currentstep
	bra	player_no_pat_reset
	; currentstep > pat_length, reset to 1
	movlw	1
	movwf	currentstep		; currenstep == 1

	movlw	(1<<MODE_TPLAY) | (1<<MODE_TWRITE)
	andwf	mode, W
	bz		player_not_trackmode

	movf	currentmeasuredata, W
	DECODE_REPEAT_FROM_MEASURE	; W = repeat
	cpfslt	repeatcounter
	bra		player_advance_trackmode

	; repeatcounter < current measure repeat
	;  just increment repeat counter and continue
	incf	repeatcounter
	bra		player_not_trackmode

player_advance_trackmode:

	; roll track mode counters
	movff	nextmeasuredata, currentmeasuredata
	movff	nextmeasure, currentmeasure
	movff	nextmeasure + 1, currentmeasure + 1
	clrf	repeatcounter

player_not_trackmode:
	; if we are in pattern write mode do not change the
	;  current pattern
	IFMODE	MODE_PWRITE, player_reset_done

	; roll to the next pattern if needed,
	; if next differs from current we are waiting for a flash event
	;  otherwise the event will never come
	IFNOEVENT	EVENT_FLASHREADOK, player_no_flash_done
	; flash read done, roll over
	CLEAREVENT	EVENT_FLASHREADOK
	movff	nextbank, currentbank
	movff	nextpattern, currentpattern
	LENGTHTOW	2
	movwf	currentlength
	SHUFFLETOW	2
	movwf	currentshuffle
	bra		player_dochain
player_no_flash_done:
	; no flash event, we should do the chain check anyway
	;  if next == current
	movf	currentbank, W
	cpfseq	nextbank
	bra		player_reset_done
	movf	currentpattern, W
	cpfseq	nextpattern
	bra		player_reset_done
player_dochain:
	IFNOTINMODE	MODE_CHAIN, player_reset_done
	; if in chain mode get next pattern from chain
	lfsr	FSR2, chainbuffer
	movf	chainposition, W
	addwf	FSR2L
	movf	INDF2, W
	bnn		player_chain_ok
	; chain ended
	clrf	chainposition
	; try again from the start
	movf	chainbuffer, W
	bn		player_no_flash_done	; no chain, just skip
player_chain_ok:
	; pattern was found in chain, decode pattern/bank
	movwf	nextpattern
	movwf	nextbank
	movlw	b'00001111'
	andwf	nextpattern				; b'0000pppp'
	swapf	nextbank				; b'pppp00bb'
	andwf	nextbank				; b'000000bb'
	incf	chainposition			; advance to next in chain
player_reset_done:
	FORCEDISPLAYREFRESH				; display should be refreshed
	IFMODE	MODE_PWRITE, player_no_pat_reset
	CLEARMODE	MODE_FILL	; clear fill flag if in play mode
player_no_pat_reset:

	; check and roll over the counters if needed
	LOADPATTOFSR	2, PAT_LENGTHS	; ... ~3us

	INSTRCHECKPOS	currentstep_bdrum, currentstep_sdrum	; ~1.6us / check
	INSTRCHECKPOS	currentstep_ltom, currentstep_mtom
	INSTRCHECKPOS	currentstep_htom, currentstep_rshot
	INSTRCHECKPOS	currentstep_hclap, currentstep_chihat
	INSTRCHECKPOS	currentstep_ohihat, currentstep_crash
	INSTRCHECKPOS	currentstep_ride, currentstep_accent	; ... 12.6us

	; blink blink_tempo with step advance
	btg		blink_tempo, 0
;
; step counters always 1 - 16 for triggering routines
;
	; (this allows lcd to settle if
	;  triggers collide with lcd enable strobe)
	;
	; NOT NEEDED HERE
	; ~13us delay already spent on length checks
;	call	player_lcd_delay

	bcf		PORT_LCD_EN, LCD_EN_PIN
	; we should protect the lcd bus before
	;  messing up with the triggers
	; RB0 - RB4 should be saved as they
	;  overlap with the trigger bus
	movlw	b'00011111'
	andwf	PORT_LCD, W
	movwf	player_temp

	; see 9090.inc, triggers the p9090 circuit
	call	tr_do_triggers

	; restore LCD bus
	movlw	b'11100000'
	andwf	PORT_LCD, W
	iorwf	player_temp, W
	movwf	PORT_LCD
	
	; trigger midi outs set so
	IFSETTINGONCALL	SETTING_MIDIN_OUT, midi_do_triggers

	; set flamcounter on trigger
	FLAMTOW	2
	addwf	WREG			; flam * 2
	movwf	flamcounter
	return

;
; checks if any flams should be played 
;  and triggers them accordingly
;
player_flams:
	; protects the shared lcd bus
	call	player_lcd_delay
	bcf		PORT_LCD_EN, LCD_EN_PIN
	movlw	b'00011111'
	andwf	PORT_LCD, W
	movwf	player_temp

	call	tr_do_flams

	; restore LCD bus
	movlw	b'11100000'
	andwf	PORT_LCD, W
	iorwf	player_temp, W
	movwf	PORT_LCD
	return

;
; sets the triggers off, timed with timer 3
;  for ~2ms trigger pulses
;
player_trig_off:
	; protects the shared lcd bus
	call	player_lcd_delay
	bcf		PORT_LCD_EN, LCD_EN_PIN
	movlw	b'00011111'
	andwf	PORT_LCD, W
	movwf	player_temp
	; see 9090.inc, sets the triggers off in p9090 circuit
	call	tr_clear_triggers
	; restore LCD bus
	movlw	b'11100000'
	andwf	PORT_LCD, W
	iorwf	player_temp, W
	movwf	PORT_LCD
	return

;
; 13us delay to avoid the lcd bus collision
;
player_lcd_delay:
	movlw	32
	movwf	player_temp
player_lcd_delay_loop:
	clrwdt
	decfsz	player_temp
	bra		player_lcd_delay_loop	
	return

External Labels :

player_stop
tr_set_trigger_latches
get_scaleticks
player_trig
player_flams
tr_do_triggers
player_lcd_delay
tr_do_flams
tr_clear_triggers