//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2016. All Rights Reserved. -----------------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Denon DN-MC7000'
// last revision: 2016-09-14
//Author: Tollen Francisco, 2017-02-17
//
//----------------------------------------------------------------------------- 
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-MC7000";
}
//----------------------------------------------------------------------------- 
// 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 0x8004;	
}
//----------------------------------------------------------------------------- 
// 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 "060200020B15E4";
	// 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-MC7000";
	//return "DENON DJ Controller Midi";// Windows 7
	//return false;	
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	return "DN-MC7000";
	//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 					= [0,0,0,0];
var loopBlink 						= [0,0,0,0];//0=off, 1=blink, 2=on


var cuepointpads 	= [[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]];
var looppointpads 	= [[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]];
var samplepads 	    = [[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[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 MC7000 ['+uniqueId+']');// this outputs a string to DebugView app on Win
	
	for(var x=0;x<4;x++){
		sendMidi(uniqueId,0x90,x+4,0x14,0x01);
		sendMidi(uniqueId,0x90,x+4,0x15,0x01);
		sendMidi(uniqueId,0x90,x+4,0x16,0x01);
		sendMidi(uniqueId,0x90,x+4,0x17,0x01);
		sendMidi(uniqueId,0x90,x+4,0x18,0x01);
		sendMidi(uniqueId,0x90,x+4,0x19,0x01);
		sendMidi(uniqueId,0x90,x+4,0x1A,0x01);
		sendMidi(uniqueId,0x90,x+4,0x1B,0x01);
		
		sendMidi(uniqueId,0x90,x,0x1B,0x01);//monitor
		sendMidi(uniqueId,0x90,x,0x0D,0x01);//
		sendMidi(uniqueId,0x90,x,0x10,0x01);//
		
		
		sendMidi(uniqueId,0x90,x,0x0C,0x01);//pitch bend down
		sendMidi(uniqueId,0x90,x,0x0B,0x01);//pitch bend up
	}			
	
	
	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)
{
	
	for(var x=0;x<4;x++){
		sendMidi(uniqueId,0x90,x+4,0x14,0x01);
		sendMidi(uniqueId,0x90,x+4,0x15,0x01);
		sendMidi(uniqueId,0x90,x+4,0x16,0x01);
		sendMidi(uniqueId,0x90,x+4,0x17,0x01);
		sendMidi(uniqueId,0x90,x+4,0x18,0x01);
		sendMidi(uniqueId,0x90,x+4,0x19,0x01);
		sendMidi(uniqueId,0x90,x+4,0x1A,0x01);
		sendMidi(uniqueId,0x90,x+4,0x1B,0x01);
		
		sendMidi(uniqueId,0x90,x,0x1B,0x01);//monitor
		sendMidi(uniqueId,0x90,x,0x0D,0x01);//
		sendMidi(uniqueId,0x90,x,0x10,0x01);//
		
		sendMidi(uniqueId,0x90,x,0x0C,0x01);//pitch bend down
		sendMidi(uniqueId,0x90,x,0x0B,0x01);//pitch bend up
		
		
	}			
	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)
		{
		case 0x00:
			if(channel == 8 || channel == 9){//fx 1 param knob
				call('deckFxFlangerDW', channel-8, data2);
			}
			if(channel == 15){ //track/browser knob
				if(data2 >= 1 && data2 <= 0x3F) 
					call('lbMoveCursor', 1);
				else 
					call('lbMoveCursor', -1);
			}
			
		break;
		case 0x01: 
			if(channel == 8 || channel == 9){//fx 1 param knob
				call('deckFxEchoDW', channel-8, data2);
			}
		break;
		case 0x02: 
			if(channel == 8 || channel == 9){//fx 1 param knob
				call('deckFxBeatwawDW', channel-8, data2);
			}
		
		break;
		case 0x03: break;
		case 0x04: break;
		case 0x05: break;
		case 0x06: 
		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, 10);
			}
			sendMidi(uniqueId,0x90,channel,0x06,data2);
		
		break;
		case 0x07: break;
		case 0x08:
			if(channel == 15){
				call('setCrossfader', data2);
			}
		break;
		case 0x09: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3){
				call('setDeckPitch', channel, 0x7F - data2);			
			}		
		break;
		case 0x0A: break;
		case 0x0B: break;
		case 0x0C: break;
		case 0x0D: break;
		case 0x0E: break;
		case 0x0F: break;
		case 0x10: break;
		case 0x11: break;
		case 0x12: break;
		case 0x13: break;
		case 0x14: break;
		case 0x15: break;
		case 0x16: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3){
				call('setDeckGain', channel, data2);			
			}
		break;
		case 0x17: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3){
				call('setDeckEqHigh', channel, data2);			
			}
		break;
		case 0x18: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3){
				call('setDeckEqMid', channel, data2);		
			}
		break;
		case 0x19: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3){
				call('setDeckEqLow', channel, data2);		
			}
		break;
		case 0x1A: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3){
				call('setDeckCutoff', channel, data2);
			}
			if(channel == 15){
				call('setSamplerMasterVolume',data2);
			}
			
		break;
		case 0x1B: break;
		case 0x1C: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3){
				call('setDeckVolume', channel, data2);
			}
		break;
		case 0x1D: break;
		case 0x1E: break;
		case 0x1F: break;
		case 0x20: break;
		case 0x21: break;
		case 0x22: break;
		case 0x23: break;
		case 0x24: break;
		case 0x25: break;
		case 0x26: break;
		case 0x27: break;
		case 0x28: break;
		case 0x29: break;
		case 0x2A: break;
		case 0x2B: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3){
				call('setDeckPos', channel, data2);
			}
		break;
		case 0x2C: break;
		case 0x2D: break;
		case 0x2E: break;
		case 0x2F: break;
		case 0x30: break;
		case 0x31: break;
		case 0x32: break;
		case 0x33: break;
		case 0x34: break;
		case 0x35: break;
		case 0x36: break;
		case 0x37: break;
		case 0x38: break;
		case 0x39: break;
		case 0x3A: break;
		case 0x3B: break;
		case 0x3C: break;
		case 0x3D: break;
		case 0x3E: break;
		case 0x3F: break;
		
		
		default: messageNotProcessed = true;
		}
	break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		//if(data2==0x40) data2 = 0x7F;
		
		switch(data1)
		{
		case 0x00: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				call('deckPlayPause', channel, data2);
			}
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				currentPadMode[channel-4] = 0;//cuepoint
				var cuePointLoaded = 0;
				//callSendAllNotifications(uniqueId, true);
				debug('cuepoint');
				cuePointLoaded = getValue('cuepos1', channel-4);				
				//debug('cuepoint loaded?: ' + (sampleLoaded).toString());
				sendMidi(uniqueId,0x90,channel,0x14,(cuePointLoaded ? 0x3F : 0x01));
				//sendMidi(uniqueId,0x90,0x04,0x15,0x3F);
				
				cuePointLoaded = getValue('cuepos2', channel-4);
				//debug('cuepoint loaded?: ' + (sampleLoaded).toString());				
				sendMidi(uniqueId,0x90,channel,0x15,(cuePointLoaded ? 0x3F : 0x01));
				
				cuePointLoaded = getValue('cuepos3', channel-4);
				sendMidi(uniqueId,0x90,channel,0x16,(cuePointLoaded ? 0x3F : 0x01));
				//debug('cuepoint loaded?: ' + (sampleLoaded).toString());
				//sendMidi(uniqueId,0x90,channel,0x16,0x00);				
				
				cuePointLoaded = getValue('cuepos4', channel-4);
				sendMidi(uniqueId,0x90,channel,0x17,(cuePointLoaded ? 0x3F : 0x01));
				//debug('cuepoint loaded?: ' + (sampleLoaded).toString());
				//sendMidi(uniqueId,0x90,channel,0x17,0x00);
				
				cuePointLoaded = getValue('cuepos5', channel-4);
				sendMidi(uniqueId,0x90,channel,0x18,(cuePointLoaded ? 0x3F : 0x01));
				//sendMidi(uniqueId,0x90,channel,0x18,0x00);
				
				cuePointLoaded = getValue('cuepos6', channel-4);
				sendMidi(uniqueId,0x90,channel,0x19,(cuePointLoaded ? 0x3F : 0x01));
				//sendMidi(uniqueId,0x90,channel,0x19,0x00);
				
				cuePointLoaded = getValue('cuepos7', channel-4);
				sendMidi(uniqueId,0x90,channel,0x1A,(cuePointLoaded ? 0x3F : 0x01));
				//sendMidi(uniqueId,0x90,channel,0x1A,0x00);
				
				cuePointLoaded = getValue('cuepos8', channel-4);
				sendMidi(uniqueId,0x90,channel,0x1B,(cuePointLoaded ? 0x3F : 0x01));
				//sendMidi(uniqueId,0x90,channel,0x1B,0x00);
			}
		break;
		case 0x01: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				call('deckCue', channel, data2);
			}
		break;
		case 0x02: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				call('deckSync', channel, data2);
			}
			if(channel == 15){
				if(data2)
					call('deckLBLoad', 0, data2);
			}
		break;
		case 0x03: 
			if(channel == 15){
				if(data2)
					call('deckLBLoad', 1, data2);
			}
		break;
		case 0x04: 
			if(channel == 15){
				if(data2)
					call('deckLBLoad', 2, data2);
			}
		break;
		case 0x05: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				call('deckBackToTop', channel, data2);
			}
			if(channel == 15){
				if(data2)
					call('deckLBLoad', 3, data2);
			}
		break;
		case 0x06: 
			call('holdDeckJogWheel', channel, data2); jogWheelsInScratchMode[channel] = data2;
		break;
		case 0x07: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				currentPadMode[channel-4] = 1;//roll
				var savedLoopLoaded = 0;
				//callSendAllNotifications(uniqueId, true);
				debug('roll');
				
				savedLoopLoaded = getValue('looppos1', channel-4);				
				//debug('cuepoint loaded?: ' + (sampleLoaded).toString());
				sendMidi(uniqueId,0x90,channel,0x14,((savedLoopLoaded > -1) ? 0x3F : 0x01));
				//sendMidi(uniqueId,0x90,0x04,0x15,0x3F);
				
				savedLoopLoaded = getValue('looppos2', channel-4);
				//debug('cuepoint loaded?: ' + (sampleLoaded).toString());				
				sendMidi(uniqueId,0x90,channel,0x15,((savedLoopLoaded > -1) ? 0x3F : 0x01));
				
				savedLoopLoaded = getValue('looppos3', channel-4);
				sendMidi(uniqueId,0x90,channel,0x16,((savedLoopLoaded > -1) ? 0x3F : 0x01));
				debug('cuepoint loaded?: ' + (savedLoopLoaded).toString());
				//sendMidi(uniqueId,0x90,channel,0x16,0x00);				
				
				savedLoopLoaded = getValue('looppos4', channel-4);
				sendMidi(uniqueId,0x90,channel,0x17,((savedLoopLoaded > -1) ? 0x3F : 0x01));
				//debug('cuepoint loaded?: ' + (sampleLoaded).toString());
				//sendMidi(uniqueId,0x90,channel,0x17,0x00);
				
				savedLoopLoaded = getValue('looppos5', channel-4);
				sendMidi(uniqueId,0x90,channel,0x18,((savedLoopLoaded > -1) ? 0x3F : 0x01));
				//sendMidi(uniqueId,0x90,channel,0x18,0x00);
				
				sendMidi(uniqueId,0x90,channel,0x19,0x01);
				sendMidi(uniqueId,0x90,channel,0x1A,0x01);
				sendMidi(uniqueId,0x90,channel,0x1B,0x01);
				
			}
		break;
		case 0x08: break;
		case 0x09: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				currentPadMode[channel-4] = 2;//slicer
				debug('slicer');
				sendMidi(uniqueId,0x90,channel,0x14,0x01);
				sendMidi(uniqueId,0x90,channel,0x15,0x01);
				sendMidi(uniqueId,0x90,channel,0x16,0x01);
				sendMidi(uniqueId,0x90,channel,0x17,0x01);
				sendMidi(uniqueId,0x90,channel,0x18,0x01);
				sendMidi(uniqueId,0x90,channel,0x19,0x01);
				sendMidi(uniqueId,0x90,channel,0x1A,0x01);
				sendMidi(uniqueId,0x90,channel,0x1B,0x01);
				
			}
		break;
		case 0x0A: break;
		case 0x0B: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				call('deckBendUp', channel, data2);
				sendMidi(uniqueId,0x90,channel,0x0B,(data2 ? 0x3F : 0x01));
			}
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				var sampleLoaded = 0;
				currentPadMode[channel-4] = 3;//sampler
				debug('sampler');
				//callSendAllNotifications(uniqueId, true);
				
				sampleLoaded = getValue('sampler1loaded', channel-4);
				sendMidi(uniqueId,0x90,channel,0x14,sampleLoaded ? 0x3F : 0x01);
				
				sampleLoaded = getValue('sampler2loaded', channel-4);				
				sendMidi(uniqueId,0x90,channel,0x15,sampleLoaded ? 0x3F : 0x01);				
				
				sampleLoaded = getValue('sampler3loaded', channel-4);
				sendMidi(uniqueId,0x90,channel,0x16,sampleLoaded ? 0x3F : 0x01);				
				
				sampleLoaded = getValue('sampler4loaded', channel-4);
				sendMidi(uniqueId,0x90,channel,0x17,sampleLoaded ? 0x3F : 0x01);
				
				sampleLoaded = getValue('sampler5loaded', channel-4);
				sendMidi(uniqueId,0x90,channel,0x18,sampleLoaded ? 0x3F : 0x01);
				
				sampleLoaded = getValue('sampler6loaded', channel-4);
				sendMidi(uniqueId,0x90,channel,0x19,sampleLoaded ? 0x3F : 0x01);
				
				sampleLoaded = getValue('sampler7loaded', channel-4);
				sendMidi(uniqueId,0x90,channel,0x1A,sampleLoaded ? 0x3F : 0x01);
				
				sampleLoaded = getValue('sampler8loaded', channel-4);
				sendMidi(uniqueId,0x90,channel,0x1B,sampleLoaded ? 0x3F : 0x7F);
				
				
				
			}
		break;
		case 0x0C: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				call('deckBendDown', channel, data2);
				sendMidi(uniqueId,0x90,channel,0x0C,(data2 ? 0x3F : 0x01));
			}
		break;
		case 0x0D: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				call('deckKeyLock', channel, data2);
			}
		break;
		case 0x0E: break;
		case 0x0F: break;
		case 0x10: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				call('deckReverse', channel, data2);
			}
		break;
		case 0x11: break;
		case 0x12: break;
		case 0x13: break;
		case 0x14: 
		//drum pad 1
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(currentPadMode[channel-4]){
					case 0:
						//cuepointpads
						call('deckCuePos1', channel-4, data2);
						break;
					case 1:
						//looppointpads
						call('deckLoopPoint1', channel-4, data2);
						break;
					case 2:					
						break;
					case 3:
						//samplepads
						call('samplerPlay', 1);						
						break;
				}
			}
		break;
		case 0x15: 
		//drum pad 2
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(currentPadMode[channel-4]){
					case 0:
						//cuepointpads
						call('deckCuePos2', channel-4, data2);
						break;
					case 1:
						//looppointpads
						call('deckLoopPoint2', channel-4, data2);
						break;
					case 2:					
						break;
					case 3:
						//samplepads
						call('samplerPlay', 2);
						break;
				}
			}
		break;
		case 0x16: 
		//drum pad 3
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(currentPadMode[channel-4]){
					case 0:
						//cuepointpads
						call('deckCuePos3', channel-4, data2);
						break;
					case 1:
						//looppointpads
						call('deckLoopPoint3', channel-4, data2);
						break;
					case 2:					
						break;
					case 3:
						//samplepads
						call('samplerPlay', 3);
						break;
				}
			}
		break;
		case 0x17: 
		//drum pad 4
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(currentPadMode[channel-4]){
					case 0:
						//cuepointpads
						call('deckCuePos4', channel-4, data2);
						break;
					case 1:
						//looppointpads
						call('deckLoopPoint4', channel-4, data2);
						break;
					case 2:					
						break;
					case 3:
						//samplepads
						call('samplerPlay', 4);
						break;
				}
			}
		break;
		case 0x18: 
		//drum pad 5
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(currentPadMode[channel-4]){
					case 0:
						//cuepointpads
						call('deckCuePos5', channel-4, data2);
						break;
					case 1:
						//looppointpads
						call('deckLoopPoint5', channel-4, data2);
						break;
					case 2:					
						break;
					case 3:
						//samplepads
						call('samplerPlay', 5);
						break;
				}
			}
		break;
		case 0x19: 
		//drum pad 6
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(currentPadMode[channel-4]){
					case 0:
						//cuepointpads
						call('deckCuePos6', channel-4, data2);
						break;
					case 1:
						//looppointpads
						call('deckLoopPoint6', channel-4, data2);
						break;
					case 2:					
						break;
					case 3:
						//samplepads
						call('samplerPlay', 6);
						break;
				}
			}
		break;
		case 0x1A: 
		//drum pad 7
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(currentPadMode[channel-4]){
					case 0:
						//cuepointpads
						call('deckCuePos7', channel-4, data2);
						break;
					case 1:
						//looppointpads
						call('deckLoopPoint7', channel-4, data2);
						break;
					case 2:					
						break;
					case 3:
						//samplepads
						call('samplerPlay', 7);
						break;
				}
			}
		break;
		case 0x1B: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				call('deckMonitor', channel, data2); 
			}
		//drum pad 8
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(currentPadMode[channel-4]){
					case 0:
						//cuepointpads
						call('deckCuePos8', channel-4, data2);
						break;
					case 1:
						//looppointpads
						call('deckLoopPoint8', channel-4, data2);
						break;
					case 2:					
						break;
					case 3:
						//samplepads
						call('samplerPlay', 8);
						break;
				}
			}
			if(channel == 15){
				if(data2)
					call('lbToggleSelect', data2);
				sendMidi(uniqueId,0x90,15,0x0F,(data2 ? 0x3F : 0x01));
			}
		break;
		case 0x1C: break;
		case 0x1D: break;
		case 0x1E: break;
		case 0x1F: break;
		case 0x20: break;
		case 0x21: break;
		case 0x22: break;
		case 0x23: break;
		case 0x24: break;
		case 0x25: break;
		case 0x26: break;
		case 0x27: break;
		case 0x28: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){ 
				call('deckLoopPrev', channel-4, data2); 
				sendMidi(uniqueId,0x90,channel,0x28,(data2 ? 0x3F : 0X01));
			}  
		break;
		case 0x29: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){ 
				call('deckLoopNext', channel-4, data2); 
				sendMidi(uniqueId,0x90,channel,0x29,(data2 ? 0x3F : 0X01));
			}  
		break;
		case 0x2A: break;
		case 0x2B: 
			if(channel == 1 || channel == 2 || channel == 3 || channel == 4){
				var currentPitchRange = getValue('pitchRange');
				currentPitchRange = currentPitchRange + 1; if(currentPitchRange>6) currentPitchRange = 1;
				call('setPitchRange', currentPitchRange);
			}
		break;
		case 0x2C: 
			if(channel == 1 || channel == 2 || channel == 3 || channel == 4){
				var currentPitchRange = getValue('pitchRange');
				currentPitchRange = currentPitchRange - 1; if(currentPitchRange<1) currentPitchRange = 6;
				call('setPitchRange', currentPitchRange);
			}
		break;
		case 0x2D: break;
		case 0x2E: break;
		case 0x2F: break;
		case 0x30: break;
		case 0x31: break;
		case 0x32: 
			if(channel == 0 || channel == 1 || channel == 2 || channel == 3) { 
				shiftPressed[channel] = data2; 
			} 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){ 
				call('deckLoop16', channel-4, data2); 
			}  
		break;
		case 0x33: break;
		case 0x34: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){ 
				call('deckLoopLess', channel-4, data2);
			} 
		break;
		case 0x35: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){ 
				call('deckLoopMore', channel-4, data2);
			}
		break;
		case 0x36: break;
		case 0x37: break;
		case 0x38: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){ 
				if(data2)
				if(!getValue('looping', channel-4) && loopBlink[channel-4] != 1){ 
					call('deckLoopInEnd', channel-4, data2);
					loopBlink[channel-4]=1;
				}
			}
		
		break;
		case 0x39: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){ 
				if(data2)
				if(!getValue('looping', channel-4) && loopBlink[channel-4] == 1){
					call('deckLoopInEnd', channel-4, data2);
					loopBlink[channel-4]=1;
				} else {
					call('deckLoopExit', channel-4, 0x7F);
					call('deckLoopExit', channel-4, 0x00);
					loopBlink[channel-4] = 0;
				}
			}
		break;
		case 0x3A: break;
		case 0x3B: break;
		case 0x3C: break;
		case 0x3D: break;
		case 0x3E: break;
		case 0x3F: break;
		case 0x77: 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,0x90,deck,0x00,(param1 ? 0x7F : 0X01)); //play
	if(name=='cue') 		sendMidi(uniqueId,0x90,deck,0x01,(param1 ? 0x7F : 0X01)); //cue
	if(name=='onbeat') 		sendMidi(uniqueId,0x90,deck,0x02,(param1 ? 0x7F : 0X01)); //play
	//debug((currentPadMode[deck]).toString());
	
	if(currentPadMode[deck] == 0){
		if(name=='cuepos1'){ 	
		debug('enter notif cue pos 1');
			sendMidi(uniqueId,0x90,deck+4,0x14,(param1 ? 0x3F : 0x01));
		}
		if(name=='cuepos2'){ 	
			sendMidi(uniqueId,0x90,deck+4,0x15,(param1 ? 0x3F : 0x01));
		}
		if(name=='cuepos3'){ 	
			sendMidi(uniqueId,0x90,deck+4,0x16,(param1 ? 0x3F : 0x01));
		}
		
		if(name=='cuepos4'){
			sendMidi(uniqueId,0x90,deck+4,0x17,(param1 ? 0x3F : 0x01));
		}
		
		if(name=='cuepos5'){
			sendMidi(uniqueId,0x90,deck+4,0x18,(param1 ? 0x3F : 0x01));
		}
		
		if(name=='cuepos6'){
			sendMidi(uniqueId,0x90,deck+4,0x19,(param1 ? 0x3F : 0x01));
		}
		
		if(name=='cuepos7'){
			sendMidi(uniqueId,0x90,deck+4,0x1A,(param1 ? 0x3F : 0x01));
		}
		
		if(name=='cuepos8'){
			sendMidi(uniqueId,0x90,deck+4,0x1B,(param1 ? 0x3F : 0x01));
		}
	}
	
	/*************************************************/
	if(currentPadMode[deck] == 1){
		if(name=='looppos1'){
			sendMidi(uniqueId,0x90,deck+4,0x14,(param1 ? 0x3F : 0x01));
		}
		if(name=='looppos2'){
			sendMidi(uniqueId,0x90,deck+4,0x15,(param1 ? 0x3F : 0x01));
		}
		if(name=='looppos3'){	
			sendMidi(uniqueId,0x90,deck+4,0x16,(param1 ? 0x3F : 0x01));
		}
		if(name=='looppos4'){	
			sendMidi(uniqueId,0x90,deck+4,0x17,(param1 ? 0x3F : 0x01));
		}
		if(name=='looppos5'){	
			sendMidi(uniqueId,0x90,deck+4,0x18,(param1 ? 0x3F : 0x01));
		}
	}
	
	if(name=='keyLock') 	sendMidi(uniqueId,0x90,deck,0x0D,(param1 ? 0x3F : 0x01));//keylock
	if(name=='monitor') 	sendMidi(uniqueId,0x90,deck,0x1B,(param1 ? 0x3F : 0x01));//monitor
	if(name=='reverse')		sendMidi(uniqueId,0x90,deck,0x10,(param1 ? 0x3F : 0x01));//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(currentPadMode[0] == 3){
					if(name=='sampler1') sendMidi(uniqueId,0x90,4,0x14,(param1 ? 0x3F : 0x01));
					if(name=='sampler2') sendMidi(uniqueId,0x90,4,0x15,(param1 ? 0x3F : 0x01));
					if(name=='sampler3') sendMidi(uniqueId,0x90,4,0x16,(param1 ? 0x3F : 0x01));
					if(name=='sampler4') sendMidi(uniqueId,0x90,4,0x17,(param1 ? 0x3F : 0x01));
					if(name=='sampler5') sendMidi(uniqueId,0x90,4,0x18,(param1 ? 0x3F : 0x01));
					if(name=='sampler6') sendMidi(uniqueId,0x90,4,0x19,(param1 ? 0x3F : 0x01));
					if(name=='sampler7') sendMidi(uniqueId,0x90,4,0x1A,(param1 ? 0x3F : 0x01));
					if(name=='sampler8') sendMidi(uniqueId,0x90,4,0x1B,(param1 ? 0x3F : 0x01));
				}
				
				if(currentPadMode[1] == 3){
					if(name=='sampler1') sendMidi(uniqueId,0x90,5,0x14,(param1 ? 0x3F : 0x01));
					if(name=='sampler2') sendMidi(uniqueId,0x90,5,0x15,(param1 ? 0x3F : 0x01));
					if(name=='sampler3') sendMidi(uniqueId,0x90,5,0x16,(param1 ? 0x3F : 0x01));
					if(name=='sampler4') sendMidi(uniqueId,0x90,5,0x17,(param1 ? 0x3F : 0x01));
					if(name=='sampler5') sendMidi(uniqueId,0x90,5,0x18,(param1 ? 0x3F : 0x01));
					if(name=='sampler6') sendMidi(uniqueId,0x90,5,0x19,(param1 ? 0x3F : 0x01));
					if(name=='sampler7') sendMidi(uniqueId,0x90,5,0x1A,(param1 ? 0x3F : 0x01));
					if(name=='sampler8') sendMidi(uniqueId,0x90,5,0x1B,(param1 ? 0x3F : 0x01));
				}
				
				if(currentPadMode[2] == 3){
					if(name=='sampler1') sendMidi(uniqueId,0x90,6,0x14,(param1 ? 0x3F : 0x01));
					if(name=='sampler2') sendMidi(uniqueId,0x90,6,0x15,(param1 ? 0x3F : 0x01));
					if(name=='sampler3') sendMidi(uniqueId,0x90,6,0x16,(param1 ? 0x3F : 0x01));
					if(name=='sampler4') sendMidi(uniqueId,0x90,6,0x17,(param1 ? 0x3F : 0x01));
					if(name=='sampler5') sendMidi(uniqueId,0x90,6,0x18,(param1 ? 0x3F : 0x01));
					if(name=='sampler6') sendMidi(uniqueId,0x90,6,0x19,(param1 ? 0x3F : 0x01));
					if(name=='sampler7') sendMidi(uniqueId,0x90,6,0x1A,(param1 ? 0x3F : 0x01));
					if(name=='sampler8') sendMidi(uniqueId,0x90,6,0x1B,(param1 ? 0x3F : 0x01));
				}
				
				if(currentPadMode[3] == 3){
					if(name=='sampler1') sendMidi(uniqueId,0x90,7,0x14,(param1 ? 0x3F : 0x01));
					if(name=='sampler2') sendMidi(uniqueId,0x90,7,0x15,(param1 ? 0x3F : 0x01));
					if(name=='sampler3') sendMidi(uniqueId,0x90,7,0x16,(param1 ? 0x3F : 0x01));
					if(name=='sampler4') sendMidi(uniqueId,0x90,7,0x17,(param1 ? 0x3F : 0x01));
					if(name=='sampler5') sendMidi(uniqueId,0x90,7,0x18,(param1 ? 0x3F : 0x01));
					if(name=='sampler6') sendMidi(uniqueId,0x90,7,0x19,(param1 ? 0x3F : 0x01));
					if(name=='sampler7') sendMidi(uniqueId,0x90,7,0x1A,(param1 ? 0x3F : 0x01));
					if(name=='sampler8') sendMidi(uniqueId,0x90,7,0x1B,(param1 ? 0x3F : 0x01));
				}
				
				
				//if(param2>0) samplerBlinkLedStatus[samplerIdx] = 2;else samplerBlinkLedStatus[samplerIdx] = 1;
			}
		}
	}				

}

var vuStatus=[0,0,0,0];// last (previous) value for each deck
var vuCurrent=[-44,-36,-28,-20,-16,-12,-8,-4,-2,-1];// at how many dB to trigger each of the 8 leds
function setVUValueForDeck(uniqueId, deck, dbValue)
{	
	
	var howManyLedsToPowerOn=0;// according to the docs there are 8 leds in total
	for(var i=0; i<10; 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)
		sendMidi(uniqueId, 0xB0, deck, 0x1F, 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;
}

//----------------------------------------------------------------------------- 
