//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2015 XYLIO INFO SRL. All Rights Reserved. --------
//-----------------------------------------------------------------------------
// File implementing MIDI controller 'Numark MixTrack Pro 3'
// Written By: Tollen Francisco
// Version: 1.0
// Date: 07-17-2014
//----------------------------------------------------------------------------- 
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 "MixTrack Pro 3";
}
//----------------------------------------------------------------------------- 
// return the communication type that the controller supports 
// this can be either 'midi' or 'hid' (please return only one of them in case the controller supports both)
function getControllerType()
{
	return 'midi';
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// preferred way to identify the controller (using the USB device VID and PID -- vendor and product id)
// return false if the controller does not support this kind of identification
// osId -- 0 Windows; 1 MacOSX; 2 Linux; -1 unknown
function getSupportedMidiDeviceVID(osId)
{	
	return 0x15E4;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDevicePID(osId)
{	
	return 0x0035;	
}
//----------------------------------------------------------------------------- 
// 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 (if it supports MIDI SysEx)
// return false if the controller does not support this kind of identification
function getSupportedMidiSysExIdString(osId)
{
	// sysex response to 'F0 7E 7F 06 01 F7' (identification request sysex)
	// please note that the response contains 5 bytes in hex format containing
	// the Manufacturer ID (3 bytes) and Model Number (2 bytes)
	// ATTN: the returned value should omit the standard 'F0 7E 00 06 02' message start
	//if(osId==1) return false;
	return false;
}
//----------------------------------------------------------------------------- 
// 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 "MixTrack Pro 3";	
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	return "MixTrack Pro 3";	
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var shiftMode=[0,0,0,0];

var padMode						=[0,0,0,0];//0=Manual Loop;1=Auto Loop; 2=Sampler;
var padModeButtonPressed 		=[0,0,0,0];

var jogWheelsInScratchMode		=[0,0,0,0];
var loopingInBlinkLed			=[-1,-1,-1,-1];// -1 means disabled
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 ignoreNextWheelBtnCommand	=[false,false,false,false];
// effects
var cutoffValue			=[63,63,63,63];
var flangerValue		=[127,127,127,127];
var delayValue			=[127,127,127,127];
var echoValue			=[127,127,127,127];

var flangerParam1		=[63,63,63,63];
var delayParam1			=[63,63,63,63];
var echoParam1			=[63,63,63,63];

var flangerParam2		=[63,63,63,63];
var delayParam2			=[63,63,63,63];
var echoParam2			=[63,63,63,63];

var controllerChange 	= [0,0,0,0];
var base 				= 16256;		
var amount 				= [0,0,0,0];
var percent 			= [0,0,0,0];
var newPitch 			= [0,0,0,0];

//  0);	off	
// 32);	blue		
// 96);	yellow	
//127); purple
var off     = 00
var blue 	= 32;
var yellow 	= 96;					
var purple 	= 127;

var loopBlink 	= [0,0,0,0];//0=off, 1=blink, 2=on

var currentFXDW = [0,0,0,0];

var echo 	= 0;
var delay 	= 1;
var flanger = 2;

var fxEnabled = [[false,false,false],[false,false,false],[false,false,false]];
//----------------------------------------------------------------------------- 
// value is 0x7F for left (-1); 0x01 for right(+1);
function trimEncoderValue(value, increment, initialValue)
{
	var outValue = 0;
	var outIncrement = 0;
	if(value>0x40) // left (decrease)
		outIncrement = increment * (value - 0x80);
	else // left (increase)
		outIncrement = increment * (value);
		outValue = initialValue + outIncrement;
	if(outValue<0) outValue = 0;
	if(outValue>0x7F) outValue = 0x7F;
	return outValue;
}
//----------------------------------------------------------------------------- 
// this is called when the controller is initialized and the control is passed to this script
// use this to initialize any variables you might need, the controller' leds, etc
// uniqueId is a unique integer between 0..15 that is guaranteed not to change for 
// a certain opened controller -- please note that there can be many controllers
// of the same type in use at the same time ! Use uniqueId to identify between them
function controllerInit(uniqueId)
{
	//debug('controllerInit MixTrack 2 / MixTrack Pro 2 ['+uniqueId+']');// this outputs a string to DebugView app on Win	
	//sendMidi(uniqueId,0x90,0,0x75,0x7F);// power on ALL leds
	//call('sleep', 300);
	//sendMidi(uniqueId,0x90,0,0x75,0x00);// power off ALL leds
	/*
	for(var i=0; i<0x7F; i++)
	{
		sendMidi(uniqueId,0x90,0,i,0x7F);// on
		if(i % 10 == 0) call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
	}
	*/
	// power on some leds that are not set by the notification system
	for(var deck=0; deck<2; deck++)
	{
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x48:0x50,(jogWheelsInScratchMode[deck] ? 0x7F : 0x00));// scratch mode A/B
		
		// PAD Leds: Sampler
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x20,purple);// pad 1 A/B
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x21,purple);// pad 2 A/B
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x22,purple);// pad 3 A/B
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x23,purple);// pad 4 A/B
		
		//PAD Leds: Auto Loop
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x17,yellow);// pad 4 A/B
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x18,yellow);// pad 4 A/B
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x19,yellow);// pad 4 A/B
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x1A,yellow);// pad 4 A/B
		
		//pad button leds
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x0D,0x7F);
		
		//Wheel button leds
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x06,off);
		
		call('sleep', 5);// delay for 5ms to allow the MIDI out buffer to flush
	}	
	
	sendMidi(uniqueId,176,0,2,0);// l1 VU Led (each led is + 24 or the previous(1 to 96)
	sendMidi(uniqueId,176,0,3,0);// r1 VU Led
	
	
	
	
	
	
	
	// enable loop mode (for the bottom 4 pseudo-pads)
	//sendMidi(uniqueId,0x90,0,0x1E,0x7F);// loop (small led)
	//sendMidi(uniqueId,0x90,0,0x21,0x7F);// loop (small led)
	//------------------------------------
	//callSendAllNotifications(uniqueId, true);// send all notifications to the controller (so that we don't have to manually init all leds/lcd screens)
}
//----------------------------------------------------------------------------- 
// called just before the controller will be deinitialized and the control will go away from the script
// use to power off any powered leds, etc
function controllerDeinit(uniqueId)
{
	//debug('controllerDeinit ['+uniqueId+']');
	// power off some leds that are not set by the notification system
	//sendMidi(uniqueId,0x90,0,0x48,0x00);// scratch mode A
	//sendMidi(uniqueId,0x90,0,0x50,0x00);// scratch mode B
	for(var deck=0; deck<2; deck++)
	{
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x48:0x50,(jogWheelsInScratchMode[deck] ? 0x7F : 0x00));// scratch mode A/B
		// top 4 pads leds
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x20,off);// pad 1 A/B
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x21,off);// pad 2 A/B
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x22,off);// pad 3 A/B
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x23,off);// pad 4 A/B
		
		//autoloop button leds
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x17,off);// pad 4 A/B
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x18,off);// pad 4 A/B
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x19,off);// pad 4 A/B
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x1A,off);// pad 4 A/B
		
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x1B,off);//Cue Position 1
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x1C,off);//Cue Position 2
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x1D,off);//Cue Position 3
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x1E,off);//Cue Position 4
		
		//monitor leds
		sendMidi(uniqueId,0x90,0,(deck ? 0x0F : 0x0E),off);
		//pad button leds (These cant be powered down?)
		//sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x0D,off);
		
		//Wheel Button LEDs
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x06,off);
		
		//FX button LEDs
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x07,0x00);
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x08,0x00);
		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x09,0x00);
		
		//Play Pause Button
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x01,0x00);
		//Cue Button
		sendMidi(uniqueId,(deck ? 0x91 : 0x92),0,0x03,0x00);


		
		call('sleep', 5);// delay for 5ms to allow the MIDI out buffer to flush
	}	
	//------------------------------------	
	//callSendAllNotifications(uniqueId, false);// send all notifications to the controller (so that we don't have to manually de-init all leds/lcd screens)
	//sendMidi(uniqueId,0x90,0,0x75,0x00);// power off ALL leds
}
//----------------------------------------------------------------------------- 
// this is your main target function to implement the controller -- this is called 
// for each and every MIDI message that comes from the controller itself
// return true if the message was processed by the script and false to let it 
// pass thru the software (eg. so it can be assigned by the user using the LEARN function)
function messageReceived(uniqueId,status,channel,data1,data2)
{		
	var deck 				= channel-1;  //Double check to ensure that this is not causing issues; Sometimes this variable gets set to -1
	var messageNotProcessed	=false;
	switch(status)
	{
	//-----------------------------------------------
	// controllers: sliders, knobs, jog-wheels, encoders ('moving' things)		
		case 0x90: //GLOBAL
		//debug('messageReceived ['+uniqueId+'] - status:'+status+' channel:'+channel+' data1:'+data1+' data2:'+data2)
			switch(data1){
				case 0x0E:	//Monitor Deck 1
					call('deckMonitor', 0, data2);					
					break;
				case 0x0F: 	//Monitor Deck 2
					call('deckMonitor', 1, data2);					
					break;
				case 0x10:	//Load Deck 1
					if(data2>0x3F) call('deckLBLoad', 0, data2);
					break;
				case 0x11:	//Load Deck 2
					if(data2>0x3F) call('deckLBLoad', 1, data2);	
					break;
				case 0x12:	//Toggle Browser/List select
					if(data2>0x3F) call('lbToggleSelect', data2); break;
					break;
			}
		//break;
		case 0x91: //DECK 1
		case 0x92: //DECK 2
			switch(data1){ 
				case 0x01: 									//Play/Pause Pressed/Released	
					if(shiftMode[deck]){
					    call('deckBrake', deck, data2);         //Press Shift+Play/Pause to engage Brake. Press again to disengage.
					} else {
						call('deckPlayPause', deck, data2);  	//If Deck is Paused, Play Deck. If Deck is Playing, Pause Deck.
					}				
					
					break;
				case 0x02: 								//Sync Button Pressed/Released
					if(shiftMode[deck]){ 				//If Shift Button is pressed, we will Match the deck/s.
						call('deckMatch', deck, 0x7F); 	//Match Deck.									
					} else { 							//If Shift Button is not pressed, we will Sync the deck/s.
						call('deckSync', deck, 0x7F); 	//Sync Deck.						
					}					
					break;
				case 0x03: 										//Cue Button Pressed/Released				 
					if(shiftMode[deck]){						//If Shift Button IS pressed, the Deck will Cue at the beginning of the Track.
						call('deckBackToTop', deck, data2); 	//Cue to beginning of Track.
					} else { 									//If Shift Button IS NOT pressed, the Deck will Cue at the Cue-Point.
						call('deckCue', deck, data2); 			//Cue to Cue-Point
					}
					break;
				case 0x04: 								//Pitch-Bend Down Button (00 or 7F)
					/************************
					* Pitch-Bend Down as long as button is pressed.
					*************************/
					call('deckBendDown', deck, data2); 	//Pitch-Bend Down
					break;
				case 0x05: 								//Pitch-Bend Up Button (00 or 7F)
					/************************
					* Pitch-Bend Up as long as button is pressed.
					*************************/
					call('deckBendUp', deck, data2);	//Pitch-Bend Up				
					break;
				case 0x06: 								//Wheel Button (00 or 7F)				
					if(data2){ //Only concerned about button presses, not button releases.
						jogWheelsInScratchMode[deck] = (jogWheelsInScratchMode[deck] ? 0x00 : 0x7F); 	//If scratch mode was enabled, disable it. If scratch mode was disabled, enable it.
						sendMidi(uniqueId,0x90,channel,data1,jogWheelsInScratchMode[deck]);				//Turn on Scratch Mode LED based on the state of 'Scratch Mode'.						
					}
					break;
				case 0x07: //FX 1 (00 or 7F)							
					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,0x07,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,0x07,0x00); 		//Disable FX 1 LED on controller.					
						}					
					}
					break;
				case 0x08: //FX 2 (00 or 7F)										
					if(data2){//Only concerned about button presses, not button releases.
						if(!fxEnabled[deck]['delay']){ //If Delay is not currently enabled, enable it.
							fxEnabled[deck]['delay'] = true; //Update the state of Delay effect to 'on/true'.		
							currentFXDW[deck] = 'deckFxDelayDW'; //Set the currently selected effect to Delay.					
							call(currentFXDW[deck], deck, delayValue[deck]); //Enable the Delay effect in software.
							sendMidi(uniqueId,0x90,channel,0x08,0x7F);//Enable FX 2 LED on controller.
						} else { //If Delay is currently enabled, then disable it.
							fxEnabled[deck]['delay'] = false;			//Update the state of the Delay effect to 'off/false'.						
							call('deckFxDelayDW', deck, 0x00); 			//Disable the Delay effect in software.						
							currentFXDW[deck] = '';             		//Unset the currently selected effect.
							sendMidi(uniqueId,0x90,channel,0x08,0x00); 	//Disable FX 2 LED on controller.
						}
					}					
					break;
				case 0x09: //FX 3 (00 or 7F)
                    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,0x09,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,0x09,0x00); 			//Disable FX 2 LED on controller.					
						}
					}
					break;
				case 0x0A: 										//Tap Button Pressed/Released.
					//data2 will be one of two values (0x00 = released/off; 0x7F = pressed/on)
					call('deckTapBpm', deck, data2);            //Update Tap BPM function in software.
					sendMidi(uniqueId,0x90,channel,0x0A,data2); //Update Tap LED using data2 value.
					break;
				
				
				/*******************
				 * Pad Mode Buttons: Begin
				 *******************/
				/*
				PAD MODE: MANUAL LOOP
				*/
				case 0x13: //Manual Mode: Loop in ( 00 or 7F)									
					if(!getValue('looping', deck) && loopBlink[deck] != 1){ 
						call('deckLoopInEnd', deck, data2);	//this will trigger loopInOut. Cant seem to track the complete in/out state					
						loopBlink[deck]=1; //loop in; no loop out; not looping;
						sendMidi(uniqueId,0x90,channel,data1,blue);
						
						sendMidi(uniqueId,0x90,channel,0x14,off);
						sendMidi(uniqueId,0x90,channel,0x15,off);
						sendMidi(uniqueId,0x90,channel,0x16,off);
					}
					
					
					break;
				case 0x14: //Manual Mode: Loop Out
					
					if(!getValue('looping', deck) && loopBlink[deck] == 1){
						call('deckLoopInEnd', deck, data2);	
						loopBlink[deck] = 2; //no loop in; looping;
						sendMidi(uniqueId,0x90,channel,0x13,blue);												
						sendMidi(uniqueId,0x90,channel,0x14,blue);										
						sendMidi(uniqueId,0x90,channel,0x15,blue);
						sendMidi(uniqueId,0x90,channel,0x16,yellow);												
					}
						
						
						
					break;
				case 0x15: //Manual Mode: Loop On/Off
					
					if(data2>0x3F)
						{ 
							if(getValue('looping', deck)) 
							{
								call('deckLoopExit', deck, 0x7F);
								call('deckLoopExit', deck, 0x00);
								loopBlink[deck] = 0; //no loop in; not looping;
								sendMidi(uniqueId,0x90,channel,0x13,off);												
								sendMidi(uniqueId,0x90,channel,0x14,off);										
								sendMidi(uniqueId,0x90,channel,0x15,off);
								sendMidi(uniqueId,0x90,channel,0x16,off);
								
							}else /*if(loopBlink[deck] = 0)*/
							{	
								call('deckLoopReloop', deck, 0x7F);
								call('deckLoopReloop', deck, 0x00);
								if(getValue('looping', deck)){
									loopBlink[deck] = 2; //no loop in; looping;
									sendMidi(uniqueId,0x90,channel,0x13,blue);												
									sendMidi(uniqueId,0x90,channel,0x14,blue);										
									sendMidi(uniqueId,0x90,channel,0x15,blue);
									sendMidi(uniqueId,0x90,channel,0x16,yellow);	
								}
								
							}
						}					
					break;
					
				
				case 0x16: //Manual Mode: Loop 1/2 or x2
					(shiftMode[deck] ? call('deckLoopMore', deck, data2) : call('deckLoopLess', deck, data2));
					break;
					
				/*
				PAD MODE: AUTO LOOP
				*/				
				case 0x17: //Auto Mode: Auto Loop 1 (1/4)
					call('deckLoop1_4', deck, data2);
					break;
				case 0x18: //Auto Mode: Auto Loop 2 (1)
					call('deckLoop1', deck, data2);
					break;
				case 0x19: //Auto Mode: Auto Loop 3 (4)
					call('deckLoop4', deck, data2);
					break;
				case 0x1A: //Auto Mode: Auto Loop 4 (16)
					call('deckLoop16', deck, data2);
					break;
					
					
				/*
				PAD MODE: SAMPLER
				*/	
				case 0x20: //Sampler 1
					if(data2>0x3F) if(deck == 0) call('samplerPlay', 1); else call('samplerPlay',5);
					break;
				case 0x21: //Sampler 2
					if(data2>0x3F) if(deck == 0) call('samplerPlay', 2); else call('samplerPlay',6);
					break;
				case 0x22: //Sampler 3
					if(data2>0x3F) if(deck == 0) call('samplerPlay', 3); else call('samplerPlay',7);
					break;
				case 0x23: //Sampler 4
					if(data2>0x3F) if(deck == 0) call('samplerPlay', 4); else call('samplerPlay',8);
					break;
					
				
				/*******************
				 * Pad Mode Buttons: End
				 *******************/	
					
				case 0x0A: //FX 1 (00 or 7F)
					break;
				case 0x0B: //Shift (00 or 7F)
					shiftMode[deck] = data2;										
					break;
				case 0x0C: //Deck Pressed					
					break;
				case 0x0D: //Pad Mode Pressed					
					padModeButtonPressed[deck] = data2;					
					break;					
				case 0x1B: if(shiftMode[deck]) call('deckCuePos1Clear', deck, data2); else call('deckCuePos1', deck, data2);
					break; 
				case 0x1C: if(shiftMode[deck]) call('deckCuePos2Clear', deck, data2); else call('deckCuePos2', deck, data2); break;
					break;
				case 0x1D: if(shiftMode[deck]) call('deckCuePos3Clear', deck, data2); else call('deckCuePos3', deck, data2); break;
					break;
				case 0x1E: if(shiftMode[deck]) call('deckCuePos4Clear', deck, data2); else call('deckCuePos4', deck, data2); break;
					break;
			}
		break;
		case 0xB0:			
		case 0xB1: //Deck 1
		case 0xB2: //Deck 2
		switch(data1){
			case 0x01: // Treble (min=00;max=7F)
				//debug('Set EQ Low');
				call('setDeckEqHigh', deck, data2);
				break;
			case 0x02: // Mid (min=00;max=7F)
				//debug('Set EQ Mid');
				call('setDeckEqMid', deck, data2);
				break;
			case 0x03: // High (min=00;max=7F)
				//debug('Set EQ Low');
				call('setDeckEqLow', deck, data2);
				break;
			case 0x04: // Filter (min=00;max=7F)			
				call('setDeckCutoff', deck, data2);
				break;
			case 0x05: // Deck Volume (min=00; max=7F)
				call('setDeckVolume', deck, data2);
				break;
			case 0x07: // CrossFader (min=00;max=7F)
				call('setCrossfader', data2);
				break;
			case 0x08: //Cue Gain
				call('setMonitorGain', data2); break;
				break;			
			case 0x09: //Cue Mix
				call('setCuePgmMix', data2); break;
				break;			
			case 0x0A: //Master Gain
				call('setMasterGain', data2); break;
				break;				
			case 0x0B:
				break;
			case 0x0C: 				
				break;
			case 0x11:
				var val1 = (data2>0x40 ? data2-0x80 : data2);				
				if(jogWheelsInScratchMode[deck]){					
					call('scratchDeckJW', deck, val1, 600);					
				} else {						
					if(shiftMode[deck])					
						call('searchDeckJW', deck, val1, 600);
					else
						call('moveDeckJogWheel', deck, val1/2, 600);					
				}
				break;
			case 0x13: //param1 knob
				if(currentFXDW[deck]){
					if(!shiftMode[deck]){					
						switch(currentFXDW[deck]){					
							case 'deckFxEchoDW':	
								echoParam1[deck] = adjust_param_value(echoParam1[deck],data2,0x7F);						
								call('deckFxEchoP1', deck, echoParam1[deck]);							
								break;
							case 'deckFxDelayDW':							
								delayParam1[deck] = adjust_param_value(delayParam1[deck],data2,0x7F);
								call('deckFxDelayP1', deck, delayParam1[deck]);
								break;
							case 'deckFxFlangerDW':											
								flangerParam1[deck] = adjust_param_value(flangerParam1[deck],data2,0x7F);
								call('deckFxFlangerP1', deck, flangerParam1[deck]);								
								break;					
						}					
					} else {						
						switch(currentFXDW[deck]){					
							case 'deckFxEchoDW':	
								echoParam2[deck] = adjust_param_value(echoParam2[deck],data2,0x7F);												
								call('deckFxEchoP2', deck, echoParam2[deck]);							
								break;
							case 'deckFxDelayDW':							
								delayParam2[deck] = adjust_param_value(delayParam2[deck],data2,0x7F);								
								call('deckFxDelayP2', deck, delayParam2[deck]);
								break;
							case 'deckFxFlangerDW':											
								flangerParam2[deck] = adjust_param_value(flangerParam2[deck],data2,0x7F);
								call('deckFxFlangerP2', deck, flangerParam2[deck]);
								
								break;					
						}
					}
					
				}
				
					
				break;
			
				
				
			case 0x18:
				controllerChange[deck] = data2;								
				break;
			//case 0x12:
			//case 0x19:
			case 0x1A:
				if(!shiftMode[deck])
					switch(currentFXDW[deck]){					
						case 'deckFxEchoDW':	
							echoValue[deck] = data2;
							echoValue[deck] ? sendMidi(uniqueId,0x90,channel,0x07,0x7F) : sendMidi(uniqueId,0x90,channel,0x07,0x00);
							call(currentFXDW[deck], deck, echoValue[deck]);
							break;
						case 'deckFxDelayDW':
							delayValue[deck] = data2;
							delayValue[deck] ? sendMidi(uniqueId,0x90,channel,0x08,0x7F) : sendMidi(uniqueId,0x90,channel,0x08,0x00);
							call(currentFXDW[deck], deck, delayValue[deck]);
							break;
						case 'deckFxFlangerDW':
							flangerValue[deck] = data2;
							flangerValue[deck] ? sendMidi(uniqueId,0x90,channel,0x09,0x7F) : sendMidi(uniqueId,0x90,channel,0x09,0x00);
							call(currentFXDW[deck], deck, flangerValue[deck]);
							break;					
					}	
				break;
			case 0x17:
				var val1 = (data2>0x40 ? data2-0x80 : data2);
				call('lbMoveCursor', val1);
				break;
			case 0x38:				
				amount[deck] = (((controllerChange[deck]+0x01)*0x7F)+data2)-0x7F;
				percent[deck] =  1.0 - (amount[deck] / base);
				newPitch[deck] = (percent[deck] * 0x7F);
				call('setDeckPitch', deck, newPitch[deck]); 
				break;
		}
		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)
{
	if(deck>=2) return;
	var nameLen = name.length;
	if(name=='monitor') debug('Monitoring: name='+name + ' deck='+deck + ' p1(int)='+param1 + ' p2(float)='+param2 + ' p3(str)='+param3);
	
	if(name=='play')        sendMidi(uniqueId,(deck==0) ? 0x91:0x92,0,0x01,(param1 ? 0x7F : 0x00));
	
	
	if(name=='cue')       {  sendMidi(uniqueId,(deck==0) ? 0x91:0x92,0,0x03,(param1 ? 0x7F : 0x00)); }
	
	//sync
	if(name=='onbeat') 	   {	sendMidi(uniqueId,(deck==0) ? 0x91:0x92,0,0x02,(param1 ? 0x7F : 0x00));}
	
	
	if(name=='monitor') 	sendMidi(uniqueId,0x90,0,(deck==0?0x0E:0x0F),(param1 ? 0x7F : 0x00));
	
	
	
	if(name=='pitchIs0')	sendMidi(uniqueId,0x90,0,(deck==0)?0x28:0x29,(param1 ? 0x7F : 0x00));		
	//if(name=='reverse') 	sendMidi(uniqueId,0x90,0,(deck==0)?0x4A:0x4C,(param1 ? 0x7F : 0x00));// stutter leds (cueplay) -- not used
	
	if(name=='cuepos1')		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x1B,(param1 ? 0x7F : 0x00));
	if(name=='cuepos2')		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x1C,(param1 ? 0x7F : 0x00));
	if(name=='cuepos3')		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x1D,(param1 ? 0x7F : 0x00));
	if(name=='cuepos4')		sendMidi(uniqueId,(deck ? 0x92 : 0x91),0,0x1E,(param1 ? 0x7F : 0x00));
	
	if(name=='reverse')		sendMidi(uniqueId,0x90,0,(deck==0)?0x59:0x5D,(param1 ? 0x7F : 0x00));
	if(name=='brake')		sendMidi(uniqueId,0x90,0,(deck==0)?0x5A:0x5E,(param1 ? 0x7F : 0x00));
	//if(name=='cuepos3')		sendMidi(uniqueId,0x90,0,(deck==0)?0x5B:0x5F,(param1 ? 0x7F : 0x00));// not used
	//if(name=='cuepos4')		sendMidi(uniqueId,0x90,0,(deck==0)?0x5C:0x60,(param1 ? 0x7F : 0x00));// not used

	if(name=='loopIn')
	{
		sendMidi(uniqueId,0x90,0,(deck==0)?0x53:0x56,(param1 ? 0x7F : 0x00));// Loop In (on or off)
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x1E:0x21,(param1 ? 0x7F : 0x00));// Loop In (on or off) // loop in/loop (small led)
		// activate blinking for the LoopIn led (so the user knows to press it again)
		if(param1) loopingInBlinkLed[deck] = 0;else loopingInBlinkLed[deck] = -1;
	}
	if(name=='looping')
	{
		sendMidi(uniqueId,0x90,0,(deck==0)?0x53:0x56,0x00);// Loop In (off)		
		sendMidi(uniqueId,0x90,0,(deck==0)?0x54:0x57,(param1 ? 0x7F : 0x00));// Loop Out (on or off)
		sendMidi(uniqueId,0x90,0,(deck==0)?0x55:0x58,(param1 ? 0x7F : 0x00));// Reloop (on or off)
		sendMidi(uniqueId,0x90,0,(deck==0)?0x63:0x64,0x00);// Loop more (off)
		// alternate small leds
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x1E:0x21,0x00);// Loop In (on or off) // loop in/loop (small led)
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x1F:0x22,(param1 ? 0x7F : 0x00));// Loop Out (on or off) // loop out/sample (small led)
		//sendMidi(uniqueId,0x90,0,(deck==0)?0x20:0x23,(param1 ? 0x7F : 0x00));// Reloop (on or off) // reloop/cue (small led)
	}
	if(name=='lbMode')
	{
		sendMidi(uniqueId,0x90,0,0x34,(param1==1 ? 0x7F : 0x00));// folder
		sendMidi(uniqueId,0x90,0,0x4B,(param1==0 ? 0x7F : 0x00));// file
	}	

	//var samplerBlinkLedStatus=[0,0,0,0,0,0,0,0];// 2 decks leds, each with 4 samplers // 0 means off; 1 means on; 2 means blink on; 3 means blink off;
	if(nameLen==8)
	{
		var name1 = name.substr(0, nameLen-1);
		if(name1=='sampler')
		{
			var samplerIdx = parseInt(name.charAt(nameLen-1))-1;		
			if(!param1) 
			{
				samplerBlinkLedStatus[samplerIdx] = 0;
				if(name=='sampler1') sendMidi(uniqueId,0x90,0,0x65,0x00);
				if(name=='sampler2') sendMidi(uniqueId,0x90,0,0x66,0x00);
				if(name=='sampler3') sendMidi(uniqueId,0x90,0,0x67,0x00);
				if(name=='sampler4') sendMidi(uniqueId,0x90,0,0x68,0x00);
				if(name=='sampler5') sendMidi(uniqueId,0x90,0,0x69,0x00);
				if(name=='sampler6') sendMidi(uniqueId,0x90,0,0x6A,0x00);
				if(name=='sampler7') sendMidi(uniqueId,0x90,0,0x6B,0x00);
				if(name=='sampler8') sendMidi(uniqueId,0x90,0,0x6C,0x00);
				// instead of Math.floor(19/4), parseInt(19/4) = 4 can also be used
			}else
			{
				if(name=='sampler1') sendMidi(uniqueId,0x90,0,0x65,0x7F);
				if(name=='sampler2') sendMidi(uniqueId,0x90,0,0x66,0x7F);
				if(name=='sampler3') sendMidi(uniqueId,0x90,0,0x67,0x7F);
				if(name=='sampler4') sendMidi(uniqueId,0x90,0,0x68,0x7F);
				if(name=='sampler5') sendMidi(uniqueId,0x90,0,0x69,0x7F);
				if(name=='sampler6') sendMidi(uniqueId,0x90,0,0x6A,0x7F);
				if(name=='sampler7') sendMidi(uniqueId,0x90,0,0x6B,0x7F);
				if(name=='sampler8') sendMidi(uniqueId,0x90,0,0x6C,0x7F);
				if(param2>0) samplerBlinkLedStatus[samplerIdx] = 2;else samplerBlinkLedStatus[samplerIdx] = 1;
			}
		}
	}	
}
//----------------------------------------------------------------------------- 
// this is called from time to time by the software (several times per second)
// (it is guaranteed that 'timerTick' will get executed ALWAYS right after notificationReceived)
var vuStatus=[[0,0,0,0,0],[0,0,0,0,0]];
var oldValueL = -60;
var oldValueR = -60;
	
