//-----------------------------------------------------------------------------
//------------ Copyright (c)2003-2018 Xylio. All Rights Reserved. -------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Hercules DJ Console RMX2'
//-----------------------------------------------------------------------------
function getLanguageVersion()
{
	return 1;// do not modify this, this should be 1 for all scripts
}
//-----------------------------------------------------------------------------
function getFileVersion()
{
	return 1;// update this as needed
}
//-----------------------------------------------------------------------------
// return a user friendly name for the controller supported
function getControllerUserFriendlyName()
{
	return "Hercules DJ Console RMX2";
}
//-----------------------------------------------------------------------------
// 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 0x06F8;
}
//-----------------------------------------------------------------------------
function getSupportedMidiDevicePID(osId)
{
	return 0xB113;
}
//-----------------------------------------------------------------------------
// 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 "00014E02000E";
	// F0 7E 00 06 02 00 01 4E 02 00 02 00 01 00 00 00 F7 -- Hercules DJ Console MK2 complete sysex
	// F0 7E 00 06 02 00 01 4E 01 00 03 00 01 00 00 00 F7 -- Hercules DJ Control MP3 complete sysex
	// F0 7E 00 06 02 00 01 4E 02 00 04 00 01 00 00 00 F7 -- Hercules DJ Console RMX complete sysex
	// F0 7E 00 06 02 00 01 4E 01 00 05 00 01 00 00 00 F7 -- Hercules DJ Control Steel complete sysex
	// F0 7E 00 06 02 00 01 4E 01 00 07 00 01 00 00 00 F7 -- Hercules DJ Control MP3e2 complete sysex
	// F0 7E 00 06 02 00 01 4E 02 00 08 00 01 00 00 00 F7 -- Hercules DJ Console MK4 complete sysex
	// F0 7E 00 06 02 00 01 4E 02 00 0B 00 01 00 00 00 F7 -- Hercules DJ Control Air complete sysex
	// F0 7E 00 06 02 00 01 4E 02 00 0C 00 01 00 00 00 F7 -- Hercules DJ Control Instinct complete sysex
	// F0 7E 00 06 02 00 01 4E 02 00 0E 00 13 00 00 00 F7 -- Hercules DJ Console RMX2 complete sysex
}
//-----------------------------------------------------------------------------
// 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)
{
	return "Hercules DJ Console RMX2";
}
//-----------------------------------------------------------------------------
function getSupportedMidiDeviceNameForInput2(osId)
{
	return "DJConsole Rmx2";
}
//-----------------------------------------------------------------------------
function getSupportedMidiDeviceNameForOutput(osId)
{
	return "Hercules DJ Console RMX2";
}
//-----------------------------------------------------------------------------
function getSupportedMidiDeviceNameForOutput2(osId)
{
	return "DJConsole Rmx2";
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// some variables necessary only for this particular script
var jogWheelsInScratchMode=0;
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;
var loopingInBlinkLed=[-1,-1,-1,-1];// -1 means disabled
var cutoffValue=[63,63,63,63];
var pitchKeyValue=[63,63,63,63];
var samplerVolumeValue=[127,127];
//-----------------------------------------------------------------------------
// 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;
}
//-----------------------------------------------------------------------------
var vuLast=[[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]];// used to save the last state
//-----------------------------------------------------------------------------
function setVUmeter(uniqueId, ch, dbValue)
{
	var vuCurrent=[0,0,0,0,0,0];// temp
	if(dbValue>-36) vuCurrent[0] = 0x7F;
	if(dbValue>-24) vuCurrent[1] = 0x7F;
	if(dbValue>-18) vuCurrent[2] = 0x7F;
	if(dbValue>-12) vuCurrent[3] = 0x7F;
	if(dbValue>-8)  vuCurrent[4] = 0x7F;
	if(dbValue>-4)  vuCurrent[5] = 0x7F;
	// 0x49 .. 0x4E left VU-meter
	// 0x4F .. 0x54 right VU-meter
	for(var i=0; i<6; i++)
	{
		if(vuLast[ch][i] != vuCurrent[i])
		{
			vuLast[ch][i] = vuCurrent[i];
			sendMidi(uniqueId, 0x90, 0, (ch==0)?(0x49+i):(0x4F+i), vuLast[ch][i]);
		}
	}
}
//-----------------------------------------------------------------------------
// 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 Hercules DJ Console RMX2 ['+uniqueId+']');// this outputs a string to DebugView app on Win
	// setting audio (the actual routing is done automatically for this controller so
	// we don't need to manually set "RoutingOutHeadphones", "RoutingOutDeckA", etc
	// ATTN: we first try to set ASIO mode, and only then regular CoreAudio, DirectSound audio/WDM/Wasapi
	if(setAudioDevice("DJConsole Rmx2 ASIO|||Hercules DJ Console RMX2|||Hercules DJConsole Rmx2|||DJConsole Rmx2|||Speakers (Hercules DJConsole Rmx2)|||Speakers (DJConsole Rmx2)"))
	{
		//debug('AudioDevice was set OK');
		setSetting('AudioDevice2', "NO AUDIO");
	}
	//sendMidi(uniqueId, status, channel, data1, data2);
	sendMidi(uniqueId,0x90,0,0x35,(jogWheelsInScratchMode ? 0x7F : 0x00));// scratch mode
	//------------------------------------
	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+']');
	// set leds off
	sendMidi(uniqueId,0x90,0,0x35,0x00);// scratch mode
	setVUmeter(uniqueId, 0, -90);
	setVUmeter(uniqueId, 1, -90);
	//------------------------------------
	callSendAllNotifications(uniqueId, false);// send all notifications to the controller (so that we don't have to manually de-init all leds/lcd screens)
}
//-----------------------------------------------------------------------------
// 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 0x49: break;
		case 0x48: call('setCrossfader', data2); break;
		case 0x46: call('setCuePgmMix', data2); break;
		case 0x44: call('setMasterGain', data2); break;

		// per deck
		// deck A
		case 0x40: call('setDeckEqLow', 0, data2); break;
		case 0x3E: call('setDeckEqMid', 0, data2); break;
		case 0x3C: call('setDeckEqHigh', 0, data2); break;
		case 0x3A: call('setDeckVolume', 0, data2); break;
		case 0x42: call('setDeckGain', 0, data2); break;
		case 0x36: call('setDeckPitch', 0, data2); break;
		case 0x30: // params: (deck, increment, total positions per complete revolution)
		case 0x32:
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			if(jogWheelsInScratchMode) call('scratchDeckJW', 0, val1, 256);else call('moveDeckJogWheel', 0, val1, 256);
			break;
			//-----------------------------
			// ENCODER
		case 0x34: // encoder on loop-mode (loop less/more)
			if(data2>0x40) // moved to left
			{
				// we simulate a fast press-release
				call('deckLoopLess', 0, 0x7F);
				call('deckLoopLess', 0, 0x00);
			}else // moved to right
			{
				call('deckLoopMore', 0, 0x7F);
				call('deckLoopMore', 0, 0x00);
			}
			break;
		case 0x54: // encoder on fx-mode (cutoff filter)
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			cutoffValue[0] = trimEncoderValue(data2, 2, cutoffValue[0]);
			call('setDeckCutoff', 0, cutoffValue[0]);
			break;
		case 0x56: // encoder on sample-mode (samples 1-4 volume)
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			samplerVolumeValue[0] = trimEncoderValue(data2, 2, samplerVolumeValue[0]);
			// set the volume for the first 4 samples
			call('samplerVolume', 1, samplerVolumeValue[0]);
			call('samplerVolume', 2, samplerVolumeValue[0]);
			call('samplerVolume', 3, samplerVolumeValue[0]);
			call('samplerVolume', 4, samplerVolumeValue[0]);
			break;
		case 0x58: // encoder on cue-mode (pitch-key)
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			pitchKeyValue[0] = trimEncoderValue(data2, 2, pitchKeyValue[0]);
			call('setDeckPitchKey', 0, pitchKeyValue[0]);
			break;
			//-----------------------------

		// deck B
		case 0x50: call('setDeckEqLow', 1, data2); break;
		case 0x4E: call('setDeckEqMid', 1, data2); break;
		case 0x4C: call('setDeckEqHigh', 1, data2); break;
		case 0x4A: call('setDeckVolume', 1, data2); break;
		case 0x52: call('setDeckGain', 1, data2); break;
		case 0x38: call('setDeckPitch', 1, data2); break;

		case 0x31: // params: (deck, increment, total positions per complete revolution)
		case 0x33:
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			if(jogWheelsInScratchMode) call('scratchDeckJW', 1, val1, 256);else call('moveDeckJogWheel', 1, val1, 256);
			break;
			//-----------------------------
			// ENCODER
		case 0x35: // encoder on loop-mode (loop less/more)
			if(data2>0x40) // moved to left
			{
				// we simulate a fast press-release
				call('deckLoopLess', 1, 0x7F);
				call('deckLoopLess', 1, 0x00);
			}else // moved to right
			{
				call('deckLoopMore', 1, 0x7F);
				call('deckLoopMore', 1, 0x00);
			}
			break;
		case 0x55: // encoder on fx-mode (cutoff filter)
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			cutoffValue[1] = trimEncoderValue(data2, 2, cutoffValue[1]);
			call('setDeckCutoff', 1, cutoffValue[1]);
			break;
		case 0x57: // encoder on sample-mode (samples 5-8 volume)
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			samplerVolumeValue[1] = trimEncoderValue(data2, 2, samplerVolumeValue[1]);
			// set the volume for the first 4 samples
			call('samplerVolume', 5, samplerVolumeValue[1]);
			call('samplerVolume', 6, samplerVolumeValue[1]);
			call('samplerVolume', 7, samplerVolumeValue[1]);
			call('samplerVolume', 8, samplerVolumeValue[1]);
			break;
		case 0x59: // encoder on cue-mode (pitch-key)
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			pitchKeyValue[1] = trimEncoderValue(data2, 2, pitchKeyValue[1]);
			call('setDeckPitchKey', 1, pitchKeyValue[1]);
			break;
			//-----------------------------

		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		switch(data1)
		{
		// global
		/*
		case 0x40:// headphone volume -/+
		case 0x41:
			if(data2>0x3F)
			{
				var monValue = getValue('monitorGain');
				if(data1==0x40) monValue -= 2;else monValue += 2;
				if(monValue<-40) monValue = -40;
				if(monValue>0) monValue = 0;
				// we need to convert from -40..0dB to 0..127 (MIDI)
				call('setMonitorGain', Math.round((monValue+40)*3.175));
			}
			break;
		*/
		case 0x45: if(data2>0x3F) call('lbMoveCursor', -1); break;
		case 0x46: if(data2>0x3F) call('lbMoveCursor', +1); break;
		case 0x43: if(data2>0x3F) call('lbListSelect', 1); break;
		case 0x44: if(data2>0x3F) call('lbBrowserSelect', 1); break;
		case 0x47: if(data2>0x3F){ jogWheelsInScratchMode = (jogWheelsInScratchMode+1)%2; sendMidi(uniqueId,0x90,0,0x47,(jogWheelsInScratchMode ? 0x7F : 0x00));} break;
		case 0x48:
			{
				call('micOnAir', 0x7F);
				call('micOnAir', 0x00);
			}
			break;

		// deck A
		case 0x01: call('deckFxFlanger', 0, data2); break;
		case 0x02: call('deckFxEcho', 0, data2); break;
		case 0x03: call('deckFxBeatwaw', 0, data2); break;
		case 0x04: call('deckFxReverb', 0, data2); break;
		case 0x05: call('samplerPlay', 1, data2); break;
		case 0x06: call('samplerPlay', 2, data2); break;
		case 0x07: call('samplerPlay', 3, data2); break;
		case 0x08: call('samplerPlay', 4, data2); break;
		case 0x09: call('deckCuePos1', 0, data2); break;
		case 0x0A: call('deckCuePos2', 0, data2); break;
		case 0x0B: call('deckCuePos3', 0, data2); break;
		case 0x0C: call('deckCuePos4', 0, data2); break;
		case 0x0D: call('deckLoopInEnd', 0, data2); break;
		case 0x0E:
			if(data2>0x40)
			{
				if(getValue('looping', 0))
				{
					call('deckLoopExit', 0, 0x7F);
					call('deckLoopExit', 0, 0x00);
				}else
				{
					call('deckLoopReloop', 0, 0x7F);
					call('deckLoopReloop', 0, 0x00);
				}
			}
			break;
		case 0x0F: call('deckLoopPrev', 0, data2); break;
		case 0x10: call('deckLoopNext', 0, data2); break;

		case 0x2C: call('deckBendDown', 0, data2); break;
		case 0x2D: call('deckBendUp', 0, data2); break;
		case 0x26: call('deckSearchLeft', 0, data2); break;
		case 0x27: call('deckSearchRight', 0, data2); break;
		case 0x22: call('deckCue', 0, data2); break;
		case 0x21: call('deckPlayPause', 0, data2); break;
		case 0x23: call('deckSync', 0, data2); break;
		case 0x2E: call('deckMonitor', 0, data2); break;
		case 0x28: call('deckEqHighKill', 0, data2); break;
		case 0x29: call('deckEqMidKill', 0, data2); break;
		case 0x2A: call('deckEqLowKill', 0, data2); break;
		case 0x24: if(data2>0x3F) call('deckLBLoad', 0, data2); break;
		case 0x2F: call('holdDeckJogWheel', 0, data2); break;
		case 0x2B: call('deckAnalogIn', 0, data2); break;

		// deck B
		case 0x11: call('deckFxFlanger', 1, data2); break;
		case 0x12: call('deckFxEcho', 1, data2); break;
		case 0x13: call('deckFxBeatwaw', 1, data2); break;
		case 0x14: call('deckFxReverb', 1, data2); break;
		case 0x15: call('samplerPlay', 5, data2); break;
		case 0x16: call('samplerPlay', 6, data2); break;
		case 0x17: call('samplerPlay', 7, data2); break;
		case 0x18: call('samplerPlay', 8, data2); break;
		case 0x19: call('deckCuePos1', 1, data2); break;
		case 0x1A: call('deckCuePos2', 1, data2); break;
		case 0x1B: call('deckCuePos3', 1, data2); break;
		case 0x1C: call('deckCuePos4', 1, data2); break;
		case 0x1D: call('deckLoopInEnd', 1, data2); break;
		case 0x1E:
			if(data2>0x40)
			{
				if(getValue('looping', 1))
				{
					call('deckLoopExit', 1, 0x7F);
					call('deckLoopExit', 1, 0x00);
				}else
				{
					call('deckLoopReloop', 1, 0x7F);
					call('deckLoopReloop', 1, 0x00);
				}
			}
			break;
		case 0x1F: call('deckLoopPrev', 1, data2); break;
		case 0x20: call('deckLoopNext', 1, data2); break;

		case 0x3D: call('deckBendDown', 1, data2); break;
		case 0x3E: call('deckBendUp', 1, data2); break;
		case 0x37: call('deckSearchLeft', 1, data2); break;
		case 0x38: call('deckSearchRight', 1, data2); break;
		case 0x33: call('deckCue', 1, data2); break;
		case 0x32: call('deckPlayPause', 1, data2); break;
		case 0x34: call('deckSync', 1, data2); break;
		case 0x3F: call('deckMonitor', 1, data2); break;
		case 0x39: call('deckEqHighKill', 1, data2); break;
		case 0x3A: call('deckEqMidKill', 1, data2); break;
		case 0x3B: call('deckEqLowKill', 1, data2); break;
		case 0x35: if(data2>0x3F) call('deckLBLoad', 1, data2); break;
		case 0x40: call('holdDeckJogWheel', 1, data2); break;
		case 0x3C: call('deckAnalogIn', 1, 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)
{
	//debug('notif ['+uniqueId+'] name='+name + ' deck='+deck + ' p1(int)='+param1 + ' p2(float)='+param2 + ' p3(str)='+param3);
	if(deck>=2) return;
	var nameLen = name.length;

	if(name=='fxFlanger') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x01:0x11,(param1 ? 0x7F : 0x00));
	if(name=='fxEcho') 		sendMidi(uniqueId,0x90,0,(deck==0)?0x02:0x12,(param1 ? 0x7F : 0x00));
	if(name=='fxBeatwaw') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x03:0x13,(param1 ? 0x7F : 0x00));
	if(name=='fxReverb') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x04:0x14,(param1 ? 0x7F : 0x00));

	if(name=='cuepos1') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x09:0x19,(param1 ? 0x7F : 0x00));
	if(name=='cuepos2') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x0A:0x1A,(param1 ? 0x7F : 0x00));
	if(name=='cuepos3') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x0B:0x1B,(param1 ? 0x7F : 0x00));
	if(name=='cuepos4') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x0C:0x1C,(param1 ? 0x7F : 0x00));

	if(name=='cue') 		sendMidi(uniqueId,0x90,0,(deck==0)?0x22:0x33,(param1 ? 0x7F : 0x00));
	if(name=='play') 		sendMidi(uniqueId,0x90,0,(deck==0)?0x21:0x32,(param1 ? 0x7F : 0x00));
	if(name=='onbeat') 		sendMidi(uniqueId,0x90,0,(deck==0)?0x23:0x34,(param1 ? 0x7F : 0x00));
	if(name=='monitor') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x2E:0x3F,(param1 ? 0x7F : 0x00));
	if(name=='killLow') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x2A:0x3B,(param1 ? 0x7F : 0x00));
	if(name=='killMid') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x29:0x3A,(param1 ? 0x7F : 0x00));
	if(name=='killHigh') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x28:0x39,(param1 ? 0x7F : 0x00));
	if(name=='analogIn') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x2B:0x3C,(param1 ? 0x7F : 0x00));

	if(name=='loopIn')
	{
		sendMidi(uniqueId,0x90,0,(deck==0)?0x0D:0x1D,(param1 ? 0x7F : 0x00));// loop in/out
		// 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)?0x0E:0x1E,(param1 ? 0x7F : 0x00));// loop exit
	}
	//if(name=='autoDj') 		sendMidi(uniqueId,0x90,0,0x2E,(param1 ? 0x7F : 0x00));// automix
	//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,0x05,0x00);
				if(name=='sampler2') sendMidi(uniqueId,0x90,0,0x06,0x00);
				if(name=='sampler3') sendMidi(uniqueId,0x90,0,0x07,0x00);
				if(name=='sampler4') sendMidi(uniqueId,0x90,0,0x08,0x00);
				if(name=='sampler5') sendMidi(uniqueId,0x90,0,0x15,0x00);
				if(name=='sampler6') sendMidi(uniqueId,0x90,0,0x16,0x00);
				if(name=='sampler7') sendMidi(uniqueId,0x90,0,0x17,0x00);
				if(name=='sampler8') sendMidi(uniqueId,0x90,0,0x18,0x00);
				// instead of Math.floor(19/4), parseInt(19/4) = 4 can also be used
			}else
			{
				if(name=='sampler1') sendMidi(uniqueId,0x90,0,0x05,0x7F);
				if(name=='sampler2') sendMidi(uniqueId,0x90,0,0x06,0x7F);
				if(name=='sampler3') sendMidi(uniqueId,0x90,0,0x07,0x7F);
				if(name=='sampler4') sendMidi(uniqueId,0x90,0,0x08,0x7F);
				if(name=='sampler5') sendMidi(uniqueId,0x90,0,0x15,0x7F);
				if(name=='sampler6') sendMidi(uniqueId,0x90,0,0x16,0x7F);
				if(name=='sampler7') sendMidi(uniqueId,0x90,0,0x17,0x7F);
				if(name=='sampler8') sendMidi(uniqueId,0x90,0,0x18,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+']');
	// VU-meters
	for(var i=0; i<2; i++)
	{
		var dbValue = getValue('vumeterDeck', i);// float dB
		setVUmeter(uniqueId, i, dbValue);
		// loop in led
		if(loopingInBlinkLed[i]!=-1)
		{
			// actually set the led
			sendMidi(uniqueId,0x90,0,(i==0)?0x0D:0x1D,((loopingInBlinkLed[i]<5) ? 0x7F : 0x00));// loop in/out
			loopingInBlinkLed[i]++;
			if(loopingInBlinkLed[i]>=10) 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,0x05,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 1: sendMidi(uniqueId,0x90,0,0x06,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 2: sendMidi(uniqueId,0x90,0,0x07,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 3: sendMidi(uniqueId,0x90,0,0x08,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 4: sendMidi(uniqueId,0x90,0,0x15,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 5: sendMidi(uniqueId,0x90,0,0x16,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 6: sendMidi(uniqueId,0x90,0,0x17,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			case 7: sendMidi(uniqueId,0x90,0,0x18,((samplerBlinkLedStatus[i]==2) ? 0x7F : 0x00)); break;
			}
		}
	}
}
//-----------------------------------------------------------------------------
