//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2013 XYLIO INFO SRL. All Rights Reserved. --------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Numark MixTrack 2 / MixTrack Pro 2' (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 II / MixTrack Pro II";
}
//----------------------------------------------------------------------------- 
// 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 0x004B;// MixTrack Pro 2
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceVID2(osId)
{
	return 0x15E4;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDevicePID2(osId)
{
	return 0x004A;// MixTrack 2
}
//----------------------------------------------------------------------------- 
// 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 "00013F4B";// MixTrack Pro 2
	//F07E00060200013F4B001900010000007F7F7F7F004E030102010209010202000200405F7 - Numark Mixtrack Pro II full sysex
}
//----------------------------------------------------------------------------- 
function getSupportedMidiSysExIdString2(osId)
{
	return "00013F4A";// MixTrack 2
}
//----------------------------------------------------------------------------- 
// 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 II";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==1) return "MixTrack Pro II";
	return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var shiftMode=[0,0,0,0];
var jogWheelsInScratchMode=[0,0,0,0];
var loopingInBlinkLed=[-1,-1,-1,-1];// -1 means disabled
var samplerBlinkLedStatus=[0,0,0,0,0,0,0,0];// 2 decks leds, each with 4 samplers // 0 means off; 1 means on; 2 means blink on; 3 means blink off;
// effects
var cutoffValue=[63,63,63,63];
var flangerValue=[0,0,0,0];
var beatwawValue=[0,0,0,0];
var echoValue=[0,0,0,0];
//----------------------------------------------------------------------------- 
// value is 0x7F for left (-1); 0x01 for right(+1);
function trimEncoderValue(value, increment, initialValue)
{
	var outValue = 0;
	var outIncrement = 0;
	if(value>0x40) // left (decrease)
		outIncrement = increment * (value - 0x80);
	else // left (increase)
		outIncrement = increment * (value);
	outValue = initialValue + outIncrement;
	if(outValue<0) outValue = 0;
	if(outValue>0x7F) outValue = 0x7F;
	return outValue;
}
//----------------------------------------------------------------------------- 
// 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 2 / MixTrack Pro 2 ['+uniqueId+']');// this outputs a string to DebugView app on Win	
	//sendMidi(uniqueId,0x90,0,0x75,0x7F);// power on ALL leds
	//call('sleep', 300);
	//sendMidi(uniqueId,0x90,0,0x75,0x00);// power off ALL leds
	/*
	for(var i=0; i<0x7F; 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
	}
	*/
	// power on some leds that are not set by the notification system
	for(var deck=0; deck<2; deck++)
	{
		sendMidi(uniqueId,0x90,0,(deck==0)?0x48:0x50,(jogWheelsInScratchMode[deck] ? 0x7F : 0x00));// scratch mode A/B
		// top 4 pads leds
		sendMidi(uniqueId,0x90,0,(deck==0)?0x59:0x5D,0x00);// pad 1 A/B
		sendMidi(uniqueId,0x90,0,(deck==0)?0x5A:0x5E,0x00);// pad 2 A/B
		sendMidi(uniqueId,0x90,0,(deck==0)?0x5B:0x5F,0x00);// pad 3 A/B
		sendMidi(uniqueId,0x90,0,(deck==0)?0x5C:0x60,0x00);// pad 4 A/B
		call('sleep', 5);// delay for 5ms to allow the MIDI out buffer to flush
	}	
	// enable loop mode (for the bottom 4 pseudo-pads)
	//sendMidi(uniqueId,0x90,0,0x1E,0x7F);// loop (small led)
	//sendMidi(uniqueId,0x90,0,0x21,0x7F);// loop (small led)
	//------------------------------------
	callSendAllNotifications(uniqueId, true);// send all notifications to the controller (so that we don't have to manually init all leds/lcd screens)
}
//----------------------------------------------------------------------------- 
// 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 ['+uniqueId+']');
	// power off some leds that are not set by the notification system
	sendMidi(uniqueId,0x90,0,0x48,0x00);// scratch mode A
	sendMidi(uniqueId,0x90,0,0x50,0x00);// scratch mode B
	//------------------------------------
	callSendAllNotifications(uniqueId, false);// send all notifications to the controller (so that we don't have to manually de-init all leds/lcd screens)
	//sendMidi(uniqueId,0x90,0,0x75,0x00);// power off ALL leds
}
//----------------------------------------------------------------------------- 
// 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);
			call('lbMoveCursor', val1);
			break;

		//-------------------------------
		// deck A
		case 0x16: 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(shiftMode[0])
			{
				call('searchDeckJW', 0, val1, 600);
			}else
			{
				if(jogWheelsInScratchMode[0]) call('scratchDeckJW', 0, val1, 600);else call('moveDeckJogWheel', 0, val1, 600);
			}
			break;
		// encoder effects
		case 0x1B: // fx 1 encoder (cutoff filter)
			cutoffValue[0] = trimEncoderValue(data2, 2, cutoffValue[0]);
			call('setDeckCutoff', 0, cutoffValue[0]);
			break;
		case 0x1C: // fx 2 encoder (fx flanger)
			flangerValue[0] = trimEncoderValue(data2, 3, flangerValue[0]);
			call('deckFxFlangerDW', 0, flangerValue[0]);
			break;
		case 0x1D: // fx 3 encoder (fx beawaw)
			beatwawValue[0] = trimEncoderValue(data2, 3, beatwawValue[0]);
			call('deckFxBeatwawDW', 0, beatwawValue[0]);
			break;
		case 0x21: // fx 4 encoder (fx echo)
			echoValue[0] = trimEncoderValue(data2, 3, echoValue[0]);
			call('deckFxEchoDW', 0, echoValue[0]);
			break;
			
		//-------------------------------
		// deck B
		case 0x07: 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(shiftMode[1])
			{
				call('searchDeckJW', 1, val1, 600);
			}else
			{
				if(jogWheelsInScratchMode[1]) call('scratchDeckJW', 1, val1, 600);else call('moveDeckJogWheel', 1, val1, 600);
			}
			break;
		// encoder effects
		case 0x1E: // fx 1 encoder (cutoff filter)
			cutoffValue[1] = trimEncoderValue(data2, 2, cutoffValue[1]);
			call('setDeckCutoff', 1, cutoffValue[1]);
			break;
		case 0x1F: // fx 2 encoder (fx flanger)
			flangerValue[1] = trimEncoderValue(data2, 3, flangerValue[1]);
			call('deckFxFlangerDW', 1, flangerValue[1]);
			break;
		case 0x20: // fx 3 encoder (fx beawaw)
			beatwawValue[1] = trimEncoderValue(data2, 3, beatwawValue[1]);
			call('deckFxBeatwawDW', 1, beatwawValue[1]);
			break;
		case 0x22: // fx 4 encoder (fx echo)
			echoValue[1] = trimEncoderValue(data2, 3, echoValue[1]);
			call('deckFxEchoDW', 1, echoValue[1]);
			break;
		
		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		switch(data1)
		{
		// global
		case 0x77:
		case 0x76: if(data2>0x3F) call('lbToggleSelect', data2); break;

		//-------------------------------
		// deck A
		case 0x61: shiftMode[0] = data2; break;		
		case 0x4B: if(data2>0x3F) call('deckLBLoad', 0, data2); break;
		case 0x40: if(shiftMode[0]) call('deckMatch', 0, data2);else call('deckSync', 0, data2); break;
		case 0x33: if(shiftMode[0]) call('deckBackToTop', 0, data2);else call('deckCue', 0, data2); break;
		case 0x3B: if(shiftMode[0]) call('deckReverse', 0, data2);else call('deckPlayPause', 0, data2); break;
		case 0x4A: if(shiftMode[0]) call('deckBrake', 0, data2);else call('deckCuePlay', 0, data2); break;// stutter
		case 0x43: if(shiftMode[0]) call('deckKeyLock', 0, data2);else call('deckBendDown', 0, data2); break;
		case 0x44: 
			if(shiftMode[0]) 
			{
				// 1 = 4%; 2 = 8%; 3 = 16%; 4 = 24%; 5 = 32%; 6 = 50%;
				if(data2>0)
				{ 
					var currentPitchRange = getValue('pitchRange');
					currentPitchRange = currentPitchRange + 1; if(currentPitchRange>6) currentPitchRange = 1;
					call('setPitchRange', currentPitchRange);
				}
			}else 
			{
				call('deckBendUp', 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 0x51: call('deckMonitor', 0, data2); break;
		case 0x4D: if(!shiftMode[0]) call('holdDeckJogWheel', 0, data2); break;
		
		// ATTN: you need to press Shift + Loop/Sampler/Cue to change mode (the LED will light up accordingly)
		// MODE: loop
		case 0x53: call('deckLoopInEnd', 0, data2); break;
		case 0x54: call('deckLoopExit', 0, data2); break;
		case 0x55: // reloop/exit
			var deck = 0;
			if(data2>0x3F)
			{ 
				if(getValue('looping', deck)) 
				{
					call('deckLoopExit', deck, 0x7F);
					call('deckLoopExit', deck, 0x00);
				}else 
				{
					call('deckLoopReloop', deck, 0x7F);
					call('deckLoopReloop', deck, 0x00);
				}
			}
			break;
		case 0x63: call('deckLoopMore', 0, data2); break;
		
		// MODE: sampler
		case 0x65: if(data2>0x3F) call('samplerPlay', 1); break;
		case 0x66: if(data2>0x3F) call('samplerPlay', 2); break;
		case 0x67: if(data2>0x3F) call('samplerPlay', 3); break;
		case 0x68: if(data2>0x3F) call('samplerPlay', 4); break;

		// MODE: cues
		case 0x6D: call('deckCuePos1', 0, data2); break;
		case 0x6E: call('deckCuePos2', 0, data2); break;
		case 0x6F: call('deckCuePos3', 0, data2); break;
		case 0x70: call('deckCuePos4', 0, data2); break;
		
		// various functions (fixed mode, no shift)
		case 0x59: call('deckReverse', 0, data2); break;
		case 0x5A: call('deckBrake', 0, data2); break;
		case 0x5B: call('deckSkipBeatPrev', 0, data2); sendMidi(uniqueId,0x90,0,data1,data2); break;
		case 0x5C: call('deckSkipBeatNext', 0, data2); sendMidi(uniqueId,0x90,0,data1,data2); break;
		
		//-------------------------------
		// deck B
		case 0x62: shiftMode[1] = data2; break;	
		case 0x34: if(data2>0x3F) call('deckLBLoad', 1, data2); break;
		case 0x47: if(shiftMode[1]) call('deckMatch', 1, data2);else call('deckSync', 1, data2); break;
		case 0x3C: if(shiftMode[1]) call('deckBackToTop', 1, data2);else call('deckCue', 1, data2); break;
		case 0x42: if(shiftMode[1]) call('deckReverse', 1, data2);else call('deckPlayPause', 1, data2); break;
		case 0x4C: if(shiftMode[1]) call('deckBrake', 1, data2);else call('deckCuePlay', 1, data2); break;// stutter
		case 0x45: if(shiftMode[1]) call('deckKeyLock', 1, data2);else call('deckBendDown', 1, data2); break;
		case 0x46: 
			if(shiftMode[1]) 
			{
				// 1 = 4%; 2 = 8%; 3 = 16%; 4 = 24%; 5 = 32%; 6 = 50%;
				if(data2>0)
				{ 
					var currentPitchRange = getValue('pitchRange');
					currentPitchRange = currentPitchRange + 1; if(currentPitchRange>6) currentPitchRange = 1;
					call('setPitchRange', currentPitchRange);
				}
			}else
			{
				call('deckBendUp', 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 0x52: call('deckMonitor', 1, data2); break;
		case 0x4E: if(!shiftMode[1]) call('holdDeckJogWheel', 1, data2); break;
		
		// ATTN: you need to press Shift + Loop/Sampler/Cue to change mode (the LED will light up accordingly)
		// MODE: loop
		case 0x56: call('deckLoopInEnd', 1, data2); break;
		case 0x57: call('deckLoopExit', 1, data2); break;
		case 0x58: // reloop/exit
			var deck = 1;
			if(data2>0x3F)
			{ 
				if(getValue('looping', deck)) 
				{
					call('deckLoopExit', deck, 0x7F);
					call('deckLoopExit', deck, 0x00);
				}else
				{
					call('deckLoopReloop', deck, 0x7F);
					call('deckLoopReloop', deck, 0x00);
				}
			}
			break;
		case 0x64: call('deckLoopMore', 1, data2); break;
		
		// MODE: sampler
		case 0x69: if(data2>0x3F) call('samplerPlay', 5); break;
		case 0x6A: if(data2>0x3F) call('samplerPlay', 6); break;
		case 0x6B: if(data2>0x3F) call('samplerPlay', 7); break;
		case 0x6C: if(data2>0x3F) call('samplerPlay', 8); break;

		// MODE: cues
		case 0x71: call('deckCuePos1', 1, data2); break;
		case 0x72: call('deckCuePos2', 1, data2); break;
		case 0x73: call('deckCuePos3', 1, data2); break;
		case 0x74: call('deckCuePos4', 1, data2); break;
		
		// various functions (fixed mode, no shift)
		case 0x5D: call('deckReverse', 1, data2); break;
		case 0x5E: call('deckBrake', 1, data2); break;
		case 0x5F: call('deckSkipBeatPrev', 1, data2); sendMidi(uniqueId,0x90,0,data1,data2); break;
		case 0x60: call('deckSkipBeatNext', 1, data2); sendMidi(uniqueId,0x90,0,data1,data2); 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;
	var nameLen = name.length;
	//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)?0x51:0x52,(param1 ? 0x7F : 0x00));
	if(name=='pitchIs0')	sendMidi(uniqueId,0x90,0,(deck==0)?0x28:0x29,(param1 ? 0x7F : 0x00));		
	//if(name=='reverse') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x4A:0x4C,(param1 ? 0x7F : 0x00));// stutter leds (cueplay) -- not used
	
	if(name=='cuepos1')		sendMidi(uniqueId,0x90,0,(deck==0)?0x6D:0x71,(param1 ? 0x7F : 0x00));
	if(name=='cuepos2')		sendMidi(uniqueId,0x90,0,(deck==0)?0x6E:0x72,(param1 ? 0x7F : 0x00));
	if(name=='cuepos3')		sendMidi(uniqueId,0x90,0,(deck==0)?0x6F:0x73,(param1 ? 0x7F : 0x00));
	if(name=='cuepos4')		sendMidi(uniqueId,0x90,0,(deck==0)?0x70:0x74,(param1 ? 0x7F : 0x00));
	
	if(name=='reverse')		sendMidi(uniqueId,0x90,0,(deck==0)?0x59:0x5D,(param1 ? 0x7F : 0x00));
	if(name=='brake')		sendMidi(uniqueId,0x90,0,(deck==0)?0x5A:0x5E,(param1 ? 0x7F : 0x00));
	//if(name=='cuepos3')		sendMidi(uniqueId,0x90,0,(deck==0)?0x5B:0x5F,(param1 ? 0x7F : 0x00));// not used
	//if(name=='cuepos4')		sendMidi(uniqueId,0x90,0,(deck==0)?0x5C:0x60,(param1 ? 0x7F : 0x00));// not used

	if(name=='loopIn')
	{
		sendMidi(uniqueId,0x90,0,(deck==0)?0x53:0x56,(param1 ? 0x7F : 0x00));// Loop In (on or off)
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x1E:0x21,(param1 ? 0x7F : 0x00));// Loop In (on or off) // loop in/loop (small led)
		// 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)?0x63:0x64,0x00);// Loop more (off)
		// alternate small leds
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x1E:0x21,0x00);// Loop In (on or off) // loop in/loop (small led)
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x1F:0x22,(param1 ? 0x7F : 0x00));// Loop Out (on or off) // loop out/sample (small led)
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x20:0x23,(param1 ? 0x7F : 0x00));// Reloop (on or off) // reloop/cue (small led)
	}
	if(name=='lbMode')
	{
		sendMidi(uniqueId,0x90,0,0x34,(param1==1 ? 0x7F : 0x00));// folder
		sendMidi(uniqueId,0x90,0,0x4B,(param1==0 ? 0x7F : 0x00));// file
	}	

	//var samplerBlinkLedStatus=[0,0,0,0,0,0,0,0];// 2 decks leds, each with 4 samplers // 0 means off; 1 means on; 2 means blink on; 3 means blink off;
	if(nameLen==8)
	{
		var name1 = name.substr(0, nameLen-1);
		if(name1=='sampler')
		{
			var samplerIdx = parseInt(name.charAt(nameLen-1))-1;
			//debug('notif sampler strLen='+nameLen+' idx='+samplerIdx + ' char='+name.charAt(nameLen-1));
			if(!param1) 
			{
				samplerBlinkLedStatus[samplerIdx] = 0;
				if(name=='sampler1') sendMidi(uniqueId,0x90,0,0x65,0x00);
				if(name=='sampler2') sendMidi(uniqueId,0x90,0,0x66,0x00);
				if(name=='sampler3') sendMidi(uniqueId,0x90,0,0x67,0x00);
				if(name=='sampler4') sendMidi(uniqueId,0x90,0,0x68,0x00);
				if(name=='sampler5') sendMidi(uniqueId,0x90,0,0x69,0x00);
				if(name=='sampler6') sendMidi(uniqueId,0x90,0,0x6A,0x00);
				if(name=='sampler7') sendMidi(uniqueId,0x90,0,0x6B,0x00);
				if(name=='sampler8') sendMidi(uniqueId,0x90,0,0x6C,0x00);
				// instead of Math.floor(19/4), parseInt(19/4) = 4 can also be used
			}else
			{
				if(name=='sampler1') sendMidi(uniqueId,0x90,0,0x65,0x7F);
				if(name=='sampler2') sendMidi(uniqueId,0x90,0,0x66,0x7F);
				if(name=='sampler3') sendMidi(uniqueId,0x90,0,0x67,0x7F);
				if(name=='sampler4') sendMidi(uniqueId,0x90,0,0x68,0x7F);
				if(name=='sampler5') sendMidi(uniqueId,0x90,0,0x69,0x7F);
				if(name=='sampler6') sendMidi(uniqueId,0x90,0,0x6A,0x7F);
				if(name=='sampler7') sendMidi(uniqueId,0x90,0,0x6B,0x7F);
				if(name=='sampler8') sendMidi(uniqueId,0x90,0,0x6C,0x7F);
				if(param2>0) samplerBlinkLedStatus[samplerIdx] = 2;else samplerBlinkLedStatus[samplerIdx] = 1;
			}
		}
	}	
}
//----------------------------------------------------------------------------- 
// 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)
			//sendMidi(uniqueId,0x90,0,(i==0)?0x1E:0x21,((loopingInBlinkLed[i]<2) ? 0x7F : 0x00));// Loop In (on or off) // loop in/loop (small led)
			loopingInBlinkLed[i]++;
			if(loopingInBlinkLed[i]>=4) loopingInBlinkLed[i] = 0;
		}
	}
	// sampler leds
	//var samplerBlinkLedStatus=[0,0,0,0,0,0,0,0];// 2 decks leds, each with 4 samplers // 0 means off; 1 means on; 2 means blink on; 3 means blink off;		
	for(var i=0; i<8; i++) 
	{
		// test if blinking is active
		if(samplerBlinkLedStatus[i]>1)
		{
			if(samplerBlinkLedStatus[i]==2) samplerBlinkLedStatus[i] = 3;else samplerBlinkLedStatus[i] = 2;
			switch(i)
			{
			case 0: sendMidi(uniqueId,0x90,0,0x65,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 1: sendMidi(uniqueId,0x90,0,0x66,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 2: sendMidi(uniqueId,0x90,0,0x67,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 3: sendMidi(uniqueId,0x90,0,0x68,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 4: sendMidi(uniqueId,0x90,0,0x69,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 5: sendMidi(uniqueId,0x90,0,0x6A,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 6: sendMidi(uniqueId,0x90,0,0x6B,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 7: sendMidi(uniqueId,0x90,0,0x6C,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			}
		}
	}
}
//----------------------------------------------------------------------------- 
