//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2013 XYLIO INFO SRL. All Rights Reserved. --------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Numark MixTrack / MixTrack Pro' (v2)
//----------------------------------------------------------------------------- 
function getLanguageVersion()
{
	return 1;// do not modify this, this should be 1 for all scripts
}
//----------------------------------------------------------------------------- 
function getFileVersion()
{
	return 2;// update this as needed
}
//----------------------------------------------------------------------------- 
// return a user friendly name for the controller supported
function getControllerUserFriendlyName()
{
	return "Numark MixTrack / MixTrack Pro";
}
//----------------------------------------------------------------------------- 
// return the communication type that the controller supports 
// this can be either 'midi' or 'hid' (please return only one of them in case the controller supports both)
function getControllerType()
{
	return 'midi';
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// preferred way to identify the controller (using the USB device VID and PID -- vendor and product id)
// return false if the controller does not support this kind of identification
// osId -- 0 Windows; 1 MacOSX; 2 Linux; -1 unknown
function getSupportedHidDeviceVID(osId)
{
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedHidDevicePID(osId)
{
	return false;
}
//----------------------------------------------------------------------------- 
// this is NOT the preferred way to identify the controller but you should use
// it if the controller does not support standard USB VID and PID identification
// return false if the controller does not support this kind of identification
// osId -- 0 Windows; 1 MacOSX; 2 Linux; -1 unknown
function getSupportedHidDeviceName(osId)
{
	return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// preferred way to identify the controller (using the USB device VID and PID -- vendor and product id)
// return false if the controller does not support this kind of identification
// osId -- 0 Windows; 1 MacOSX; 2 Linux; -1 unknown
function getSupportedMidiDeviceVID(osId)
{
	return 0x15E4;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDevicePID(osId)
{
	return 0x0077;// Mixtrack Pro
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceVID2(osId)
{
	return 0x15E4;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDevicePID2(osId)
{
	return 0x0024;// Mixtrack
}
//----------------------------------------------------------------------------- 
// preferred way to identify the controller (if it supports MIDI SysEx)
// return false if the controller does not support this kind of identification
function getSupportedMidiSysExIdString(osId)
{
	// sysex response to 'F0 7E 7F 06 01 F7' (identification request sysex)
	// please note that the response contains 5 bytes in hex format containing
	// the Manufacturer ID (3 bytes) and Model Number (2 bytes)
	// ATTN: the returned value should omit the standard 'F0 7E 00 06 02' message start
	//if(osId==1) return false;
	return "00013F7700";// Mixtrack Pro
}
//----------------------------------------------------------------------------- 
function getSupportedMidiSysExIdString2(osId)
{
	//if(osId==1) return false;
	return "00013F2400";// Mixtrack
}
//----------------------------------------------------------------------------- 
// this is NOT the preferred way to identify the controller but you should use
// it if the controller does not support standard MIDI SysEx identification
// return false if the controller does not support this kind of identification
// osId -- 0 Windows; 1 MacOSX; 2 Linux; -1 unknown
function getSupportedMidiDeviceNameForInput(osId)
{
	//if(osId==1) return "MixTrack Pro";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==1) return "MixTrack Pro";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForInput2(osId)
{
	//if(osId==1) return "MixTrack";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput2(osId)
{
	//if(osId==1) return "MixTrack";
	return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var jogWheelsInScratchMode=[0,0,0,0];
var loopingInBlinkLed=[-1,-1,-1,-1];// -1 means disabled
var bReloopLastAction=[-1,-1,-1,-1];// used to properly simulate button press and release
// effects
var effectsTotal=3;// 3 effects
var effectsSelected=[0,0,0,0];// the selected fx/deck, 0-based index
var effectsName=['fxFlanger','fxEcho','fxBeatwaw'];// , 'fxPan'
var effectsAction=['deckFxFlanger','deckFxEcho','deckFxBeatwaw'];// , 'deckFxPan'
var effectsGetDW=['fxFlangerDryWet','fxEchoDryWet','fxBeatwawDryWet'];// , 'fxPanDryWet'
var effectsGetP1=['fxFlangerParam1','fxEchoParam1','fxBeatwawParam1'];// , 'fxPanParam1'
var effectsGetP2=['fxFlangerParam2','fxEchoParam2','fxBeatwawParam2'];// , 'fxPanParam2'
var effectsSetDW=['deckFxFlangerDW','deckFxEchoDW','deckFxBeatwawDW'];// , 'deckFxPanDW'
var effectsSetP1=['deckFxFlangerP1','deckFxEchoP1','deckFxBeatwawP1'];// , 'deckFxPanP1'
var effectsSetP2=['deckFxFlangerP2','deckFxEchoP2','deckFxBeatwawP2'];// , 'deckFxPanP2'
//var effectsStatus=[[0,0,0],[0,0,0],[0,0,0],[0,0,0]];// 1 means enabled, 0 disabled (4 decks x 3 fx)
//----------------------------------------------------------------------------- 
// this is called when the controller is initialized and the control is passed to this script
// use this to initialize any variables you might need, the controller' leds, etc
// uniqueId is a unique integer between 0..15 that is guaranteed not to change for 
// a certain opened controller -- please note that there can be many controllers
// of the same type in use at the same time ! Use uniqueId to identify between them
function controllerInit(uniqueId)
{
	debug('controllerInit MixTrack / MixTrack Pro ['+uniqueId+']');// this outputs a string to DebugView app on Win
	/*
	for(var i=0; i<0x7F; i++)
	//for(var i=0x6F; i<0x72; i++)
	{
		sendMidi(uniqueId,0x90,0,i,0x7F);// on
		if(i % 10 == 0) call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
	}
	*/
	// global
	var lbMode = getValue('lbMode', -1);
	sendMidi(uniqueId,0x90,0,0x73,(lbMode==1 ? 0x7F : 0x00));// folder
	sendMidi(uniqueId,0x90,0,0x72,(lbMode==0 ? 0x7F : 0x00));// file
	
	// deck A
	sendMidi(uniqueId,0x90,0,0x40,0x00);// sync
	sendMidi(uniqueId,0x90,0,0x33,(getValue('cue', 0) ? 0x7F : 0x00));// cue
	sendMidi(uniqueId,0x90,0,0x3B,(getValue('play', 0) ? 0x7F : 0x00));// play-pause
	sendMidi(uniqueId,0x90,0,0x4A,0x00);// cue-play
	sendMidi(uniqueId,0x90,0,0x48,0x00);// scratch
	sendMidi(uniqueId,0x90,0,0x51,(getValue('keyLock', 0) ? 0x7F : 0x00));// keylock
	sendMidi(uniqueId,0x90,0,0x63,(getValue(effectsName[effectsSelected[0]], 0) ? 0x7F : 0x00));// effect
	call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
	sendMidi(uniqueId,0x90,0,0x61, 0x00);// loop more (manual)
	sendMidi(uniqueId,0x90,0,0x53, 0x00);// loop in
	sendMidi(uniqueId,0x90,0,0x54, (getValue('looping', 0) ? 0x7F : 0x00));// loop out 
	sendMidi(uniqueId,0x90,0,0x55, (getValue('looping', 0) ? 0x7F : 0x00));// reloop
	sendMidi(uniqueId,0x90,0,0x59,(getValue('reverse', 0) ? 0x7F : 0x00));// reverse (view)
	sendMidi(uniqueId,0x90,0,0x5A,(getValue('killLow', 0) ? 0x7F : 0x00));// low
	sendMidi(uniqueId,0x90,0,0x5B,(getValue('killMid', 0) ? 0x7F : 0x00));// mid
	sendMidi(uniqueId,0x90,0,0x5C,(getValue('killHigh', 0) ? 0x7F : 0x00));// treble
	sendMidi(uniqueId,0x90,0,0x65,(getValue('monitor', 0) ? 0x7F : 0x00));// monitor
	sendMidi(uniqueId,0x90,0,0x70,(getValue('pitchIs0', 0) ? 0x7F : 0x00));// pitch 0 A
	call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
	
	// deck B
	sendMidi(uniqueId,0x90,0,0x47,0x00);// sync
	sendMidi(uniqueId,0x90,0,0x3C,(getValue('cue', 1) ? 0x7F : 0x00));// cue
	sendMidi(uniqueId,0x90,0,0x42,(getValue('play', 1) ? 0x7F : 0x00));// play-pause
	sendMidi(uniqueId,0x90,0,0x4C,0x00);// cue-play
	sendMidi(uniqueId,0x90,0,0x50,0x00);// scratch
	sendMidi(uniqueId,0x90,0,0x52,(getValue('keyLock', 1) ? 0x7F : 0x00));// keylock
	sendMidi(uniqueId,0x90,0,0x64,(getValue(effectsName[effectsSelected[1]], 1) ? 0x7F : 0x00));// effect
	call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
	sendMidi(uniqueId,0x90,0,0x62, 0x00);// loop more (manual)
	sendMidi(uniqueId,0x90,0,0x56, 0x00);// loop in
	sendMidi(uniqueId,0x90,0,0x57, (getValue('looping', 0) ? 0x7F : 0x00));// loop out 
	sendMidi(uniqueId,0x90,0,0x58, (getValue('looping', 0) ? 0x7F : 0x00));// reloop	
	sendMidi(uniqueId,0x90,0,0x5D,(getValue('reverse', 1) ? 0x7F : 0x00));// reverse (view)
	sendMidi(uniqueId,0x90,0,0x5E,(getValue('killLow', 1) ? 0x7F : 0x00));// low
	sendMidi(uniqueId,0x90,0,0x5F,(getValue('killMid', 1) ? 0x7F : 0x00));// mid
	sendMidi(uniqueId,0x90,0,0x60,(getValue('killHigh', 1) ? 0x7F : 0x00));// treble
	sendMidi(uniqueId,0x90,0,0x66,(getValue('monitor', 1) ? 0x7F : 0x00));// monitor
	sendMidi(uniqueId,0x90,0,0x71,(getValue('pitchIs0', 1) ? 0x7F : 0x00));// pitch 0 B	
}
//----------------------------------------------------------------------------- 
// called just before the controller will be deinitialized and the control will go away from the script
// use to power off any powered leds, etc
function controllerDeinit(uniqueId)
{
	debug('controllerDeinit MixTrack / MixTrack Pro ['+uniqueId+']');
	//for(var i=0; i<0x7F; i++)
	for(var i=0x33; i<0x74; i++)
	{
		sendMidi(uniqueId,0x90,0,i,0x00);// off
		if(i % 10 == 0) call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
	}
}
//----------------------------------------------------------------------------- 
// this is your main target function to implement the controller -- this is called 
// for each and every MIDI message that comes from the controller itself
// return true if the message was processed by the script and false to let it 
// pass thru the software (eg. so it can be assigned by the user using the LEARN function)
function messageReceived(uniqueId,status,channel,data1,data2)
{
	var messageNotProcessed=false;
	switch(status)
	{
	//-----------------------------------------------
	// controllers: sliders, knobs, jog-wheels, encoders ('moving' things)
	case 0xB0:
		switch(data1)
		{
		// global
		case 0x0A: call('setCrossfader', 0x7F-data2); break;
		case 0x17: call('setMasterGain', data2/2); break;		
		case 0x0B: call('setMonitorGain', data2); break;
		case 0x0C: call('setCuePgmMix', data2); break;
		case 0x1A: // move track
			var val1 = (data2>0x40 ? data2-0x80 : data2);// ATTN: on the MixTrack (not Pro) this might be reversed
			call('lbMoveCursor', val1);
			break;

		// deck A
		case 0x08: call('setDeckVolume', 0, data2); break;
		case 0x0D: call('setDeckPitch', 0, data2); break;
		case 0x14: call('setDeckEqLow', 0, data2); break;
		case 0x12: call('setDeckEqMid', 0, data2); break;
		case 0x10: call('setDeckEqHigh', 0, data2); break;
		case 0x19: // params: (deck, increment, total positions per complete revolution) 
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			if(jogWheelsInScratchMode[0]) call('scratchDeckJW', 0, val1, 600);else call('moveDeckJogWheel', 0, val1, 600);
			break;
		case 0x1B: // FX select (move between fx)
			var deck = 0;
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			// move to the next effect
			effectsSelected[deck] += val1;
			if(effectsSelected[deck]<0) effectsSelected[deck] += effectsTotal;
			effectsSelected[deck] = effectsSelected[deck] % effectsTotal;
			// re-set the led
			sendMidi(uniqueId,0x90,0,(deck==0)?0x63:0x64,(getValue(effectsName[effectsSelected[deck]], deck) ? 0x7F : 0x00));// effect
			break;
		case 0x1C: // FX control 1 (dry-wet)
			var deck = 0;
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			// adjust value
			var oldVal = getValue(effectsGetDW[effectsSelected[deck]], deck);// 0..1000
			var newVal = Math.min(Math.max(0, oldVal + val1 * 25), 1000);// 0..1000
			//debug('dry-wet old='+oldVal+' new='+newVal+' val1='+val1+' deck='+deck+' current='+effectsSelected[deck]);
			call(effectsSetDW[effectsSelected[deck]], deck, 127*newVal/1000);// 0..127
			break;
		case 0x1D: // FX control 2 (param 1)
			var deck = 0;
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			// adjust value
			var oldVal = getValue(effectsGetP1[effectsSelected[deck]], deck);// 0..1000
			var newVal = Math.min(Math.max(0, oldVal + val1 * 25), 1000);// 0..1000
			//debug('param 1 old='+oldVal+' new='+newVal+' val1='+val1+' deck='+deck+' current='+effectsSelected[deck]);
			call(effectsSetP1[effectsSelected[deck]], deck, 127*newVal/1000);// 0..127
			break;
				
		// deck B
		case 0x09: call('setDeckVolume', 1, data2); break;
		case 0x0E: call('setDeckPitch', 1, data2); break;
		case 0x15: call('setDeckEqLow', 1, data2); break;
		case 0x13: call('setDeckEqMid', 1, data2); break;
		case 0x11: call('setDeckEqHigh', 1, data2); break;
		case 0x18: // params: (deck, increment, total positions per complete revolution) 
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			if(jogWheelsInScratchMode[1]) call('scratchDeckJW', 1, val1, 600);else call('moveDeckJogWheel', 1, val1, 600);
			break;		
		case 0x1E: // FX select (move between fx)
			var deck = 1;
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			// move to the next effect
			effectsSelected[deck] += val1;
			if(effectsSelected[deck]<0) effectsSelected[deck] += effectsTotal;
			effectsSelected[deck] = effectsSelected[deck] % effectsTotal;
			// re-set the led
			sendMidi(uniqueId,0x90,0,(deck==0)?0x63:0x64,(getValue(effectsName[effectsSelected[deck]], deck) ? 0x7F : 0x00));// effect
			break;
		case 0x1F: // FX control 1 (dry-wet)
			var deck = 1;
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			// adjust value
			var oldVal = getValue(effectsGetDW[effectsSelected[deck]], deck);// 0..1000
			var newVal = Math.min(Math.max(0, oldVal + val1 * 25), 1000);// 0..1000
			//debug('dry-wet old='+oldVal+' new='+newVal+' val1='+val1+' deck='+deck+' current='+effectsSelected[deck]);
			call(effectsSetDW[effectsSelected[deck]], deck, 127*newVal/1000);// 0..127
			break;
		case 0x20: // FX control 2 (param 1)
			var deck = 1;
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			// adjust value
			var oldVal = getValue(effectsGetP1[effectsSelected[deck]], deck);// 0..1000
			var newVal = Math.min(Math.max(0, oldVal + val1 * 25), 1000);// 0..1000
			//debug('param 1 old='+oldVal+' new='+newVal+' val1='+val1+' deck='+deck+' current='+effectsSelected[deck]);
			call(effectsSetP1[effectsSelected[deck]], deck, 127*newVal/1000);// 0..127
			break;
		
		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		switch(data1)
		{
		// global
		case 0x69:
		case 0x4F: if(data2>0x3F) call('lbToggleSelect', data2); break;
		case 0x4B: if(data2>0x3F) call('deckLBLoad', 0, data2); break;
		case 0x34: if(data2>0x3F) call('deckLBLoad', 1, data2); break;

		// deck A
		case 0x40: call('deckSync', 0, data2); break;
		case 0x33: call('deckCue', 0, data2); break;
		case 0x3B: call('deckPlayPause', 0, data2); break;
		case 0x4A: call('deckCuePlay', 0, data2); break;
		case 0x65: call('deckMonitor', 0, data2); break;
		case 0x43: call('deckBendDown', 0, data2); break;
		case 0x44: call('deckBendUp', 0, data2); break;		
		case 0x51: call('deckKeyLock', 0, data2); break;	
		case 0x59: call('deckReverse', 0, data2); break;	
		case 0x48: if(data2>0x3F){ jogWheelsInScratchMode[0] = (jogWheelsInScratchMode[0]+1)%2; sendMidi(uniqueId,0x90,0,0x48,(jogWheelsInScratchMode[0] ? 0x7F : 0x00));} break;
		case 0x5A: call('deckEqLowKill', 0, data2); break;
		case 0x5B: call('deckEqMidKill', 0, data2); break;
		case 0x5C: call('deckEqHighKill', 0, data2); break;		
		case 0x63: call(effectsAction[effectsSelected[0]], 0, data2); break;// toggle current effect
		// loop
		case 0x61: call('deckLoopMore', 0, data2); break;
		case 0x53: call('deckLoopInEnd', 0, data2); break;
		case 0x54: call('deckLoopExit', 0, data2); break;
		//case 0x55: call('deckLoopReloop', 0, data2); break;		
		case 0x55:
			var deck = 0;
			var looping = getValue('looping', deck);
			if(bReloopLastAction[deck]==-1) // if no action was already started
			{
				if(looping)
				{
					bReloopLastAction[deck] = 1;
					call('deckLoopExit', deck, data2);
				}else
				{
					bReloopLastAction[deck] = 0;
					call('deckLoopReloop', deck, data2); 
				}
			}else
			{
				if(bReloopLastAction[deck])
					call('deckLoopExit', deck, data2);
				else
					call('deckLoopReloop', deck, data2);
				// reset
				bReloopLastAction[deck] = -1;
			}
			break;		
		
		// deck B
		case 0x47: call('deckSync', 1, data2); break;
		case 0x3C: call('deckCue', 1, data2); break;
		case 0x42: call('deckPlayPause', 1, data2); break;
		case 0x4C: call('deckCuePlay', 1, data2); break;
		case 0x66: call('deckMonitor', 1, data2); break;
		case 0x45: call('deckBendDown', 1, data2); break;
		case 0x46: call('deckBendUp', 1, data2); break;		
		case 0x52: call('deckKeyLock', 1, data2); break;	
		case 0x5D: call('deckReverse', 1, data2); break;	
		case 0x50: if(data2>0x3F){ jogWheelsInScratchMode[1] = (jogWheelsInScratchMode[1]+1)%2; sendMidi(uniqueId,0x90,0,0x50,(jogWheelsInScratchMode[1] ? 0x7F : 0x00));} break;
		case 0x5E: call('deckEqLowKill', 1, data2); break;
		case 0x5F: call('deckEqMidKill', 1, data2); break;
		case 0x60: call('deckEqHighKill', 1, data2); break;
		case 0x64: call(effectsAction[effectsSelected[1]], 1, data2); break;// toggle current effect
		// loop
		case 0x62: call('deckLoopMore', 1, data2); break;
		case 0x56: call('deckLoopInEnd', 1, data2); break;
		case 0x57: call('deckLoopExit', 1, data2); break;
		//case 0x58: call('deckLoopReloop', 1, data2); break;		
		case 0x58:
			var deck = 1;
			var looping = getValue('looping', deck);
			if(bReloopLastAction[deck]==-1) // if no action was already started
			{
				if(looping)
				{
					bReloopLastAction[deck] = 1;
					call('deckLoopExit', deck, data2);
				}else
				{
					bReloopLastAction[deck] = 0;
					call('deckLoopReloop', deck, data2); 
				}
			}else
			{
				if(bReloopLastAction[deck])
					call('deckLoopExit', deck, data2);
				else
					call('deckLoopReloop', deck, data2);
				// reset
				bReloopLastAction[deck] = -1;
			}
			break;		
		
		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	default: messageNotProcessed = true;
	}
	if(messageNotProcessed) debug('messageReceived ['+uniqueId+'] - status:'+status+' channel:'+channel+' data1:'+data1+' data2:'+data2);
	return !messageNotProcessed;
}
//----------------------------------------------------------------------------- 
// this is called by the software whenever something important changes (eg. play status, song changed, etc)
// depending on the notification type it may refer to a global event (deck is -1) or a specific deck (deck >= 0)
// and can contain any/all of the parameters: param 1 is an integer, param 2 is a float, param 3 is a string
function notificationReceived(uniqueId,name,deck,param1,param2,param3)
{
	if(deck>=2) return;
	//debug('notif ['+uniqueId+'] name='+name + ' deck='+deck + ' p1(int)='+param1 + ' p2(float)='+param2 + ' p3(str)='+param3);
	if(name=='cue') 		sendMidi(uniqueId,0x90,0,(deck==0)?0x33:0x3C,(param1 ? 0x7F : 0x00));
	if(name=='play') 		sendMidi(uniqueId,0x90,0,(deck==0)?0x3B:0x42,(param1 ? 0x7F : 0x00));
	if(name=='onbeat') 		sendMidi(uniqueId,0x90,0,(deck==0)?0x40:0x47,(param1 ? 0x7F : 0x00));
	if(name=='monitor') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x65:0x66,(param1 ? 0x7F : 0x00));
	if(name=='reverse') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x59:0x5D,(param1 ? 0x7F : 0x00));
	if(name=='keyLock')		sendMidi(uniqueId,0x90,0,(deck==0)?0x51:0x52,(param1 ? 0x7F : 0x00));
	if(name=='pitchIs0')	sendMidi(uniqueId,0x90,0,(deck==0)?0x70:0x71,(param1 ? 0x7F : 0x00));	
	if(name=='killLow')		sendMidi(uniqueId,0x90,0,(deck==0)?0x5A:0x5E,(param1 ? 0x7F : 0x00));
	if(name=='killMid')		sendMidi(uniqueId,0x90,0,(deck==0)?0x5B:0x5F,(param1 ? 0x7F : 0x00));
	if(name=='killHigh')	sendMidi(uniqueId,0x90,0,(deck==0)?0x5C:0x60,(param1 ? 0x7F : 0x00));
	
	if(name=='loopIn')
	{
		sendMidi(uniqueId,0x90,0,(deck==0)?0x53:0x56,(param1 ? 0x7F : 0x00));// Loop In (on or off)
		// activate blinking for the LoopIn led (so the user knows to press it again)
		if(param1) loopingInBlinkLed[deck] = 0;else loopingInBlinkLed[deck] = -1;
	}
	if(name=='looping')
	{
		sendMidi(uniqueId,0x90,0,(deck==0)?0x53:0x56,0x00);// Loop In (off)		
		sendMidi(uniqueId,0x90,0,(deck==0)?0x54:0x57,(param1 ? 0x7F : 0x00));// Loop Out (on or off)
		sendMidi(uniqueId,0x90,0,(deck==0)?0x55:0x58,(param1 ? 0x7F : 0x00));// Reloop (on or off)
		sendMidi(uniqueId,0x90,0,(deck==0)?0x61:0x62,0x00);// Loop more (off)
	}
	if(name=='lbMode')
	{
		sendMidi(uniqueId,0x90,0,0x73,(param1==1 ? 0x7F : 0x00));// folder
		sendMidi(uniqueId,0x90,0,0x72,(param1==0 ? 0x7F : 0x00));// file
	}	
	if(name == effectsName[effectsSelected[deck]]) sendMidi(uniqueId,0x90,0,(deck==0)?0x63:0x64,(param1 ? 0x7F : 0x00));// effect
}
//----------------------------------------------------------------------------- 
// this is called from time to time by the software (several times per second)
// (it is guaranteed that 'timerTick' will get executed ALWAYS right after notificationReceived)
function timerTick(uniqueId)
{
	//debug('timerTick ['+uniqueId+']');
	for(var i=0; i<2; i++)
	{
		// loop in led
		if(loopingInBlinkLed[i]!=-1)
		{
			// actually set the led
			sendMidi(uniqueId,0x90,0,(i==0)?0x53:0x56,((loopingInBlinkLed[i]<2) ? 0x7F : 0x00));// Loop In (on or off)
			loopingInBlinkLed[i]++;
			if(loopingInBlinkLed[i]>=4) loopingInBlinkLed[i] = 0;
		}
	}
}
//----------------------------------------------------------------------------- 
