//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2016. All Rights Reserved. -----------------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Denon DN-MC4000'
// last revision: 2016-09-14
//Author: Tollen Francisco, 2015-11-10
//some editing and bug fixes by Ionut, 2016-09-14
//-----------------------------------------------------------------------------
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 "Denon DN-MC4000";
}
//-----------------------------------------------------------------------------
// 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 0x8001;
}
//-----------------------------------------------------------------------------
// 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
	return "00020B15E4";
	// F07E??0602004003154E5015????????F7 -- MC3000 (vid 0x154E pid 0x5015)
	// F07E??060200400303006100????????F7 -- MC6000 (vid 0x154E pid 0x5013)
	// F07E7F0602004003154E502131363032F7 -- MC6000 MK2 (vid 0x154E pid 0x5021)
}
//-----------------------------------------------------------------------------
// 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 "DN-MC6000";
	//return "DENON DJ Controller Midi";// Windows 7
	//return false;
}
//-----------------------------------------------------------------------------
function getSupportedMidiDeviceNameForOutput(osId)
{
	return "DN-MC6000";
	//return "DENON DJ Controller Midi";// Windows 7
	//return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// some variables necessary only for this particular script
var jogWheelsInScratchMode			=[0,0,0,0];
var jogWheelsWasDeckPlaying			=[0,0,0,0];
var shiftPressed 					= [0,0,0,0];
var padModePressed 					= [0,0,0,0];
var currentPadMode 					= ['cuepoint','cuepoint','cuepoint','cuepoint'];
var filterValue			 			= [0x7F,0x7F,0x7F,0x7F];
var filterOnOff 					= [0,0,0,0];
//ignore variables are used when pressing a particular button causes the midi controller to issue two identical commands in succession.
var ignore_pitch_command 			= false;
var ignore_load_command 			= false;
var ignore_sample_1_command 		= false;
var ignore_sample_2_command 		= false;
var ignore_sample_3_command 		= false;
var ignore_sample_4_command 		= false;
var ignore_lbToggleSelect_command 	= false;
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 currentFXDW 					= [0,0,0,0];
var fxEnabled 						= [[false,false,false],[false,false,false],[false,false,false]];

var flangerValue					=[127,127,127,127];
var delayValue						=[127,127,127,127];
var echoValue						=[127,127,127,127];

var loopBlink 						= [0,0,0,0];//0=off, 1=blink, 2=on
/************************************************************************/
var cuepoint 	= [[0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00]];
var looppos 	= [[0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00]];
//-----------------------------------------------------------------------------
// 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 MC6000 ['+uniqueId+']');// this outputs a string to DebugView app on Win

	//play

	sendMidi(uniqueId,0x9A,0,0x7F,0x01); //cuepoint color
	sendMidi(uniqueId,0x9B,0,0x7F,0x01); //cuepoint color


	sendMidi(uniqueId,0x90,0,0x00,0x00); //play
	sendMidi(uniqueId,0x91,0,0x00,0x00); //play

	//cue
	sendMidi(uniqueId,0x90,0,0x01,0x00); //cue
	sendMidi(uniqueId,0x90,1,0x01,0x00); //cue
	//shift

	//hotcue1
	sendMidi(uniqueId,0x94,0,0x14,0x00); //cuepoint1
	sendMidi(uniqueId,0x95,0,0x14,0x00); //cuepoint1
	//hotcue2
	sendMidi(uniqueId,0x94,0,0x15,0x00); //cuepoint2
	sendMidi(uniqueId,0x95,0,0x15,0x00); //cuepoint2
	//hotcue3
	sendMidi(uniqueId,0x94,0,0x16,0x00); //cuepoint3
	sendMidi(uniqueId,0x95,0,0x16,0x00); //cuepoint3
	//hotcue4
	sendMidi(uniqueId,0x94,0,0x17,0x00); //cuepoint4
	sendMidi(uniqueId,0x95,0,0x17,0x00); //cuepoint4
	//fx1button
	sendMidi(uniqueId,0x90,8,0x00,0x00);
	sendMidi(uniqueId,0x90,9,0x00,0x00);

	//fx2button
	sendMidi(uniqueId,0x90,8,0x01,0x00);
	sendMidi(uniqueId,0x90,9,0x01,0x00);
	//fx3button
	sendMidi(uniqueId,0x90,8,0x02,0x00);
	sendMidi(uniqueId,0x90,9,0x02,0x00);
	//monitorled1
	sendMidi(uniqueId,0x90,0,0x1B,0x00);//monitor
	sendMidi(uniqueId,0x90,1,0x1B,0x00);//monitor
	//monitorled2
	sendMidi(uniqueId,0x90,0,0x1B,0x00);//monitor
	sendMidi(uniqueId,0x90,1,0x1B,0x00);//monitor
	//keylock
	sendMidi(uniqueId,0x90,0,0x0D,0x00);//keylock
	sendMidi(uniqueId,0x90,1,0x0D,0x00);//keylock
	//sample1
	sendMidi(uniqueId,0x9F,0,0x21,0x7F);
	//sample2
	sendMidi(uniqueId,0x9F,0,0x22,0x7F);
	//sample3
	sendMidi(uniqueId,0x9F,0,0x23,0x7F);
	//sample4
	sendMidi(uniqueId,0x9F,0,0x24,0x7F);
	//uvmeters
	sendMidi(uniqueId,0xB0,15,0x44,0x00);// VU Led
	sendMidi(uniqueId,0xB0,15,0x45,0x00);// VU Led
	//vinyl
	sendMidi(uniqueId,0x90,0,0x07,0x00);//reverse
	sendMidi(uniqueId,0x90,1,0x07,0x00);//reverse
	call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush

}
//-----------------------------------------------------------------------------
// 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)
{

	sendMidi(uniqueId,0x9A,0,0x7F, 0x01); //cuepoint color
	sendMidi(uniqueId,0x9B,0,0x7F, 0x01); //cuepoint color

	//debug('controllerDeinit MC6000 ['+uniqueId+']');
	//play
	sendMidi(uniqueId,0x90,0,0x00,0x00); //play
	sendMidi(uniqueId,0x91,0,0x00,0x00); //play
	//cue
	sendMidi(uniqueId,0x90,0,0x01,0x00); //cue
	sendMidi(uniqueId,0x90,1,0x01,0x00); //cue
	//hotcue1
	sendMidi(uniqueId,0x94,0,0x14,0x00); //cuepoint1
	sendMidi(uniqueId,0x95,0,0x14,0x00); //cuepoint1
	//hotcue2
	sendMidi(uniqueId,0x94,0,0x15,0x00); //cuepoint2
	sendMidi(uniqueId,0x95,0,0x15,0x00); //cuepoint2
	//hotcue3
	sendMidi(uniqueId,0x94,0,0x16,0x00); //cuepoint3
	sendMidi(uniqueId,0x95,0,0x16,0x00); //cuepoint3
	//hotcue4
	sendMidi(uniqueId,0x94,0,0x17,0x00); //cuepoint4
	sendMidi(uniqueId,0x95,0,0x17,0x00); //cuepoint4
	//fx1button
	sendMidi(uniqueId,0x90,8,0x00,0x00);
	sendMidi(uniqueId,0x90,9,0x00,0x00);
	//fx2button
	sendMidi(uniqueId,0x90,8,0x01,0x00);
	sendMidi(uniqueId,0x90,9,0x01,0x00);
	//fx3button
	sendMidi(uniqueId,0x90,8,0x02,0x00);
	sendMidi(uniqueId,0x90,9,0x02,0x00);
	//monitorled1
	sendMidi(uniqueId,0x90,0,0x1B,0x00);//monitor
	sendMidi(uniqueId,0x90,1,0x1B,0x00);//monitor
	//monitorled2
	sendMidi(uniqueId,0x90,0,0x1B,0x00);//monitor
	sendMidi(uniqueId,0x90,1,0x1B,0x00);//monitor
	//keylock
	sendMidi(uniqueId,0x90,0,0x0D,0x00);//keylock
	sendMidi(uniqueId,0x90,1,0x0D,0x00);//keylock
	//sample1
	sendMidi(uniqueId,0x9F,0,0x21,0x7F);
	//sample2
	sendMidi(uniqueId,0x9F,0,0x22,0x7F);
	//sample3
	sendMidi(uniqueId,0x9F,0,0x23,0x7F);
	//sample4
	sendMidi(uniqueId,0x9F,0,0x24,0x7F);
	//uvmeters
	sendMidi(uniqueId,0xB0,15,0x44,0x00);// VU Led
	sendMidi(uniqueId,0xB0,15,0x45,0x00);// VU Led
	//vinyl
	sendMidi(uniqueId,0x90,0,0x07,0x00);//reverse
	sendMidi(uniqueId,0x90,1,0x07,0x00);//reverse
	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)
{
	//debug('messageReceived ['+uniqueId+'] - status:'+status+' channel:'+channel+' data1:'+data1+' data2:'+data2);
	var messageNotProcessed=false;

    //if(channel==2) channel = 1;else if(channel==1) channel = 2; // use this to switch channel; 2 and 3 instead of multiple if's
	switch(status)
	{
	//-----------------------------------------------
	// controllers: sliders, knobs, jog-wheels, encoders ('moving' things)
	case 0xE0: // call('setDeckPitch', channel, data2); break; //pitch sliders decks 1-4
		break;

	case 0xB0:
		switch(data1)
		{
		// global
		case 0x09: call('setDeckPitch', channel, data2); break;//Tollen 10-19-2015
		case 0x00: if(channel == 15){if(data2 >= 1 && data2 <= 0x3F) call('lbMoveCursor', 1);else call('lbMoveCursor', -1);} else call('deckFxFlangerDW', channel-8, data2); break;//Tollen 10-19-2015
		case 0x01: call('deckFxEchoDW', channel-8, data2); break;//Tollen 10-19-2015
		case 0x02: call('deckFxBeatwawDW', channel-8, data2); break;//Tollen 10-19-2015
		case 0x0C: call('setMonitorGain', data2); break; //Tollen 10-05-2016
		case 0x0D: call('setCuePgmMix', data2); break; //Tollen 10-05-2016
		case 0x1A: if(channel == 15){call('setSamplerMasterVolume',data2);} else call('setDeckCutoff', channel, data2); break;//Tollen 10-19-2015
		case 0x16: call('setDeckGain', channel, data2); break;//Tollen 10-19-2015
		case 0x17: call('setDeckEqHigh', channel, data2); break;//Tollen 10-19-2015
		case 0x18: call('setDeckEqMid', channel, data2); break;//Tollen 10-19-2015
		case 0x19: call('setDeckEqLow', channel, data2); break;//Tollen 10-19-2015
		case 0x1C: call('setDeckVolume', channel, data2); break;//Tollen 10-19-2015
		case 0x08: call('setCrossfader', data2); break;//Tollen 10-19-2015

		case 0x06: //Tollen 10-19-2015
		{
			var val1 = 0;//Tollen 10-19-2015
			if(data2 > 0x3F){//back
				val1 = (data2-0x80-0x1F);//Tollen 10-19-2015
			} else {
				val1 = data2+0x1F;//Tollen 10-19-2015
			}

			if (jogWheelsInScratchMode[channel]){
				call('scratchDeckJW', channel, val1, 2000);
			} else {
				call('moveDeckJogWheel', channel, val1, 2000)
			}
		}
		break;//Tollen 10-19-2015

		default: messageNotProcessed = true;
		}
	break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		if(data2==0x40) data2 = 0x7F;

		switch(data1)
		{
		case 0x05: call('deckBackToTop', channel, data2); break;
		case 0x06: call('holdDeckJogWheel', channel, data2); jogWheelsInScratchMode[channel] = data2; break;//10/27/2015
		case 0x14:
			if(padModePressed[channel-4]) {
				currentPadMode[channel-4] = 'cuepoint';
				//change color of padmode button
				sendMidi(uniqueId,(channel-4 == 0 ? 0x9A : 0x9B ),0,0x7F, 0x01); //cuepoint color

				sendMidi(uniqueId,(channel-4 == 0 ? 0x94 : 0x95 ),0,0x14, cuepoint[channel-4][1]); //cuepoint1
				sendMidi(uniqueId,(channel-4 == 0 ? 0x94 : 0x95 ),0,0x15, cuepoint[channel-4][2]); //cuepoint2
				sendMidi(uniqueId,(channel-4 == 0 ? 0x94 : 0x95 ),0,0x16, cuepoint[channel-4][3]); //cuepoint3
				sendMidi(uniqueId,(channel-4 == 0 ? 0x94 : 0x95 ),0,0x17, cuepoint[channel-4][4]); //cuepoint4

			} else {
				switch(currentPadMode[channel-4]){
					case "cuepoint":
						debug((currentPadMode[channel-4]).toString());
						call('deckCuePos1', channel-4, data2);
						break;
					case"savedloops":
						debug((currentPadMode[channel-4]).toString());
						call('deckLoopPoint1', channel-4, data2);
						break;
					case"notdefined1":
						break;
					case"notdefined2":
						break;
				}
			}
			break;//10-16-2015 Tollen
		case 0x15:
			if(padModePressed[channel-4]) {
				currentPadMode[channel-4] = 'savedloops';
				sendMidi(uniqueId,(channel-4 == 0 ? 0x9A : 0x9B ),0,0x7F, 0x02); //cuepoint color
				//send all current saved loops LED states to controller.
				sendMidi(uniqueId,(channel-4 == 0 ? 0x94 : 0x95 ),0,0x14, looppos[channel-4][1]); //looppos1
				sendMidi(uniqueId,(channel-4 == 0 ? 0x94 : 0x95 ),0,0x15, looppos[channel-4][2]); //looppos2
				sendMidi(uniqueId,(channel-4 == 0 ? 0x94 : 0x95 ),0,0x16, looppos[channel-4][3]); //looppos3
				sendMidi(uniqueId,(channel-4 == 0 ? 0x94 : 0x95 ),0,0x17, looppos[channel-4][4]); //looppos3
				debug((looppos).toString());


				//change color of padmode button, and load hotpoint LEDs with same color.
				debug('savedloops pad mode selected!');
			} else {
				switch(currentPadMode[channel-4]){
					case "cuepoint":
						call('deckCuePos2', channel-4, data2);
						break;
					case"savedloops":
						call('deckLoopPoint2', channel-4, data2);
					break;
						case "notdefined1":
					break;
						case "notdefined2":
					break;

				}
			}
			break;//10-16-2015 Tollen
		case 0x16:
			if(padModePressed[channel-4]) {
				currentPadMode[channel-4] = 'notdefined1';
				sendMidi(uniqueId,(channel-4 == 0 ? 0x9A : 0x9B ),0,0x7F, 0x00); //cuepoint color
				//change color of padmode button, and load hotpoint LEDs with same color.
			} else {
				switch(currentPadMode[channel-4]){
					case "cuepoint":
						call('deckCuePos3', channel-4, data2);
						break;
					case"savedloops":
						call('deckLoopPoint3', channel-4, data2);
						break;
					case"notdefined1":
						break;
					case"notdefined2":
						break;
				}
			}
			break;//10-16-2015 Tollen
		case 0x17:
			if(padModePressed[channel-4]) {
				currentPadMode[channel-4] = 'notdefined2';
				sendMidi(uniqueId,(channel-4 == 0 ? 0x9A : 0x9B ),0,0x7F, 0x00); //cuepoint color
				//change color of padmode button, and load hotpoint LEDs with same color.
			} else {
				switch(currentPadMode[channel-4]){
					case "cuepoint":
						call('deckCuePos4', channel-4, data2);
						break;
					case"savedloops":
						call('deckLoopPoint4', channel-4, data2);
						break;
					case"notdefined1":
						break;
					case"notdefined2":
						break;
				}
			}
			break;//10-16-2015 Tollen

		//break;//10-16-2015 Tollen
		case 0x07: call('deckReverse', channel, data2); break; //Vinyl Button Pressed (channel-1) break//10-16-2015 Tollen
		case 0x7F: padModePressed[channel-10] = data2; /*sendMidi(uniqueId,0x90,channel,0x7F,padModePressed[channel-10]);*/ break;
		case 0x0C: call('deckBendDown', channel, data2); break;//10-16-2015 Tollen
		case 0x0B: call('deckBendUp', channel, data2); break;//10-16-2015 Tollen
		case 0x0D: call('deckKeyLock', channel, data2); break;//10-16-2015 Tollen

		case 0x0E:
				if(!ignore_pitch_command){
					var currentPitchRange = getValue('pitchRange');
					currentPitchRange = currentPitchRange + 1; if(currentPitchRange>6) currentPitchRange = 1;
					call('setPitchRange', currentPitchRange);
				}
				ignore_pitch_command = (ignore_pitch_command ? false : true);
			break;
		case 0x00:
			if(channel >= 8) {
				var deck = channel - 8;
				if(data2){//Only concerned about button presses, not button releases.
					if(!fxEnabled[deck]['flanger']){//If Flanger is not currently enabled, enable it.
						fxEnabled[deck]['flanger'] = true; 					//Update the state of Flanger effect to 'on/true'.
						currentFXDW[deck] = 'deckFxFlangerDW'; 				//Set the currently selected effect to Flanger.
						call('deckFxFlangerDW', deck, flangerValue[deck]); 	//Enable the Flanger effect in software.
						sendMidi(uniqueId,0x90,channel,0x00,0x7F);			//Enable FX 2 LED on controller.
					} else {//If Flanger is currently enabled, then disable it.
						fxEnabled[deck]['flanger'] =false; 					//Update the state of the Flanger effect to 'off/false'.
						call('deckFxFlangerDW', deck, 0x00); 				//Disable the Flanger effect in software.
						currentFXDW[deck] = '';								//Unset the currently selected effect.
						sendMidi(uniqueId,0x90,channel,0x00,0x00); 			//Disable FX 2 LED on controller.
					}
				}

			} else {
				call('deckPlayPause', channel, data2);
			}
			break;//FX 1 On //10-16-2015 Tollen
		case 0x01:
			if(channel >= 8) {
				var deck = channel - 8;
				if(data2){ //Only concerned about button presses, not button releases.
					if(!fxEnabled[deck]['echo']){ //If Echo is not currently enabled, enable it.
						fxEnabled[deck]['echo'] = true; 				//Update the state of Echo effect to 'on/true'.
						currentFXDW[deck] 		= 'deckFxEchoDW'; 		//Set the currently selected effect to Echo.
						call('deckFxEchoDW', deck, echoValue[deck]);	//Enable the Echo effect in software.
						sendMidi(uniqueId,0x90,channel,0x01,0x7F); 		//Enable FX 1 LED on controller.
					} else { //If Echo is currently enabled, then disable it.
						fxEnabled[deck]['echo'] = false;				//Update the state of the Echo effect to 'off/false'.
						call('deckFxEchoDW', deck, 0x00);				//Disable the Echo effect in software.
						currentFXDW[deck] 		= '';					//Unset the currently selected effect.
						sendMidi(uniqueId,0x90,channel,0x01,0x00); 		//Disable FX 1 LED on controller.
					}
				}
			} else {
				call('deckCue', channel, data2);
			}
			break;//FX 2 On //10-16-2015 Tollen
		case 0x02:
			if(channel >= 10){
				if(!ignore_load_command){
					call('deckLBLoad', channel-15, data2);
				}
				ignore_load_command = (ignore_load_command ? false : true);
			} else {
				if( channel >= 8){
					var deck = channel - 8;
					if(data2){
						if(!fxEnabled[deck]['beatwaw']){ //If Echo is not currently enabled, enable it.
							fxEnabled[deck]['beatwaw'] = true; 				//Update the state of Echo effect to 'on/true'.
							currentFXDW[deck] 		= 'deckFxBeatwawDW'; 		//Set the currently selected effect to Echo.
							call('deckFxBeatwawDW', deck, echoValue[deck]);	//Enable the Echo effect in software.
							sendMidi(uniqueId,0x90,channel,0x02,0x7F); 		//Enable FX 1 LED on controller.
						} else { //If Echo is currently enabled, then disable it.
							fxEnabled[deck]['beatwaw'] = false;				//Update the state of the Echo effect to 'off/false'.
							call('deckFxBeatwawDW', deck, 0x00);				//Disable the Echo effect in software.
							currentFXDW[deck] 		= '';					//Unset the currently selected effect.
							sendMidi(uniqueId,0x90,channel,0x02,0x00); 		//Disable FX 1 LED on controller.
						}
					}
				} else {
					call('deckSync', channel, data2);
				}
			}
			break;//FX 3 On //10-16-2015 Tollen
		case 0x03:
			if(channel >= 9) {
				if(!ignore_load_command)
					call('deckLBLoad', channel-14, data2);
				ignore_load_command = (ignore_load_command ? false : true);
			} else {
			}
			break;//FX 3 On //10-16-2015 Tollen
		case 0x04:
			if(channel >= 9) {
			} else {
				call('deckBrake', channel, data2);
			}
			break;//TAP //10-16-2015 Tollen


		case 0x32:
			if(channel >= 15) {
				shiftPressed[channel] = data2;
			} else {
				call('deckLoop16', channel-4, data2); }
			break;//10-16-2015 Tollen
		case 0x34:
			call('deckLoopLess', channel-4, data2);
			break;//10-16-2015 Tollen
		case 0x35:
			call('deckLoopMore', channel-4, data2);
			break;//10-16-2015 Tollen

		case 0x1B:
			call('deckMonitor', channel, data2);
			break;//10-16-2015 Tollen

		case 0x1C:
			call('deckCuePos1Clear', channel-4, data2);
			break;//10-27-2015 Tollen
		case 0x1D:
			call('deckCuePos2Clear', channel-4, data2);
			break;//10-27-2015 Tollen
		case 0x1E: call('deckCuePos3Clear', channel-4, data2);
			break;//10-27-2015 Tollen
		case 0x1F:
			if( channel == 15) {
				if(!ignore_lbToggleSelect_command)
					call('lbToggleSelect', data2);
				ignore_lbToggleSelect_command = (ignore_lbToggleSelect_command ? false : true);
			} else {
				call('deckCuePos4Clear', channel-4, data2);
			}
			break;//10-27-2015 Tollen



		case 0x21:
			if(!ignore_sample_1_command)
				call('samplerPlay', 1);
			ignore_sample_1_command = (ignore_sample_1_command ? false : true);
			break;//10-16-2015 Tollen
		case 0x22:
			if(!ignore_sample_2_command)
				call('samplerPlay', 2);
			ignore_sample_2_command = (ignore_sample_2_command ? false : true);
			break;//10-16-2015 Tollen
		case 0x23:
			if(!ignore_sample_3_command)
				call('samplerPlay', 3);
			ignore_sample_3_command = (ignore_sample_3_command ? false : true);
			break;//10-16-2015 Tollen
		case 0x24:
			if(!ignore_sample_4_command)
				call('samplerPlay', 4);
			ignore_sample_4_command = (ignore_sample_4_command ? false : true);
			break;//10-16-2015 Tollen

		case 0x38:
			if(data2>0x3F){
				if(!getValue('looping', channel-4) && loopBlink[channel-5] != 1){
					debug('NOT LOOPING');
					call('deckLoopInEnd', channel-4, data2);	//this will trigger loopInOut. Cant seem to track the complete in/out state
					loopBlink[channel-4]=1; //loop in; no loop out; not looping;

				}
				 debug('Loop In/End');
			}

			break;
		case 0x39:
			if(data2>0x3F){
				if(!getValue('looping', channel-4) && loopBlink[channel-4] == 1){
					debug('NOT LOOPING');
					call('deckLoopInEnd', channel-4, data2);
					loopBlink[channel-4] = 2; //no loop in; looping;

				}
				debug('LOOPOUT PRESSED');
			}


			break;//loop out or exit
		case 0x33:
			if(data2>0x3F){
				if(getValue('looping', channel-4))
				{
				debug('LOOPING');
					call('deckLoopExit', channel-4, 0x7F);
					call('deckLoopExit', channel-4, 0x00);
					loopBlink[channel-4] = 0; //no loop in; not looping;


				}else /*if(loopBlink[deck] = 0)*/
				{
					call('deckLoopReloop', channel-4, 0x7F);
					call('deckLoopReloop', channel-4, 0x00);
					if(getValue('looping', channel-4)){
						debug('NOT/LOOPING');
						loopBlink[channel-4] = 2; //no loop in; looping;

					}

				}
			}
			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);
	var nameLen = name.length;

	if(name=='play') 		sendMidi(uniqueId,(deck == 0 ? 0x90 : 0x91 ),0,0x00,(param1 ? 0x7F : 0x00)); //play
	if(name=='cue') 		sendMidi(uniqueId,(deck == 0 ? 0x90 : 0x91 ),0,0x01,(param1 ? 0x7F : 0x00)); //cue
	if(name=='onbeat') 		sendMidi(uniqueId,(deck == 0 ? 0x90 : 0x91 ),0,0x02,(param1 ? 0x7F : 0x00)); //onbeat


	if(name=='cuepos1'){
		if(cuepoint[deck][1] != (param1 ? 0x01 : 0x00)){
		//change made. send midi command
			if(currentPadMode[deck] == 'cuepoint')
				sendMidi(uniqueId,(deck == 0 ? 0x94 : 0x95 ),0,0x14,(param1 ? 0x01 : 0x00)); //cuepoint1
			cuepoint[deck][1] = (param1 ? 0x01 : 0x00);//save for later
		}
	}
	if(name=='cuepos2'){
		if(cuepoint[deck][2] != (param1 ? 0x01 : 0x00)){
			if(currentPadMode[deck] == 'cuepoint')
				sendMidi(uniqueId,(deck == 0 ? 0x94 : 0x95 ),0,0x15,(param1 ? 0x01 : 0x00)); //cuepoint2
			cuepoint[deck][2] = (param1 ? 0x01 : 0x00);
		}
	}
	if(name=='cuepos3'){
		if(cuepoint[deck][3] != (param1 ? 0x01 : 0x00)){
			if(currentPadMode[deck] == 'cuepoint')
				sendMidi(uniqueId,(deck == 0 ? 0x94 : 0x95 ),0,0x16,(param1 ? 0x01 : 0x00)); //cuepoint3
			cuepoint[deck][3] = (param1 ? 0x01 : 0x00);
		}
	}


	if(name=='cuepos4'){
		if(cuepoint[deck][4] != (param1 ? 0x01 : 0x00)){
			if(currentPadMode[deck] == 'cuepoint')
				sendMidi(uniqueId,(deck == 0 ? 0x94 : 0x95 ),0,0x17,(param1 ? 0x01 : 0x00)); //cuepoint4
			cuepoint[deck][4] = (param1 ? 0x01 : 0x00);
		}
	}
	/*************************************************/
	if(name=='looppos1'){
		debug((param1).toString());
		if(looppos[deck][1] != (param1 != -1 ? 0x02 : 0x00)){
			if(currentPadMode[deck] == 'savedloops')
				sendMidi(uniqueId,(deck == 0 ? 0x94 : 0x95 ),0,0x14,(param1 != -1 ? 0x02 : 0x00)); //cuepoint1
			looppos[deck][1] = (param1 != -1 ? 0x02 : 0x00);
		}

	}
	if(name=='looppos2'){
	debug((param1).toString());
		if(looppos[deck][2] != (param1 != -1 ? 0x02 : 0x00)){
			if(currentPadMode[deck] == 'savedloops')
				sendMidi(uniqueId,(deck == 0 ? 0x94 : 0x95 ),0,0x15,(param1 != -1 ? 0x02 : 0x00)); //cuepoint2
			looppos[deck][2] = (param1 != -1 ? 0x02 : 0x00);
		}

	}
	if(name=='looppos3'){
	debug((param1).toString());
		if(looppos[deck][3] != (param1 != -1 ? 0x02 : 0x00)){
			if(currentPadMode[deck] == 'savedloops')
				sendMidi(uniqueId,(deck == 0 ? 0x94 : 0x95 ),0,0x16,(param1 != -1 ? 0x02 : 0x00)); //cuepoint3
			looppos[deck][3] = (param1 != -1 ? 0x02 : 0x00);
		}
	}
	if(name=='looppos4'){
	debug((param1).toString());
		if(looppos[deck][4] != (param1 != -1 ? 0x02 : 0x00)){
			if(currentPadMode[deck] == 'savedloops')
				sendMidi(uniqueId,(deck == 0 ? 0x94 : 0x95 ),0,0x17,(param1 != -1 ? 0x02 : 0x00)); //cuepoint4
			looppos[deck][4] = (param1 != -1 ? 0x02 : 0x00);
		}
	}

	if(name=='keyLock') 	sendMidi(uniqueId,0x90,(deck == 0 ? 0 : 1 ),0x0D,(param1 ? 0x7F : 0x00));//keylock
	if(name=='monitor') 	sendMidi(uniqueId,0x90,(deck == 0 ? 0 : 1 ),0x1B,(param1 ? 0x7F : 0x00));//monitor
	if(name=='reverse')		sendMidi(uniqueId,0x90,(deck == 0 ? 0 : 1 ),0x07,(param1 ? 0x7F : 0x00));//reverse

	if(nameLen==8)
	{
		var name1 = name.substr(0, nameLen-1);
		if(name1=='sampler')
		{
			var samplerIdx = parseInt(name.charAt(nameLen-1))-1;
			if(!param1)
			{
				samplerBlinkLedStatus[samplerIdx] = 0;


			}else{
				if(name=='sampler1') sendMidi(uniqueId,0x9F,0,0x21,0x7F);
				if(name=='sampler2') sendMidi(uniqueId,0x9F,0,0x22,0x7F);
				if(name=='sampler3') sendMidi(uniqueId,0x9F,0,0x23,0x7F);
				if(name=='sampler4') sendMidi(uniqueId,0x9F,0,0x24,0x7F);

				//if(param2>0) samplerBlinkLedStatus[samplerIdx] = 2;else samplerBlinkLedStatus[samplerIdx] = 1;
			}
		}
	}

}
//-----------------------------------------------------------------------------
var vuStatus=[0,0,0,0];// last (previous) value for each deck
var vuCurrent=[-53,-16,-12,-8,-6,-4,-2,-0];// at how many dB to trigger each of the 8 leds
function setVUValueForDeck(uniqueId, deck, dbValue)
{
	/*
	// NOT good (we send 8 midi out signals per deck, we should only send 1 -- see new implementation below)
	var param1=0;
	for(var i=0; i<8; i++)
	{
		if(dbValue>=vuCurrent[i]) param1 = 1;else param1 = 0;

		if(param1 != vuStatus[deck][i])
		{
			vuStatus[deck][i] = param1;
			sendMidi(uniqueId,0xB0,15,(param1 ? 0x45: 0x44),i*18);// VU Led
		}
	}
	*/
	var howManyLedsToPowerOn=0;// according to the docs there are 8 leds in total
	for(var i=0; i<8; i++)
	{
		// if the current audio dB value is greater than the current led's value it means we need to power it on
		// eg. if we receive -14dB, that is greater than both the 1st and the 2nd leds' values (-53 and -16 in our case; see 'vuCurrent' above)
		if(dbValue >= vuCurrent[i]) howManyLedsToPowerOn = i+1;
	}
	// OK, now we know how many leds need to be on
	// We send a midi out signal ONLY if it's different than what we sent previously.
	// If we don't test for this, we will unnecessary send signals and we may also
	// choke the controller since it doesn't like receiving too much MIDI data per second
	if(howManyLedsToPowerOn != vuStatus[deck][i])
	{
		// we convert between 0..8 (no led to all leds) to 0.0 .. 1.0 (float) and then to 0x00 .. 0x7F
		var normalizedLedsValue = Math.round((0x7F * howManyLedsToPowerOn) / 8.0);
		// power as many leds as needed - the MC4000 needs only one value and it will control all 8 leds for us (see User Manual - MIDI Commands section)
		sendMidi(uniqueId, 0xB0, 15, ((deck==0) ? 0x44 : 0x45), normalizedLedsValue);// VU Led (0x44 is left, 0x45 is right)
		// save the value
		vuStatus[deck] = howManyLedsToPowerOn;
	}
}
//-----------------------------------------------------------------------------
// 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((looppos).toString());
	//debug('timerTick ['+uniqueId+']');
	for(var deck=0; deck<2; deck++)
	{
		var dbValue = getValue('vumeterDeck', deck);// float dB
		setVUValueForDeck(uniqueId, deck, dbValue);
	}

	// TODO: ATTN: the following doesn't execute since it is unstable for some reason (most likely the old VU-meters implementation, which would in turn mean that it is OK after all)
	// in any case, we should NOT send constantly MIDI OUT signals to the controller since it will most likely choke on them (it doesn't like to many per second)
	// this whole (next) part should be refactored !
	//return;

	for(var i = 0; i<2; i++){
		if(currentPadMode[i] == 'savedloops'){
			//looppos[i][1] = (getValue('looppos1',i) > -1 ? 2 : 0);
			//looppos[i][2] = (getValue('looppos2',i) > -1 ? 2 : 0);
			//looppos[i][3] = (getValue('looppos3',i) > -1 ? 2 : 0);
			//looppos[i][4] = (getValue('looppos4',i) > -1 ? 2 : 0);
			/*
			if (getValue('looppos1',i) > -1){
				//loop position 1 is on
				looppos[i][1] = true;
				sendMidi(uniqueId,0x90,i+4,0x14,2);
			} else{
				looppos[i][1] = false;
				sendMidi(uniqueId,0x90,i+4,0x14,0x00);
			}
			*/

			/*
			if (getValue('looppos2',i) > -1){
				looppos[i][2] = true;
				sendMidi(uniqueId,0x90,i+4,0x15,2);
			} else{
				looppos[i][2] = false;
				sendMidi(uniqueId,0x90,i+4,0x15,0x00);
			}
			*/

			/*
			if (getValue('looppos3',i) > -1){
				sendMidi(uniqueId,0x90,i+4,0x16,2);
			} else{
				sendMidi(uniqueId,0x90,i+4,0x16,0x00);
			}
			*/

			/*
			if (getValue('looppos4',i) > -1){
				sendMidi(uniqueId,0x90,i+4,0x17,2);
			} else{
				sendMidi(uniqueId,0x90,i+4,0x17,0x00);
			}
			*/
		} else if (currentPadMode[i] == 'cuepoint'){
			//cuepoint[i][1] = (getValue('cuepos1',i) > 0 ? 1 : 0);
			//cuepoint[i][2] = (getValue('cuepos2',i) > 0 ? 1 : 0);
			//cuepoint[i][3] = (getValue('cuepos3',i) > 0 ? 1 : 0);
			//cuepoint[i][4] = (getValue('cuepos4',i) > 0 ? 1 : 0);

			/*
			if (getValue('cuepos1',i) > 0){
			//debug(getValue('cuepos1',i).toString());
				sendMidi(uniqueId,0x90,i+4,0x14,0x7F);
			} else{
				sendMidi(uniqueId,0x90,i+4,0x14,0x00);
			}
			*/

			/*
			if (getValue('cuepos2',i) > 0){
				sendMidi(uniqueId,0x90,i+4,0x15,0x7F);
			} else{
				sendMidi(uniqueId,0x90,i+4,0x15,0x00);
			}
			*/

			/*
			if (getValue('cuepos3',i) > 0){
				sendMidi(uniqueId,0x90,i+4,0x16,0x7F);
			} else{
				sendMidi(uniqueId,0x90,i+4,0x16,0x00);
			}
			*/

			/*
			if (getValue('cuepos4',i) > 0){
				sendMidi(uniqueId,0x90,i+4,0x17,0x7F);
			} else{
				sendMidi(uniqueId,0x90,i+4,0x17,0x00);
			}
			*/
		}


		/*
		switch(i){
			case 0:
				if(samplerBlinkLedStatus[i] == 2){
					sendMidi(uniqueId,0x90,7,0x30,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,8,0x30,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,9,0x30,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,10,0x30,(samplerBlinkState[i] ? 0x00:0x7F));
					samplerBlinkState[i] ? samplerBlinkState[i]=0:samplerBlinkState[i]=1;
				} else {
					sendMidi(uniqueId,0x90,0,0x30,0x7F);
				}
			break;
			case 1:
				if(samplerBlinkLedStatus[i] == 2){
					sendMidi(uniqueId,0x90,7,0x31,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,8,0x31,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,9,0x31,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,10,0x31,(samplerBlinkState[i] ? 0x00:0x7F));
					samplerBlinkState[i] ? samplerBlinkState[i]=0:samplerBlinkState[i]=1;
				} else {
					sendMidi(uniqueId,0x90,0,0x31,0x7F);
				}
			break;
			case 2:
				if(samplerBlinkLedStatus[i] == 2){
					sendMidi(uniqueId,0x90,7,0x32,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,8,0x32,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,9,0x32,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,10,0x32,(samplerBlinkState[i] ? 0x00:0x7F));
					samplerBlinkState[i] ? samplerBlinkState[i]=0:samplerBlinkState[i]=1;
				} else {
					sendMidi(uniqueId,0x90,0,0x32,0x7F);
				}
			break;
			case 3:
				if(samplerBlinkLedStatus[i] == 2){
					sendMidi(uniqueId,0x90,7,0x33,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,8,0x33,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,9,0x33,(samplerBlinkState[i] ? 0x00:0x7F));
					sendMidi(uniqueId,0x90,10,0x33,(samplerBlinkState[i] ? 0x00:0x7F));
					samplerBlinkState[i] ? samplerBlinkState[i]=0:samplerBlinkState[i]=1;
				} else {
					sendMidi(uniqueId,0x90,7,0x33,0x7F);
				}
			break;
			case 4:
				if(samplerBlinkLedStatus[i] == 2){
						sendMidi(uniqueId,0x90,7,0x34,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,8,0x34,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,9,0x34,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,10,0x34,(samplerBlinkState[i] ? 0x00:0x7F));
						samplerBlinkState[i] ? samplerBlinkState[i]=0:samplerBlinkState[i]=1;
					} else {
						sendMidi(uniqueId,0x90,7,0x34,0x7F);
					}
				break;
			case 5:
				if(samplerBlinkLedStatus[i] == 2){
						sendMidi(uniqueId,0x90,7,0x35,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,8,0x35,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,9,0x35,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,10,0x35,(samplerBlinkState[i] ? 0x00:0x7F));
						samplerBlinkState[i] ? samplerBlinkState[i]=0:samplerBlinkState[i]=1;
					} else {
						sendMidi(uniqueId,0x90,1,0x35,0x7F);
					}
				break;
			case 6:
				if(samplerBlinkLedStatus[i] == 2){
						sendMidi(uniqueId,0x90,7,0x36,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,8,0x36,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,9,0x36,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,10,0x36,(samplerBlinkState[i] ? 0x00:0x7F));
						samplerBlinkState[i] ? samplerBlinkState[i]=0:samplerBlinkState[i]=1;
					} else {
						sendMidi(uniqueId,0x90,7,0x36,0x7F);
					}
				break;
			case 7:
				if(samplerBlinkLedStatus[i] == 2){
						sendMidi(uniqueId,0x90,7,0x37,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,8,0x37,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,9,0x37,(samplerBlinkState[i] ? 0x00:0x7F));
						sendMidi(uniqueId,0x90,10,0x37,(samplerBlinkState[i] ? 0x00:0x7F));
						samplerBlinkState[i] ? samplerBlinkState[i]=0:samplerBlinkState[i]=1;
				} else {
					sendMidi(uniqueId,0x90,7,0x37,0x7F);
				}
				break;
		}
		*/
	}
}
//-----------------------------------------------------------------------------
