//-----------------------------------------------------------------------------
//------------ Copyright (c)2003-2016 Xylio. All Rights Reserved. -------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Hercules P32 DJ'
//----------------------------------------------------------------------------- 
// TODO: 
// for version 2 we should add:
// - slicer (and slicerLoop) cursor
//----------------------------------------------------------------------------- 
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 P32 DJ";
}
//----------------------------------------------------------------------------- 
// 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 getSupportedMidiDeviceVID(osId)
{
	return 0x06F8;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDevicePID(osId)
{
	return 0xB121;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceVID2(osId)
{
	return false;
	//return 0x06F8;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDevicePID2(osId)
{
	return false;
	//return 0xB11C;
}
//----------------------------------------------------------------------------- 
// 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 "00014E020017";
	// complete sysex list:
	// F0 7E 00 06 02 00 01 4E 02 00 02 00 01 00 00 00 F7 -- Hercules DJ Console MK2 
	// F0 7E 00 06 02 00 01 4E 01 00 03 00 01 00 00 00 F7 -- Hercules DJ Control MP3
	// F0 7E 00 06 02 00 01 4E 02 00 04 00 01 00 00 00 F7 -- Hercules DJ Console RMX
	// F0 7E 00 06 02 00 01 4E 01 00 05 00 01 00 00 00 F7 -- Hercules DJ Control Steel
	// F0 7E 00 06 02 00 01 4E 01 00 07 00 01 00 00 00 F7 -- Hercules DJ Control MP3e2
	// F0 7E 00 06 02 00 01 4E 02 00 08 00 01 00 00 00 F7 -- Hercules DJ Console MK4
	// F0 7E 00 06 02 00 01 4E 02 00 0B 00 01 00 00 00 F7 -- Hercules DJ Control Air
	// F0 7E 00 06 02 00 01 4E 02 00 0C 00 01 00 00 00 F7 -- Hercules DJ Control Instinct
	// F0 7E 00 06 02 00 01 4E 02 00 10 00 01 00 00 00 F7 -- Hercules DJ Control Air+
	// F0 7E 00 06 02 00 01 4E 02 00 0A 00 01 00 00 00 F7 -- Hercules DJ4Set
	// F0 7E 00 06 02 00 01 4E 02 00 09 00 01 00 00 00 F7 -- Hercules DJ Console 4-Mx
	// F0 7E 00 06 02 00 01 4E 02 00 13 00 01 00 00 00 F7 -- Hercules DJControl Jogvision
	// F0 7E 00 06 02 00 01 4E 02 00 17 00 01 00 00 00 F7 -- Hercules P32 DJ
}
//----------------------------------------------------------------------------- 
// 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==0) return "Hercules P32 DJ";
	return "Hercules P32 DJ";
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==0) return "Hercules P32 DJ";
	return "Hercules P32 DJ";
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var cutoffValue=[63,63,63,63];// filter value for setDeckCutoff (63 is at the middle so it means disabled)
var shiftOn=[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 Hercules P32 DJ ['+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("Hercules P32 DJ ASIO|||Hercules P32 DJ|||Hercules P32DJ|||HerculesP32DJ|||P32DJ|||Speakers (Hercules P32 DJ)|||Speakers (2- Hercules P32 DJ)|||Speakers (P32 DJ)|||Speakers (2- P32 DJ)"))
	{
		//debug('AudioDevice was set OK');
		setSetting('AudioDevice2', "NO AUDIO");	
	}	
	//------------------------------------
	callSendAllNotifications(uniqueId, true);// send all notifications to the controller (so that we don't have to manually init all leds/lcd screens)
	// update all analog controls (eg. fader/knob positions)
	sendMidi(uniqueId,0xB0,0,0x7F,0x7F);
}
//----------------------------------------------------------------------------- 
// 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+']');
	//------------------------------------
	callSendAllNotifications(uniqueId, false);// send all notifications to the controller (so that we don't have to manually de-init all leds/lcd screens)
	// set leds off
	for(var deck=0; deck<2; deck++)
	{
		// loop 7-segment displays
		// we first set it to loop 1 so that the dot will disappear (unfortunately we cannot erase the dot otherwise)
		sendMidi(uniqueId,0xB0,deck+1,0x1B,0x05);
		sendMidi(uniqueId,0xB0,deck+1,0x1C,0x00);
		sendMidi(uniqueId,0xB0,deck+1,0x1D,0x00);
	}
}
//----------------------------------------------------------------------------- 
// 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 0x01: 
			if(channel==0) 
			{
				call('setCrossfader', data2); 
			}else
			{
				if(channel<3) call('setDeckVolume', channel-1, data2);
			}
			break;
		case 0x02: 
			if(channel==0) // move track
			{ 
				var val1 = (data2>0x40 ? data2-0x80 : data2);
				call('lbMoveCursor', val1);// library cursor up/down
			}else
			{
				if(channel<3) call('setDeckEqLow', channel-1, data2);
			}
			break;
		case 0x03: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('setDeckEqMid', channel-1, data2);
			}
			break;
		case 0x04: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('setDeckEqHigh', channel-1, data2);
			}
			break;
		case 0x05: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) 
				{
					// Filter Encoder
					var deck = channel - 1;
					cutoffValue[deck] = trimEncoderValue(data2, 2, cutoffValue[deck]);
					call('setDeckCutoff', deck, cutoffValue[deck]);
				}
				if(channel>=4)
				{
					// Move Encoder (Filter Encoder + SHIFT)
					if(data2>0x40) // to the left (0x7F = -1)
					{
						call('deckSkipBarPrev', channel-4, 0x7F);
						call('deckSkipBarPrev', channel-4, 0x00);
					}else // to the right (0x01 = +1)
					{
						call('deckSkipBarNext', channel-4, 0x7F);
						call('deckSkipBarNext', channel-4, 0x00);
					}
				}
			}
			break;
		case 0x06: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('deckFxFlangerDW', channel-1, data2);
			}
			break;
		case 0x07: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('deckFxEchoDW', channel-1, data2);
			}
			break;
		case 0x08: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('deckFxBeatwawDW', channel-1, data2);
			}
			break;
		case 0x09: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('deckFxReverbDW', channel-1, data2);
			}
			break;
		case 0x0A: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3)
				{
					// Loop / Active Encoder
					if(data2>0x40) // to the left (0x7F = -1)
					{
						call('deckLoopLess', channel-1, 0x7F);
						call('deckLoopLess', channel-1, 0x00);
					}else // to the right (0x01 = +1)
					{
						call('deckLoopMore', channel-1, 0x7F);
						call('deckLoopMore', channel-1, 0x00);
					}
				}
				if(channel>=4)
				{
					// Tempo / Adjust Encoder (Loop / Active Encoder + SHIFT)
					call('moveDeckPitch', channel-4, (data2>0x40 ? data2-0x80 : data2));// increment mode
				}
			}
			break;
					
		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		switch(data1)
		{
		case 0x01: 
			if(channel==0)
			{ 
				if(data2>0x3F) call('lbToggleSelect', data2);
			}else
			{
				if(channel<3)
				{
					// Loop / Active Encoder push
					if(data2>0x40)
					{ 
						if(getValue('looping', channel-1)) 
						{
							call('deckLoopExit', channel-1, 0x7F);
							call('deckLoopExit', channel-1, 0x00);
						}else 
						{
							call('deckLoopInEnd', channel-1, 0x7F);
							call('deckLoopInEnd', channel-1, 0x00);
							//call('deckLoopReloop', channel-1, 0x7F);
							//call('deckLoopReloop', channel-1, 0x00);
						}
					}
				}
				if(channel==3)
				{
					// Loop / Active Encoder push + SHIFT
					call('mixNext', data2);
				}
				if(channel>=4)
				{
					// Tempo / Adjust Encoder push (Loop / Active Encoder push + SHIFT)
					call('deckResetPitch', channel-4, data2);
				}				
			}
			break;
		case 0x02: 
			if(channel==0)
			{ 
				call('record', data2);
			}else
			{
				if(channel<3)
				{
					// Filter / On/Off Encoder push
					if(data2>0x40)
					{				
						// we only use it as OFF mode for the cutoff filter
						var deck = channel - 1;
						if(cutoffValue[deck] != 63)
						{	
							cutoffValue[deck] = 63;
							call('setDeckCutoff', deck, cutoffValue[deck]);
						}
					}
				}
				if(channel>=4)
				{
					// Move Encoder push (Filter / On/Off Encoder push + SHIFT)
					call('deckBackToTop', channel-4, data2);
				}				
			}
			break;
		case 0x03: 
			if(channel==0)
			{ 
				call('deckLoopMode', channel, data2);// SLIP mode
			}else
			{
				if(channel<3) call('deckFxFlanger', channel-1, data2);
			}
			break;
		case 0x04: 
			if(channel==0)
			{ 
				// Cue Volume +
				if(data2>0x3F) 
				{
					var monValue = getValue('monitorGain');
					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));
				}
			}else
			{
				if(channel<3) call('deckFxEcho', channel-1, data2);
			}
			break;
		case 0x05: 
			if(channel==0)
			{ 
				// Cue Volume -
				if(data2>0x3F) 
				{
					var monValue = getValue('monitorGain');
					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));
				}				
			}else
			{
				if(channel<3) call('deckFxBeatwaw', channel-1, data2);
			}
			break;
		case 0x06: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('deckFxReverb', channel-1, data2);
			}
			break;
		case 0x07: 
			if(channel==0)
			{ 
			}else
			{
				// SHIFT pressed/released - ATTN: it does send on channels 1 and 2 (not global on channel 0)
				shiftOn[channel-1] = data2;
			}
			break;
		case 0x08: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('deckSync', channel-1, data2);
				if(channel>=4) call('deckMatch', channel-4, data2);// Sync + SHIFT
			}
			break;
		case 0x09: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('deckCue', channel-1, data2);
				if(channel>=4) call('deckBackToTop', channel-4, data2);// Cue + SHIFT
			}
			break;
		case 0x0A: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('deckPlayPause', channel-1, data2);
				if(channel>=4) call('deckReverse', channel-4, data2);// Play + SHIFT
			}
			break;
		case 0x0B: 
			if(channel==0)
			{ 
			}else
			{
				//if(channel<3) call('deckPlayPause', channel-1, data2);
			}
			break;
		case 0x0C: 
			if(channel==0)
			{ 
			}else
			{
				//if(channel<3) call('deckPlayPause', channel-1, data2);
			}
			break;
		case 0x0D: 
			if(channel==0)
			{ 
			}else
			{
				//if(channel<3) call('deckPlayPause', channel-1, data2);
			}
			break;
		case 0x0E: 
			if(channel==0)
			{ 
			}else
			{
				//if(channel<3) call('deckPlayPause', channel-1, data2);
			}
			break;			
		case 0x0F: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) if(data2>0x3F) call('deckLBLoad', channel-1, data2);
			}
			break;
		case 0x10: 
			if(channel==0)
			{ 
			}else
			{
				if(channel<3) call('deckMonitor', channel-1, data2);
			}
			break;
			
		//--------------------
		//--------------------
		//--------------------
		// 16 PADs / deck (4 modes, each also with SHIFT)
		//--------------------
		// mode 1: HOT CUE
		// 1st row  / top row (4 pads)
		case 0x60: 
			if((channel>0)&&(channel<3)) call('deckCuePos1', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos1Clear', channel-4, data2);// with Shift
			break;
		case 0x61: 
			if((channel>0)&&(channel<3)) call('deckCuePos2', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos2Clear', channel-4, data2);// with Shift
			break;
		case 0x62: 
			if((channel>0)&&(channel<3)) call('deckCuePos3', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos3Clear', channel-4, data2);// with Shift
			break;
		case 0x63: 
			if((channel>0)&&(channel<3)) call('deckCuePos4', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos4Clear', channel-4, data2);// with Shift
			break;
			
		// 2nd row  / middle-top row (4 pads)
		case 0x5C: 
			if((channel>0)&&(channel<3)) call('deckCuePos5', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos5Clear', channel-4, data2);// with Shift
			break;
		case 0x5D: 
			if((channel>0)&&(channel<3)) call('deckCuePos6', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos6Clear', channel-4, data2);// with Shift
			break;
		case 0x5E: 
			if((channel>0)&&(channel<3)) call('deckCuePos7', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos7Clear', channel-4, data2);// with Shift
			break;
		case 0x5F: 
			if((channel>0)&&(channel<3)) call('deckCuePos8', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos8Clear', channel-4, data2);// with Shift
			break;

		// 3rd row  / middle-bottom row (4 pads)
		case 0x58: 
			if((channel>0)&&(channel<3)) call('deckCuePos9', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos9Clear', channel-4, data2);// with Shift
			break;
		case 0x59: 
			if((channel>0)&&(channel<3)) call('deckCuePos10', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos10Clear', channel-4, data2);// with Shift
			break;
		case 0x5A: 
			if((channel>0)&&(channel<3)) call('deckCuePos11', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos11Clear', channel-4, data2);// with Shift
			break;
		case 0x5B: 
			if((channel>0)&&(channel<3)) call('deckCuePos12', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos12Clear', channel-4, data2);// with Shift
			break;

		// 4th row  / bottom row (4 pads)
		case 0x54: 
			if((channel>0)&&(channel<3)) call('deckCuePos13', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos13Clear', channel-4, data2);// with Shift
			break;
		case 0x55: 
			if((channel>0)&&(channel<3)) call('deckCuePos14', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos14Clear', channel-4, data2);// with Shift
			break;
		case 0x56: 
			if((channel>0)&&(channel<3)) call('deckCuePos15', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos15Clear', channel-4, data2);// with Shift
			break;
		case 0x57: 
			if((channel>0)&&(channel<3)) call('deckCuePos16', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckCuePos16Clear', channel-4, data2);// with Shift
			break;
			
		//--------------------
		// mode 2: LOOP
		// 1st row  / top row (4 pads)
		case 0x50: 
			if((channel>0)&&(channel<3)) call('deckLoop1_16', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopPoint1', channel-4, data2);// with Shift
			break;
		case 0x51: 
			if((channel>0)&&(channel<3)) call('deckLoop1_8', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopPoint2', channel-4, data2);// with Shift
			break;
		case 0x52: 
			if((channel>0)&&(channel<3)) call('deckLoop1_4', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopPoint3', channel-4, data2);// with Shift
			break;
		case 0x53: 
			if((channel>0)&&(channel<3)) call('deckLoop1_2', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopPoint4', channel-4, data2);// with Shift
			break;
			
		// 2nd row  / middle-top row (4 pads)
		case 0x4C: 
			if((channel>0)&&(channel<3)) call('deckLoop1', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopPoint5', channel-4, data2);// with Shift
			break;
		case 0x4D: 
			if((channel>0)&&(channel<3)) call('deckLoop2', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopPoint6', channel-4, data2);// with Shift
			break;
		case 0x4E: 
			if((channel>0)&&(channel<3)) call('deckLoop4', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopPoint7', channel-4, data2);// with Shift
			break;
		case 0x4F: 
			if((channel>0)&&(channel<3)) call('deckLoop8', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopPoint8', channel-4, data2);// with Shift
			break;

		// 3rd row  / middle-bottom row (4 pads)
		case 0x48: 
			if((channel>0)&&(channel<3)) call('deckLoopRoll1', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopCue1', channel-4, data2);// with Shift
			break;
		case 0x49: 
			if((channel>0)&&(channel<3)) call('deckLoopRoll2', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopCue2', channel-4, data2);// with Shift
			break;
		case 0x4A: 
			if((channel>0)&&(channel<3)) call('deckLoopRoll3', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopCue3', channel-4, data2);// with Shift
			break;
		case 0x4B: 
			if((channel>0)&&(channel<3)) call('deckLoopRoll4', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopCue4', channel-4, data2);// with Shift
			break;

		// 4th row  / bottom row (4 pads)
		case 0x44: 
			if((channel>0)&&(channel<3)) call('deckLoopRoll5', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopCue5', channel-4, data2);// with Shift
			break;
		case 0x45: 
			if((channel>0)&&(channel<3)) call('deckLoopRoll6', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopCue6', channel-4, data2);// with Shift
			break;
		case 0x46: 
			if((channel>0)&&(channel<3)) call('deckLoopRoll7', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopCue7', channel-4, data2);// with Shift
			break;
		case 0x47: 
			if((channel>0)&&(channel<3)) call('deckLoopRoll8', channel-1, data2); 
			if((channel>3)&&(channel<6)) call('deckLoopCue8', channel-4, data2);// with Shift
			break;

		//--------------------
		// mode 3: SLICER
		// 1st row  / top row (4 pads)
		case 0x40: 
			if((channel>0)&&(channel<3)) call('deckSliceCue1', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x41: 
			if((channel>0)&&(channel<3)) call('deckSliceCue2', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x42: 
			if((channel>0)&&(channel<3)) call('deckSliceCue3', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x43: 
			if((channel>0)&&(channel<3)) call('deckSliceCue4', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
			
		// 2nd row  / middle-top row (4 pads)
		case 0x3C: 
			if((channel>0)&&(channel<3)) call('deckSliceCue5', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x3D: 
			if((channel>0)&&(channel<3)) call('deckSliceCue6', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x3E: 
			if((channel>0)&&(channel<3)) call('deckSliceCue7', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x3F: 
			if((channel>0)&&(channel<3)) call('deckSliceCue8', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;

		// 3rd row  / middle-bottom row (4 pads)
		case 0x38: 
			if((channel>0)&&(channel<3)) call('deckSliceLoopCue1', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x39: 
			if((channel>0)&&(channel<3)) call('deckSliceLoopCue2', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x3A: 
			if((channel>0)&&(channel<3)) call('deckSliceLoopCue3', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x3B: 
			if((channel>0)&&(channel<3)) call('deckSliceLoopCue4', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;

		// 4th row  / bottom row (4 pads)
		case 0x34: 
			if((channel>0)&&(channel<3)) call('deckSliceLoopCue5', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x35: 
			if((channel>0)&&(channel<3)) call('deckSliceLoopCue6', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x36: 
			if((channel>0)&&(channel<3)) call('deckSliceLoopCue7', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x37: 
			if((channel>0)&&(channel<3)) call('deckSliceLoopCue8', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
			
		//--------------------
		// mode 4: SAMPLER
		// 1st row  / top row (4 pads)
		case 0x30: 
			if((channel>0)&&(channel<3)) call('sampler1', data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x31: 
			if((channel>0)&&(channel<3)) call('sampler2', data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x32: 
			if((channel>0)&&(channel<3)) call('sampler3', data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x33: 
			if((channel>0)&&(channel<3)) call('sampler4', data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
			
		// 2nd row  / middle-top row (4 pads)
		case 0x2C: 
			if((channel>0)&&(channel<3)) call('sampler5', data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x2D: 
			if((channel>0)&&(channel<3)) call('sampler6', data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x2E: 
			if((channel>0)&&(channel<3)) call('sampler7', data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x2F: 
			if((channel>0)&&(channel<3)) call('sampler8', data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;

		// 3rd row  / middle-bottom row (4 pads)
		case 0x28: 
			if((channel>0)&&(channel<3)) call('deckPitchCue1', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x29: 
			if((channel>0)&&(channel<3)) call('deckPitchCue2', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x2A: 
			if((channel>0)&&(channel<3)) call('deckPitchCue3', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x2B: 
			if((channel>0)&&(channel<3)) call('deckPitchCue4', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;

		// 4th row  / bottom row (4 pads)
		case 0x24: 
			if((channel>0)&&(channel<3)) call('deckPitchCue5', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x25: 
			if((channel>0)&&(channel<3)) call('deckPitchCue6', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x26: 
			if((channel>0)&&(channel<3)) call('deckPitchCue7', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			break;
		case 0x27: 
			if((channel>0)&&(channel<3)) call('deckPitchCue8', channel-1, data2); 
			//if((channel>3)&&(channel<6)) call('_noAction_', channel-4, data2);// with Shift
			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=='recording') 	sendMidi(uniqueId,0x90,0,  0x02,(param1 ? 0x7F : 0x00));
	if(name=='loopMode')	sendMidi(uniqueId,0x90,0,  0x03,(param1 ? 0x7F : 0x00));// SLIP mode
	// effects
	if(name=='fxFlanger')	sendMidi(uniqueId,0x90,deck+1,0x03,(param1 ? 0x7F : 0x00));// fx1
	if(name=='fxEcho') 		sendMidi(uniqueId,0x90,deck+1,0x04,(param1 ? 0x7F : 0x00));// fx2
	if(name=='fxBeatwaw') 	sendMidi(uniqueId,0x90,deck+1,0x05,(param1 ? 0x7F : 0x00));// fx3
	if(name=='fxReverb') 	sendMidi(uniqueId,0x90,deck+1,0x06,(param1 ? 0x7F : 0x00));// fx4

	if(name=='onbeat') 		sendMidi(uniqueId,0x90,deck+1,0x08,(param1 ? 0x7F : 0x00));// sync button
	if(name=='cue') 		sendMidi(uniqueId,0x90,deck+1,0x09,(param1 ? 0x7F : 0x00));
	if(name=='play') 		sendMidi(uniqueId,0x90,deck+1,0x0A,(param1 ? 0x7F : 0x00));
	if(name=='reverse') 	sendMidi(uniqueId,0x90,deck+4,0x0A,(param1 ? 0x7F : 0x00));// play + Shift	
	if(name=='monitor') 	sendMidi(uniqueId,0x90,deck+1,0x10,(param1 ? 0x7F : 0x00));
	
	//-----------------
	//-----------------
	//-----------------
	// 16 PADs / deck (4 modes, each also with SHIFT)
	/*
	INFO: Pads can be Red, Blue and Purple with different brightness.
		0x00 = OFF
		0x01..0x28 = Color Red, dimmed to bright    0x20
		0x29..0x50 = Color Blue, dimmed to bright   0x40
		0x51..0x78 = Color Purple, dimmed to bright 0x60
		0x79..0x7C = OFF 0x70
		0x7D = Red Max
		0x7E = Blue Max
		0x7F = Purple Max
	*/
	//-----------------
	// mode 1: HOT CUE (normal: deckCuePos1..16 / Shift: deckCuePos1Clear..16)
	if((nameLen>=7)&&(nameLen<=8))
	{
		var idx = parseInt(name.substr(6));
		// 1st row  / top row (4 pads)
		if((idx>=1)&&(idx<=4))
		{
			sendMidi(uniqueId,0x90,deck+1,0x60+idx-1,(param1 ? 0x7E : 0x00));// blue;
			sendMidi(uniqueId,0x90,deck+4,0x60+idx-1,(param1 ? 0x7D : 0x00));// red; with Shift
		}
		// 2nd row  / middle-top row (4 pads)
		if((idx>=5)&&(idx<=8))
		{
			sendMidi(uniqueId,0x90,deck+1,0x5C+idx-5,(param1 ? 0x7E : 0x00));// blue;
			sendMidi(uniqueId,0x90,deck+4,0x5C+idx-5,(param1 ? 0x7D : 0x00));// red; with Shift
		}
		// 3rd row  / middle-bottom row (4 pads)
		if((idx>=9)&&(idx<=12))
		{
			sendMidi(uniqueId,0x90,deck+1,0x58+idx-9,(param1 ? 0x7E : 0x00));// blue;
			sendMidi(uniqueId,0x90,deck+4,0x58+idx-9,(param1 ? 0x7D : 0x00));// red; with Shift
		}
		// 4th row  / bottom row (4 pads)
		if((idx>=13)&&(idx<=16))
		{
			sendMidi(uniqueId,0x90,deck+1,0x54+idx-13,(param1 ? 0x7E : 0x00));// blue;
			sendMidi(uniqueId,0x90,deck+4,0x54+idx-13,(param1 ? 0x7D : 0x00));// red; with Shift
		}		
	}
	//-----------------
	// mode 2: LOOP (normal: deckLoop1_16..8, deckLoopRoll1..8 / Shift: deckLoopPoint1..8, deckLoopCue1..8)
	if(name=='loopSize')
	{
		//debug('deck '+deck+', loopSize='+param1);
		// first 8 pads (counting from top-left)
		sendMidi(uniqueId,0x90,deck+1,0x50,((param1==  4) ? 0x7D : 0x00));// red; 1/16
		sendMidi(uniqueId,0x90,deck+1,0x51,((param1==  8) ? 0x7D : 0x00));// 1/8
		sendMidi(uniqueId,0x90,deck+1,0x52,((param1== 16) ? 0x7D : 0x00));// 1/4
		sendMidi(uniqueId,0x90,deck+1,0x53,((param1== 32) ? 0x7D : 0x00));// 1/2
		sendMidi(uniqueId,0x90,deck+1,0x4C,((param1== 64) ? 0x7D : 0x00));// 1
		sendMidi(uniqueId,0x90,deck+1,0x4D,((param1==128) ? 0x7D : 0x00));// 2
		sendMidi(uniqueId,0x90,deck+1,0x4E,((param1==256) ? 0x7D : 0x00));// 4
		sendMidi(uniqueId,0x90,deck+1,0x4F,((param1==512) ? 0x7D : 0x00));// 8
		
		// each deck has 2 simple 7 segment displays
		// Display_Both_A: 0xB1..0xB2 0x1B value: 0x00..0x0B = loop size 1/32 to 64; 0x0C..0x0E = ". ", " .", ". ."
		// Display_DG1_A:  0xB1..0xB2 0x1C value: 1 bit for each segment from 0 to 7F (there are 7 segments)
		// Display_DG2_A:  0xB1..0xB2 0x1D value: 1 bit for each segment from 0 to 7F (there are 7 segments)
		var finalValue = -1;
		switch(param1)
		{
		case    0: finalValue = -1;   break;// loop disabled
		case    1:                    break;// loop 1/64 (not used)
		case    2: finalValue = 0x00; break;// loop 1/32
		case    4: finalValue = 0x01; break;// loop 1/16
		case    8: finalValue = 0x02; break;// loop 1/8
		case   16: finalValue = 0x03; break;// loop 1/4
		case   32: finalValue = 0x04; break;// loop 1/2
		case   64: finalValue = 0x05; break;// loop 1
		case  128: finalValue = 0x06; break;// loop 2
		case  256: finalValue = 0x07; break;// loop 4
		case  512: finalValue = 0x08; break;// loop 8
		case 1024: finalValue = 0x09; break;// loop 16
		case 2048: finalValue = 0x0A; break;// loop 32
		case 4096: finalValue = 0x0B; break;// loop 64
		}
		// set both digits at once
		if(finalValue!=-1)
		{
			sendMidi(uniqueId,0xB0,deck+1,0x1B,finalValue);
		}else
		{	
			sendMidi(uniqueId,0xB0,deck+1,0x1B,0x05);// we first set it to loop 1 so that the dot will disappear
			sendMidi(uniqueId,0xB0,deck+1,0x1C,0x00);
			sendMidi(uniqueId,0xB0,deck+1,0x1D,0x00);
		}
	}
	if(name=='loopBlink')	 
	{
		// 'loopBlink' = off (no loopIn, no looping); blink (loopIn, no looping yet); on (no loopIn, looping);
		if(getValue('loopIn', deck)) 
		{
			sendMidi(uniqueId,0xB0,deck+1,0x1B,(param1 ? 0x0C : 0x0D));// alternate the 2 dots
		}else
		{
			if(!getValue('looping', deck)) 
			{
				sendMidi(uniqueId,0xB0,deck+1,0x1B,0x05);// we first set it to loop 1 so that the dot will disappear
				sendMidi(uniqueId,0xB0,deck+1,0x1C,0x00);
				sendMidi(uniqueId,0xB0,deck+1,0x1D,0x00);
			}
		}
	}
	// first 8 pads + SHIFT
	if(nameLen==8)
	{
		// 8 (lower) pads for loop points
		if(name=='looppos1') sendMidi(uniqueId,0x90,deck+4,0x50,(param1 ? 0x7E : 0x00));// blue; with Shift
		if(name=='looppos2') sendMidi(uniqueId,0x90,deck+4,0x51,(param1 ? 0x7E : 0x00));
		if(name=='looppos3') sendMidi(uniqueId,0x90,deck+4,0x52,(param1 ? 0x7E : 0x00));
		if(name=='looppos4') sendMidi(uniqueId,0x90,deck+4,0x53,(param1 ? 0x7E : 0x00));

		if(name=='looppos5') sendMidi(uniqueId,0x90,deck+4,0x4C,(param1 ? 0x7E : 0x00));
		if(name=='looppos6') sendMidi(uniqueId,0x90,deck+4,0x4D,(param1 ? 0x7E : 0x00));
		if(name=='looppos7') sendMidi(uniqueId,0x90,deck+4,0x4E,(param1 ? 0x7E : 0x00));
		if(name=='looppos8') sendMidi(uniqueId,0x90,deck+4,0x4F,(param1 ? 0x7E : 0x00));
	}
	//-----------------
	// mode 3: SLICER (normal: deckSliceCue1..8, deckSliceLoopCue1..8 / Shift: none mapped)
	// ATTN: nothing to be mapped for now (the slicer doesn't currently send anything but it will have to send at least the cursor position)
	//-----------------
	// mode 4: SAMPLER (normal: sampler1..8, deckPitchCue1..8 / Shift: none mapped)
	// sampler leds 1..8
	if(nameLen==13)
	{
		var name1 = name.substr(0, nameLen-1);
		if(name1=='samplerBlink')
		{
			// PADs 1..8 left side (deck A) and right side (deck B) - we use blue leds
			for(var d=0; d<2; d++)
			{
				if(name=='samplerBlink1') sendMidi(uniqueId,0x90,d+1,0x30,(param1 ? 0x7E : 0x00));
				if(name=='samplerBlink2') sendMidi(uniqueId,0x90,d+1,0x31,(param1 ? 0x7E : 0x00));
				if(name=='samplerBlink3') sendMidi(uniqueId,0x90,d+1,0x32,(param1 ? 0x7E : 0x00));
				if(name=='samplerBlink4') sendMidi(uniqueId,0x90,d+1,0x33,(param1 ? 0x7E : 0x00));
				if(name=='samplerBlink5') sendMidi(uniqueId,0x90,d+1,0x2C,(param1 ? 0x7E : 0x00));
				if(name=='samplerBlink6') sendMidi(uniqueId,0x90,d+1,0x2D,(param1 ? 0x7E : 0x00));
				if(name=='samplerBlink7') sendMidi(uniqueId,0x90,d+1,0x2E,(param1 ? 0x7E : 0x00));
				if(name=='samplerBlink8') sendMidi(uniqueId,0x90,d+1,0x2F,(param1 ? 0x7E : 0x00));
			}
		}
	}
	//-----------------
}
//----------------------------------------------------------------------------- 
// 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+']');
}
//----------------------------------------------------------------------------- 