var vuCurrent=[-54,-36,-20,-12,-6,-2];
function setVUValueForDeck(uniqueId, deck, dbValueL, dbValueR)
{
	var         baseLedML = 0x02;// MasterL
	var         baseLedMR = 0x03;// MasterR
	var newValueL = 0;
	var newValueR = 0;
	for(var i=0; i<6; i++){	
		if(dbValueL>=vuCurrent[i]) newValueL = i; //if the new value is greater or equal to the current stored value, mark for change.
		if(dbValueR>=vuCurrent[i]) newValueR = i; //if the new value is greater or equal to the current stored value, mark for change.
	}
	
	if(newValueL != oldValueL)
		sendMidi(uniqueId,176,0,baseLedML,newValueL*24);// ML VU Led
	
	if(newValueL != oldValueL)
		sendMidi(uniqueId,176,0,baseLedMR,newValueR*24);// MR VU Led
		
	oldValueL = newValueL;
	oldValueR = newValueR;
}

function timerTick(uniqueId)
{	
	for(var deck=0; deck<4; deck++)
	{		
		var dbValueL = getValue('vumeterMasterL', deck);// float dB		
		var dbValueR = getValue('vumeterMasterR', deck);// float dB		
		setVUValueForDeck(uniqueId, deck, dbValueL, dbValueR);
	}
}
function adjust_param_value(current_value,data2,max_value){
	var val1;
	if(data2==0x7F) val1 = current_value - 9;// to left
	if(data2==0x01) val1 = current_value + 9;// to right
	if(val1<0x00) val1 = 0x00;
	if(val1>max_value) val1 = max_value;
	return val1;
}
//----------------------------------------------------------------------------- 