//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2016. All Rights Reserved. -----------------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Numark NS6II'
// last revision: 2016-09-14
//Author: Tollen Francisco, 2017-10-16
//
//----------------------------------------------------------------------------- 
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 "";
}
//----------------------------------------------------------------------------- 
// 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 0x0051;	
}
//----------------------------------------------------------------------------- 
// 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 "4D001900010006000000000000000000000000000000000000000000F7 "; //no sysex id returned from miditrace
	
}
//----------------------------------------------------------------------------- 
// 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 "Numark NS6 II";
	
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	return "Numark NS6 II";
	
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var loopBlink 						= [0,0,0,0];//0=off, 1=blink, 2=on
var jogWheelsInScratchMode		    =[0,0,0,0];
var displayRemaining                =[0,0,0,0];
var echoEffect                      = [0,0];
var flangerEffect                   = [0,0];
var reverbEffect                    = [0,0];
var padMode 						= ['cues','cues','cues','cues'];
//----------------------------------------------------------------------------- 
// 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 Numark NS6 II ['+uniqueId+']');// this outputs a string to DebugView app on Win
	callSendAllNotifications(uniqueId, true);// send all notifications to the controller (so that we don't have to manually init all leds/lcd screens)
	
	var sysex=[0xF0, 0x00, 0x01, 0x3F, 0x7F, 0x3A, 0x60, 0x00, 0x04, 0x04, 0x01, 0xF7]; 
	sendMidiSysEx(uniqueId, sysex, sysex.length);
	for(var d = 0; d < 4; d++){
		sendMidi(uniqueId,0x90,d + 4,0x00,0x7F);
		sendMidi(uniqueId,0x90,d + 4,0x10,0x00);
		sendMidi(uniqueId,0x90,d + 4,0x0E,0x00);
		sendMidi(uniqueId,0x90,d + 4,0x0B,0x00);
		sendMidi(uniqueId,0x90,d + 4,0x09,0x00);
		
		
	}
	sendMidi(uniqueId,0x90,0,0x14,0x00);
	sendMidi(uniqueId,0x90,0,0x15,0x00);
	sendMidi(uniqueId,0x90,0,0x16,0x00);
	sendMidi(uniqueId,0x90,0,0x17,0x00);
	sendMidi(uniqueId,0x90,0,0x18,0x00);
	sendMidi(uniqueId,0x90,0,0x19,0x00);
	sendMidi(uniqueId,0x90,0,0x1A,0x00);
	sendMidi(uniqueId,0x90,0,0x1B,0x00);
	
	sendMidi(uniqueId,0x90,1,0x14,0x00);
	sendMidi(uniqueId,0x90,1,0x15,0x00);
	sendMidi(uniqueId,0x90,1,0x16,0x00);
	sendMidi(uniqueId,0x90,1,0x17,0x00);
	sendMidi(uniqueId,0x90,1,0x18,0x00);
	sendMidi(uniqueId,0x90,1,0x19,0x00);
	sendMidi(uniqueId,0x90,1,0x1A,0x00);
	sendMidi(uniqueId,0x90,1,0x1B,0x00);
	
	
	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)
{
	//callSendAllNotifications(uniqueId, false);
	var sysex=[0xF0, 0x00, 0x20, 0x7F, 0x02, 0xF7]; 
	sendMidiSysEx(uniqueId, sysex, sysex.length);
	callSendAllNotifications(uniqueId, false);// send all notifications to the controller (so that we don't have to manually init all leds/lcd screens)
	
	var sysex=[0xF0, 0x00, 0x01, 0x3F, 0x7F, 0x3A, 0x60, 0x00, 0x04, 0x04, 0x01, 0xF7]; 
	sendMidiSysEx(uniqueId, sysex, sysex.length);
	for(var d = 0; d < 4; d++){
		sendMidi(uniqueId,0x90,d + 4,0x00,0x00);
		sendMidi(uniqueId,0x90,d + 4,0x10,0x00);
		sendMidi(uniqueId,0x90,d + 4,0x0E,0x00);
		sendMidi(uniqueId,0x90,d + 4,0x0B,0x00);
		sendMidi(uniqueId,0x90,d + 4,0x09,0x00);
		
		
	}
	
	sendMidi(uniqueId,0x90,0,0x14,0x00);
	sendMidi(uniqueId,0x90,0,0x15,0x00);
	sendMidi(uniqueId,0x90,0,0x16,0x00);
	sendMidi(uniqueId,0x90,0,0x17,0x00);
	sendMidi(uniqueId,0x90,0,0x18,0x00);
	sendMidi(uniqueId,0x90,0,0x19,0x00);
	sendMidi(uniqueId,0x90,0,0x1A,0x00);
	sendMidi(uniqueId,0x90,0,0x1B,0x00);
	
	sendMidi(uniqueId,0x90,1,0x14,0x00);
	sendMidi(uniqueId,0x90,1,0x15,0x00);
	sendMidi(uniqueId,0x90,1,0x16,0x00);
	sendMidi(uniqueId,0x90,1,0x17,0x00);
	sendMidi(uniqueId,0x90,1,0x18,0x00);
	sendMidi(uniqueId,0x90,1,0x19,0x00);
	sendMidi(uniqueId,0x90,1,0x1A,0x00);
	sendMidi(uniqueId,0x90,1,0x1B,0x00);
	
	
	
	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 == 15){
				if(data2 >= 1 && data2 <= 0x3F) 
					call('lbMoveCursor', 1);
				else 
					call('lbMoveCursor', -1);
			
			}
			if(channel == 8|| channel == 9){
				call('deckFxEchoDW', channel-8, data2);	
			}
			
			//debug('messageReceived pad mode button: ['+uniqueId+'] - status:'+status+' channel:'+channel+' data1:'+data1+' data2:'+data2);
		break;
		case 0x01: 
		if(channel == 8|| channel == 9){
				call('deckFxFlangerDW', channel-8, data2);
			}
		break;
		case 0x02: 
			if(channel == 8|| channel == 9){
				call('deckFxReverbDW', channel-8, data2);
			}
			
			
			
		break;
		case 0x03: break;
		case 0x04: break;
		case 0x05: break;
		case 0x06: 
			var val1 = (data2>0x40 ? data2-0x80 : data2);				
			if(jogWheelsInScratchMode[channel]){					
				call('scratchDeckJW', channel, val1, 600);
				
			} else {						
				if(shiftMode[channel])					
					call('searchDeckJW', channel, val1, 600);
				else
					call('moveDeckJogWheel', channel, val1/2, 600);					
			}
		break;
		case 0x07: break;
		case 0x08:
			call('setCrossfader', data2);
		break;
		case 0x09: 
			call('setDeckPitch', channel, 0x7F-data2);
			
		break;
		case 0x0A:
			call('setMasterGain', data2);
		break;
		case 0x0B: 
			
		break;
		case 0x0C: 
			call('setMonitorGain', data2);
		break;
		case 0x0D: 
			call('setCuePgmMix', data2);
		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: 
			call('setDeckGain', channel, data2)
		break;
		case 0x17: 
			call('setDeckEqHigh', channel, data2);
		break;
		case 0x18: 
			call('setDeckEqMid', channel, data2);
		break;
		case 0x19: 
			
			call('setDeckEqLow', channel, data2);
		break;
		case 0x1A: 
			call('setDeckCutoff', channel, data2);			
		break;
		case 0x1B:
			
		break;
		case 0x1C: 
			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: 
			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 < 4){
				call('deckPlayPause', channel, data2);
			}
			if(channel >= 4 && channel <= 7 && data2){ //pad mode cues
				
					var led = false;
					switch(padMode[channel - 4]){
						case 'cues':
							led = 0x00;
							break;
						case 'auto':
							led = 0x10;
							break;
						case 'loop'://do nothing
							led = 0x0E;
							break;
						case 'sampler':
							led = 0x0B;
							break;
						case 'slicer':
							led = 0x09;
							break;
					}
					//disable the rest of the pad mode leds
					sendMidi(uniqueId,0x90,channel,led, 0x00);
					//light cues led LED
					if(data2) sendMidi(uniqueId,0x90,channel,data1, 0x7F);
					
					sendMidi(uniqueId,0x90,channel,0x14,0x00);
					sendMidi(uniqueId,0x90,channel,0x15,0x00);
					sendMidi(uniqueId,0x90,channel,0x16,0x00);
					sendMidi(uniqueId,0x90,channel,0x17,0x00);
					sendMidi(uniqueId,0x90,channel,0x18,0x00);
					sendMidi(uniqueId,0x90,channel,0x19,0x00);
					sendMidi(uniqueId,0x90,channel,0x1A,0x00);
					sendMidi(uniqueId,0x90,channel,0x1B,0x00);
					
					//now, check for cue points and light the ones we need.
					
					if(getValue('cuepos1', channel - 4)){
						
						sendMidi(uniqueId,0x90,channel,0x14,0x3C);
					}
					if(getValue('cuepos2', channel - 4)){
						
						sendMidi(uniqueId,0x90,channel,0x15,0x3C);
					}
					if(getValue('cuepos3', channel - 4)){
						
						sendMidi(uniqueId,0x90,channel,0x16,0x3C);
					}
					if(getValue('cuepos4', channel - 4)){
						
						sendMidi(uniqueId,0x90,channel,0x17,0x3C);
					}
					if(getValue('cuepos5', channel - 4)){
					
						sendMidi(uniqueId,0x90,channel,0x18,0x3C);
					}
					if(getValue('cuepos6', channel - 4)){
						
						sendMidi(uniqueId,0x90,channel,0x19,0x3C);
					}
					if(getValue('cuepos7', channel - 4)){
						
						sendMidi(uniqueId,0x90,channel,0x1A,0x3C);
					}
					if(getValue('cuepos8', channel - 4)){
						
						sendMidi(uniqueId,0x90,channel,0x1B,0x3C);
					}
					//store selected pad mode to variable, per deck.
					padMode[channel - 4] = 'cues';						
				
				
			}
			if(channel > 7) {
				//fx1
				if(data2){
					echoEffect[channel-8] = (echoEffect[channel-8] ? 0 : 1);
					sendMidi(uniqueId,0x90,channel,data1,(echoEffect[channel-8] ? 0x7F : 0x00));
					if(!echoEffect[channel-8])
						call('deckFxEchoDW', channel-8, 0x00);
				}
				
			}
			
			
		break;
		case 0x01: 
			if(channel < 4){
				call('deckCue', channel, data2);
			} else {
				//fx2
				if(data2){
					flangerEffect[channel-8] = (flangerEffect[channel-8] ? 0 : 1);
					sendMidi(uniqueId,0x90,channel,data1,(flangerEffect[channel-8] ? 0x7F : 0x00));
					if(!flangerEffect[channel-8])
						call('deckFxFlangerDW', channel-8, 0x00);
				}
				
			}
			
		break;
		case 0x02: 
			if(channel < 4){
				call('deckSync', channel, data2);
			
			} else {
				
				if(data2){
					call('deckLBLoad', 0, data2);
				}
			
				
				//fx3
				/*
				if(data2){
					reverbEffect[channel-8] = (reverbEffect[channel-8] ? 0 : 1);
					sendMidi(uniqueId,0x90,channel,data1,(reverbEffect[channel-8] ? 0x7F : 0x00));
					if(!reverbEffect[channel-8])
						call('deckFxReverbDW', channel-8, 0x00);
				}
				*/
				
			} 
			
		break;
		case 0x03: 
			if(data2){
					call('deckLBLoad', 1, data2);
				}
		break;
		case 0x04: 
			if(channel == 0x0F){
				if(data2){
					call('deckLBLoad', 2, data2);
				}
			} else {
				call('deckTapBpm', channel-8, data2);
			}
			
		break;
		case 0x05: 
			if(data2){
					call('deckLBLoad', 3, data2);
				}
		break;
		case 0x06: 
			if(channel == 0x0F){
				if(data2)
					call('lbToggleSelect', data2);
			} else {
				if(jogWheelsInScratchMode[channel]){
					call('holdDeckJogWheel', channel, data2);
				}
			}
			
		break;
		case 0x07: 
			if(data2){
				jogWheelsInScratchMode[channel] = (jogWheelsInScratchMode[channel] ? 0x00 : 0x7F); 	//If scratch mode was enabled, disable it. If scratch mode was disabled, enable it.
				sendMidi(uniqueId,0x90,channel,0x07,(jogWheelsInScratchMode[channel] ? 0x7F : 0X00)); 
			}
		break;
		case 0x08: 
			
		break;
		case 0x09: 
			if(channel >= 4 && channel <= 7 && data2){ //pad mode slicer
				//light slicer led LED
				if(data2) sendMidi(uniqueId,0x90,channel,data1, 0x7F);
				var led = false;
				switch(padMode[channel - 4]){
					case 'cues':
						led = 0x00;
						break;
					case 'auto':
						led = 0x10;
						break;
					case 'loop'://do nothing
						led = 0x0E;
						break;
					case 'sampler':
						led = 0x0B;
						break;
					case 'slicer':
						led = 0x09;
						break;
				}
				//disable the rest of the pad mode leds
				sendMidi(uniqueId,0x90,channel,led, 0x00);
				
				sendMidi(uniqueId,0x90,channel,0x14,0x00);
				sendMidi(uniqueId,0x90,channel,0x15,0x00);
				sendMidi(uniqueId,0x90,channel,0x16,0x00);
				sendMidi(uniqueId,0x90,channel,0x17,0x00);
				sendMidi(uniqueId,0x90,channel,0x18,0x00);
				sendMidi(uniqueId,0x90,channel,0x19,0x00);
				sendMidi(uniqueId,0x90,channel,0x1A,0x00);
				sendMidi(uniqueId,0x90,channel,0x1B,0x00);
				//store selected pad mode to variable, per deck.
				padMode[channel - 4] = 'slicer';
			} else {
				call('setDeckPitch', channel, 0x7F-data2);
			}
		break;
		case 0x0A: break;
		case 0x0B: 
			if(channel >= 4 && channel <= 7 && data2){ //pad mode sampler
				//light slicer led LED
				if(data2) sendMidi(uniqueId,0x90,channel,data1, 0x7F);
				var led = false;
				switch(padMode[channel - 4]){
					case 'cues':
						led = 0x00;
						break;
					case 'auto':
						led = 0x10;
						break;
					case 'loop'://do nothing
						led = 0x0E;
						break;
					case 'sampler':
						led = 0x0B;
						break;
					case 'slicer':
						led = 0x09;
						break;
				}
				//disable the rest of the pad mode leds
				sendMidi(uniqueId,0x90,channel,led, 0x00);
				
				sendMidi(uniqueId,0x90,channel,0x14,0x00);
				sendMidi(uniqueId,0x90,channel,0x15,0x00);
				sendMidi(uniqueId,0x90,channel,0x16,0x00);
				sendMidi(uniqueId,0x90,channel,0x17,0x00);
				sendMidi(uniqueId,0x90,channel,0x18,0x00);
				sendMidi(uniqueId,0x90,channel,0x19,0x00);
				sendMidi(uniqueId,0x90,channel,0x1A,0x00);
				sendMidi(uniqueId,0x90,channel,0x1B,0x00);
				//store selected pad mode to variable, per deck.
				
				if(getValue('sampler1loaded')){
					sendMidi(uniqueId,0x90,channel,0x14,0x2D);
				}
				if(getValue('sampler2loaded')){
					sendMidi(uniqueId,0x90,channel,0x15,0x2D);
				}
				if(getValue('sampler3loaded')){
					sendMidi(uniqueId,0x90,channel,0x16,0x2D);
				}
				if(getValue('sampler4loaded')){
					sendMidi(uniqueId,0x90,channel,0x17,0x2D);
				}
				if(getValue('sampler5loaded')){
					sendMidi(uniqueId,0x90,channel,0x18,0x2D);
				}
				if(getValue('sampler6loaded')){
					sendMidi(uniqueId,0x90,channel,0x19,0x2D);
				}
				if(getValue('sampler7loaded')){
					sendMidi(uniqueId,0x90,channel,0x1A,0x2D);
				}
				if(getValue('sampler8loaded')){
					sendMidi(uniqueId,0x90,channel,0x1B,0x2D);
				}
				
				padMode[channel - 4] = 'sampler';
			} else {
				call('deckBendUp', channel, data2);
			}				
		break;
		case 0x0C: 
			call('deckBendDown', channel, data2);
		break;
		case 0x0D: 
			call('deckKeyLock', channel, data2);
		break;
		case 0x0E: 
			if(channel >= 4 && channel <= 7 && data2){ //pad mode loop
				//light slicer led LED
				if(data2) sendMidi(uniqueId,0x90,channel,data1, 0x7F);
				var led = false;
				switch(padMode[channel - 4]){
					case 'cues':
						led = 0x00;
						break;
					case 'auto':
						led = 0x10;
						break;
					case 'loop'://do nothing
						led = 0x0E;
						break;
					case 'sampler':
						led = 0x0B;
						break;
					case 'slicer':
						led = 0x09;
						break;
				}
				//disable the rest of the pad mode leds
				sendMidi(uniqueId,0x90,channel,led, 0x00);
				
				sendMidi(uniqueId,0x90,channel,0x14,0x00);
				sendMidi(uniqueId,0x90,channel,0x15,0x00);
				sendMidi(uniqueId,0x90,channel,0x16,0x00);
				sendMidi(uniqueId,0x90,channel,0x17,0x00);
				sendMidi(uniqueId,0x90,channel,0x18,0x00);
				sendMidi(uniqueId,0x90,channel,0x19,0x00);
				sendMidi(uniqueId,0x90,channel,0x1A,0x00);
				sendMidi(uniqueId,0x90,channel,0x1B,0x00);
				//store selected pad mode to variable, per deck.
				padMode[channel - 4] = 'loop';
			}
		break;
		case 0x0F: break;
		case 0x10: 
			if(channel >= 4 && channel <= 7 && data2){ //pad mode auto
				//light slicer led LED
				if(data2) sendMidi(uniqueId,0x90,channel,data1, 0x7F);
				var led = false;
				switch(padMode[channel - 4]){
					case 'cues':
						led = 0x00;
						break;
					case 'auto':
						led = 0x10;
						break;
					case 'loop'://do nothing
						led = 0x0E;
						break;
					case 'sampler':
						led = 0x0B;
						break;
					case 'slicer':
						led = 0x09;
						break;
				}
				//disable the rest of the pad mode leds
				sendMidi(uniqueId,0x90,channel,led, 0x00);
				
				sendMidi(uniqueId,0x90,channel,0x14,0x00);
				sendMidi(uniqueId,0x90,channel,0x15,0x00);
				sendMidi(uniqueId,0x90,channel,0x16,0x00);
				sendMidi(uniqueId,0x90,channel,0x17,0x00);
				sendMidi(uniqueId,0x90,channel,0x18,0x00);
				sendMidi(uniqueId,0x90,channel,0x19,0x00);
				sendMidi(uniqueId,0x90,channel,0x1A,0x00);
				sendMidi(uniqueId,0x90,channel,0x1B,0x00);
				//store selected pad mode to variable, per deck.
				
				var loopsize = getValue('loopSize', channel - 4);
				switch(loopsize){
				case 4:
					sendMidi(uniqueId,0x90,channel,0x14,0x3F);
					break;
				case 8:
					sendMidi(uniqueId,0x90,channel,0x15,0x3F);
					break;
				case 16:
					sendMidi(uniqueId,0x90,channel,0x16,0x3F);
					break;
				case 32:
					sendMidi(uniqueId,0x90,channel,0x17,0x3F);
					break;
				case 64:
					sendMidi(uniqueId,0x90,channel,0x18,0x3F);	
					break;
				case 128:
					sendMidi(uniqueId,0x90,channel,0x19,0x3F);
					break;
				case 256:
					sendMidi(uniqueId,0x90,channel,0x1A,0x3F);
					break;
				case 512:
					sendMidi(uniqueId,0x90,channel,0x1B,0x3F);
					break;
				
			}
				
				
				padMode[channel - 4] = 'auto';
			}
		break;
		case 0x11: break;
		case 0x12: break;
		case 0x13: break;
		case 0x14: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(padMode[channel-4]){
					case 'cues':
						sendMidi(uniqueId,0x90,channel,data1,data2 ? 0x1F : 0x3C);
						call('deckCuePos1', channel - 4, data2);	
						break;
					case 'auto':
						call('deckLoop1_16', channel - 4, data2);
						break;
					case 'loop':
						call('deckLoopInEnd', channel - 4, data2);
						break;
					case 'sampler':
						if(data2)
						call('samplerPlay', 1);
						break;
					case 'slicer':
						
						break;
				}
			}
		break;
		case 0x15: 
		if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
			switch(padMode[channel-4]){
					case 'cues':
						sendMidi(uniqueId,0x90,channel,data1,data2 ? 0x1F : 0x3C);
						call('deckCuePos2', channel - 4, data2);	
						break;
					case 'auto':
						call('deckLoop1_8', channel - 4, data2);
						break;
					case 'loop':
						call('deckLoopInEnd', channel - 4, data2);
						break;
					case 'sampler':
						if(data2)
						call('samplerPlay', 2);
						break;
					case 'slicer':
						
						break;
				}
		}
		break;
		case 0x16: 
		if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
			switch(padMode[channel-4]){
					case 'cues':
						sendMidi(uniqueId,0x90,channel,data1,data2 ? 0x1F : 0x3C);
						call('deckCuePos3', channel - 4, data2);	
						break;
					case 'auto':
						call('deckLoop1_4', channel - 4, data2);
						break;
					case 'loop':
						call('deckLoopExitOrReloop', channel - 4, data2);
						
						break;
					case 'sampler':
						if(data2)
						call('samplerPlay', 3);
						break;
					case 'slicer':
						
						break;
				}
		}
		break;
		case 0x17: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(padMode[channel-4]){
					case 'cues':
						sendMidi(uniqueId,0x90,channel,data1,data2 ? 0x1F : 0x3C);
						call('deckCuePos4', channel - 4, data2);	
						break;
					case 'auto':
						call('deckLoop1_2', channel - 4, data2);
						break;
					case 'loop':
						
						break;
					case 'sampler':
						if(data2)
						call('samplerPlay', 4);
						break;
					case 'slicer':
						
						break;
				}
			}
		break;
		case 0x18: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(padMode[channel-4]){
					case 'cues':
						sendMidi(uniqueId,0x90,channel,data1,data2 ? 0x1F : 0x3C);
						call('deckCuePos5', channel - 4, data2);	
						break;
					case 'auto':
						call('deckLoop1', channel - 4, data2);
						break;
					case 'loop':
						call('deckLoopLess', channel - 4, data2);
						break;
					case 'sampler':
						if(data2)
						call('samplerPlay', 5);
						break;
					case 'slicer':
						
						break;
				}
			}
		break;
		case 0x19: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(padMode[channel-4]){
					case 'cues':
						sendMidi(uniqueId,0x90,channel,data1,data2 ? 0x1F : 0x3C);
						call('deckCuePos6', channel - 4, data2);	
						break;
					case 'auto':
						call('deckLoop2', channel - 4, data2);
						break;
					case 'loop':
						call('deckLoopMore', channel - 4, data2);
						break;
					case 'sampler':
						if(data2)
						call('samplerPlay', 6);
						break;
					case 'slicer':
						
						break;
				}
			}
		break;
		case 0x1A: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(padMode[channel-4]){
					case 'cues':
						sendMidi(uniqueId,0x90,channel,data1,data2 ? 0x1F : 0x3C);
						call('deckCuePos7', channel - 4, data2);	
						break;
					case 'auto':
						call('deckLoop4', channel - 4, data2);
						break;
					case 'loop':
						
						break;
					case 'sampler':
						if(data2)
						call('samplerPlay', 7);
						break;
					case 'slicer':
						
						break;
				}
			}
		break;
		case 0x1B: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){
				switch(padMode[channel-4]){
					case 'cues':
						sendMidi(uniqueId,0x90,channel,data1,data2 ? 0x1F : 0x3C);
						call('deckCuePos8', channel - 4, data2);	
						break;
					case 'auto':
						call('deckLoop8', channel - 4, data2);
						break;
					case 'loop':
						
						break;
					case 'sampler':
						if(data2)
						call('samplerPlay', 8);
						break;
					case 'slicer':
						
						break;
				}
			} else {
				
				call('setDeckMonitor', channel, data2);					
			}
		break;
		case 0x1C: 
			call('deckCuePos1Clear', channel - 4, data2);
		break;
		case 0x1D: 
			call('deckCuePos2Clear', channel - 4, data2);
		break;
		case 0x1E: 
			call('deckCuePos3Clear', channel - 4, data2);
		break;
		case 0x1F: 
			call('deckCuePos4Clear', channel - 4, data2);
		break;
		case 0x20: 
			if(channel >= 4){
				call('deckCuePos5Clear', channel - 4, data2);
			}
			
		break;
		
		case 0x21:
			if(channel == 0x0F){
				call('samplerPlay', 1);
			} else {
				call('deckCuePos6Clear', channel - 4, data2);
			}

		break;
		case 0x22: 
			if(channel == 0x0F){
				call('samplerPlay', 2);
			} else {
				call('deckCuePos7Clear', channel - 4, data2);
			}
		break;
		case 0x23: 
			if(channel == 0x0F){
				call('samplerPlay', 3);
			} else {
				call('deckCuePos8Clear', channel - 4, data2);
			}
		
		break;
		case 0x24: 
			if(channel == 0x0F){
				call('samplerPlay', 4);
			}
		break;
		case 0x25: break;
		case 0x26: break;
		case 0x27: break;
		case 0x28: 
			
		break;
		case 0x29: 
			
		break;
		case 0x2A: break;
		case 0x2B: 
			
		break;
		case 0x2C: 
			
		break;
		case 0x2D: break;
		case 0x2E: break;
		case 0x2F: break;
		case 0x30: break;
		case 0x31: break;
		case 0x32: 
			//loop exit
			if(data2>0x3F)
			{ 
				if(getValue('looping', channel-4)) 
				{
					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)){
						loopBlink[channel-4] = 2; //no loop in; looping;
					}
					
				}
			}	
		break;
		case 0x33: break;
		case 0x34: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){ 
				call('deckLoopMore', channel-4, data2);
			}
		break;
		case 0x35: 
			if(channel == 4 || channel == 5 || channel == 6 || channel == 7){ 
				call('deckLoopLess', 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 0x46: if(data2) displayRemaining[channel] = (displayRemaining[channel] ? 0 : 1); 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 : 0X00)); //play
	if(name=='cue') 		sendMidi(uniqueId,0x90,deck,0x01,(param1 ? 0x7F : 0X00)); //cue
	if(name=='onbeat') 		sendMidi(uniqueId,0x90,deck,0x02,(param1 ? 0x7F : 0X00)); //sync
	//debug((currentPadMode[deck]).toString());
	
	if(name=='justLoaded') outputTime(uniqueId, deck, getValue('timeElapsed', deck), getValue('timeRemain', deck), true);//justloaded
	if(name=='keyLock') 	sendMidi(uniqueId,0x90,deck,0x0D,(param1 ? 0x02 : 0x00));//keylock this l
	if(name=='monitor') 	sendMidi(uniqueId,0x90,deck,0x1B,(param1 ? 0x7F : 0x00));//monitor
	//if(name=='reverse')		sendMidi(uniqueId,0x90,deck,0x10,(param1 ? 0x3F : 0x01));//reverse
	if(padMode[deck] == 'cues'){
		if(name=='cuepos1')		{sendMidi(uniqueId,0x90, deck + 4, 0x14,(param1 ? 0x3C : 0x00))}; 
		if(name=='cuepos2')		{sendMidi(uniqueId,0x90, deck + 4, 0x15,(param1 ? 0x3C : 0x00))}; 
		if(name=='cuepos3')		{sendMidi(uniqueId,0x90, deck + 4, 0x16,(param1 ? 0x3C : 0x00))}; 
		if(name=='cuepos4')		{sendMidi(uniqueId,0x90, deck + 4, 0x17,(param1 ? 0x3C : 0x00))}; 
		if(name=='cuepos5')		{sendMidi(uniqueId,0x90, deck + 4, 0x18,(param1 ? 0x3C : 0x00))}; 
		if(name=='cuepos6')		{sendMidi(uniqueId,0x90, deck + 4, 0x19,(param1 ? 0x3C : 0x00))}; 
		if(name=='cuepos7')		{sendMidi(uniqueId,0x90, deck + 4, 0x1A,(param1 ? 0x3C : 0x00))}; 
		if(name=='cuepos8')		{sendMidi(uniqueId,0x90, deck + 4, 0x1B,(param1 ? 0x3C : 0x00))}; 
	}
	
	if(padMode[deck] == 'loop'){
		if(name=='loopIn') 	 	 sendMidi(uniqueId,0x90,deck + 4,0x14,(param1 ? 0x21 : 0x00)); //loop in
		if(name=='looping') {
					sendMidi(uniqueId,0x90,deck + 4,0x14,(param1 ? 0x21 : 0x00));// loop in led
					sendMidi(uniqueId,0x90,deck + 4,0x15,(param1 ? 0x21 : 0x00));//loop out led
					
					//debug('Blink State: ' + samplerBlinkLedStatus[deck].toString());
					sendMidi(uniqueId,0x90,deck + 4,0x16, (param1 ? 0x2F : 0x00));//loop out led ???
		}
	
	
	}
	
	if(padMode[deck] == 'auto'){
		if(name=='loopSize'){
			//debug('Loop Size: ' + param1);
			sendMidi(uniqueId,0x90,deck + 4,0x14,0x00);
			sendMidi(uniqueId,0x90,deck + 4,0x15,0x00);
			sendMidi(uniqueId,0x90,deck + 4,0x16,0x00);
			sendMidi(uniqueId,0x90,deck + 4,0x17,0x00);
			sendMidi(uniqueId,0x90,deck + 4,0x18,0x00);
			sendMidi(uniqueId,0x90,deck + 4,0x19,0x00);
			sendMidi(uniqueId,0x90,deck + 4,0x1A,0x00);
			sendMidi(uniqueId,0x90,deck + 4,0x1B,0x00);
			switch(param1){
				case 4:
					sendMidi(uniqueId,0x90,deck + 4,0x14,0x3F);
					break;
				case 8:
					sendMidi(uniqueId,0x90,deck + 4,0x15,0x3F);
					break;
				case 16:
					sendMidi(uniqueId,0x90,deck + 4,0x16,0x3F);
					break;
				case 32:
					sendMidi(uniqueId,0x90,deck + 4,0x17,0x3F);
					break;
				case 64:
					sendMidi(uniqueId,0x90,deck + 4,0x18,0x3F);	
					break;
				case 128:
					sendMidi(uniqueId,0x90,deck + 4,0x19,0x3F);
					break;
				case 256:
					sendMidi(uniqueId,0x90,deck + 4,0x1A,0x3F);
					break;
				case 512:
					sendMidi(uniqueId,0x90,deck + 4,0x1B,0x3F);
					break;

			}
		}
	}
	
	for(var d = 0; d < 4; d++){
		if(padMode[d] == 'sampler'){
		
			if(name=='sampler1'){ 		
				sendMidi(uniqueId,0x90,d + 4,0x14,(param1 ? 0xC4 : 0x00)); //sample
				//debug('Condition s1 found');
			}
			if(name=='sampler2'){ 
				sendMidi(uniqueId,0x90,d + 4,0x15,(param1 ? 0xC4 : 0x00)); //sample
				//debug('Condition s2 found');
			}
			if(name=='sampler3'){ 
				sendMidi(uniqueId,0x90,d + 4,0x16,(param1 ? 0xC4 : 0x00)); //sample
				//debug('Condition s3 found');
			}
			if(name=='sampler4'){ 
				sendMidi(uniqueId,0x90,d + 4,0x17,(param1 ? 0xC4 : 0x00)); //sample
				//debug('Condition s4 found');
			}
			if(name=='sampler5'){ 
				sendMidi(uniqueId,0x90,d + 4,0x18,(param1 ? 0xC4 : 0x00)); //sample
				//debug('Condition s5 found');
			}
			if(name=='sampler6'){ 
				sendMidi(uniqueId,0x90,d + 4,0x19,(param1 ? 0xC4 : 0x00)); //sample
				//debug('Condition s6 found');
			}
			if(name=='sampler7'){ 
				sendMidi(uniqueId,0x90,d + 4,0x1A,(param1 ? 0xC4 : 0x00)); //sample
				//debug('Condition s7 found');
			}
			if(name=='sampler8'){ 
				sendMidi(uniqueId,0x90,d + 4,0x1B,(param1 ? 0xC4 : 0x00)); //sample
				//debug('Condition s8 found');
			}
		}
	
	}
	
	
		
	

}

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)
{
	//outputTime(uniqueId, 0, true);
	//debug('Pad Modes: ' + padMode);
	for(var deck=0; deck<4; deck++)
	{
		// track position
		
		
		var timeElapsed = getValue('timeElapsed', deck);// float seconds
		var timeRemain = getValue('timeRemain', deck);// float seconds		
		var timePos = Math.floor(100*timeElapsed/(timeElapsed + timeRemain));// 0..100 integer
		var totalTime = timeElapsed + timeRemain;
		var ans = timeRemain * 52;
		ans = ans/totalTime;		
		sendMidi(uniqueId,0xB0,deck,0x3F, Math.round(ans));//time remaining inner circle
		
		var jwAngle = getValue('jogWheelAngle', deck);// float degrees
		sendMidi(uniqueId,0xB0,deck,0x06, Math.round((jwAngle * 52 / 360.0) + 1));// Angle outter circle
				
		outputTime(uniqueId, deck, timeElapsed, timeRemain, false);
		outputBPM(uniqueId, deck);
		
		var vuDeck = getValue('vumeterDeck', deck);// float dB
		sendMidi(uniqueId,0xB0,deck,0x1F,getMIDIValueForVU(vuDeck));
		
		
	}
	//now do left/right master vu
	//var masterVU = getValue('vumeterMasterL');// float dB
	//sendMidi(uniqueId,0xBF,deck,0x01,getMIDIValueForVU(masterVU));
	//masterVU = getValue('vumeterMasterR');// float dB
	//sendMidi(uniqueId,0xBF,deck,0x02,getMIDIValueForVU(masterVU));
		
	return;
}
function outputBPM(uniqueId, deck){
	var bpm = getValue('currentBpm', deck) * 100;

	var sysexData = new Array(13);
	sysexData[0] = 0xF0;// start of SysEx message
	sysexData[1] = 0x00;
	sysexData[2] = 0x20;
	sysexData[3] = 0x7F;
	sysexData[4] = deck + 1;// deck number (ATTN: 1-based index, that's why we add 1)
	sysexData[5] = 0x01;
	for(var j=11; j>5; j--)
		 {
		  // we always use the last 4 bits from our INT value
		  sysexData[j] = bpm & 0x0F;// get last 4 bits
		  bpm = bpm >> 4;// shift bits to the right
		  //alert("number["+(j-6)+"]=" + sysexData[j].toString(16));
		 }
	sysexData[12] = 0xF7;// end of SysEx message
	sendMidiSysEx(uniqueId, sysexData, sysexData.length);		
	
}
function getMIDIValueForVU(vu_val)
{
	// translating VU-meter signal from decibels (dB) to 0..127 MIDI range
	var val = 0;
	var step = 12.7;
	if(vu_val>-44) val =  1 * step;
	
	if(vu_val>-28) val =  2 * step;
	
	if(vu_val>-16) val =  4 * step;
	
	if(vu_val> -8) val =  6 * step;
	
	if(vu_val> -2) val =  8 * step;
	if(vu_val> -1) val = 10 * step;
	return Math.floor(val);
}
function outputTime(uniqueId, deck, timeElapsed, timeRemain, sendTotal)
{
 // get the time
 //var timeElapsed = getValue('timeElapsed', deck);// float seconds
 //var timeRemain = getValue('timeRemain', deck);// float seconds
// var timeElapsed = 0; var timeRemain = 230.130;// test values

 // convert to the Numark format
 var timeMS = timeElapsed * 1000;
 
 if(displayRemaining[deck])
	timeMS = timeRemain * 1000;
	
 if(sendTotal) timeMS = (timeElapsed + timeRemain) * 1000;
 var timeConverted = Math.round(timeMS) + 0x80000000;
 //debug("number=" + timeConverted.toString(16));// use alert() instead of debug() if in a browser

 // prepare the SysEx message
 var sysexData = new Array(15);
 sysexData[0] = 0xF0;// start of SysEx message
 sysexData[1] = 0x00;
 sysexData[2] = 0x20;
 sysexData[3] = 0x7F;

 sysexData[4] = deck + 1;// deck number (ATTN: 1-based index, that's why we add 1)
 sysexData[5] = sendTotal ? 0x03 : 0x04;// use 0x03 for total track time; use 0x04 for elapsed track time;

 // we now output the digits one by one (there are 8 of them, each is 4-bits wide, so 8 * 4bits = 32bits = 1 int)
 for(var j=13; j>5; j--)
 {
  // we always use the last 4 bits from our INT value
  sysexData[j] = timeConverted & 0x0F;// get last 4 bits
  timeConverted = timeConverted >> 4;// shift bits to the right
  //alert("number["+(j-6)+"]=" + sysexData[j].toString(16));
 }
 sysexData[14] = 0xF7;// end of SysEx message

 // send the sysex
 sendMidiSysEx(uniqueId, sysexData, sysexData.length);
}
//----------------------------------------------------------------------------- 
