//-----------------------------------------------------------------------------
//------------ Copyright (c)2003-2018 Xylio. All Rights Reserved. -------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'American Audio VMS2'
//-----------------------------------------------------------------------------
function getLanguageVersion()
{
	return 1;// do not modify this, this should be 1 for all scripts
}
//-----------------------------------------------------------------------------
function getFileVersion()
{
	return 2;// update this as needed
}
//-----------------------------------------------------------------------------
// return a user friendly name for the controller supported
function getControllerUserFriendlyName()
{
	return "American Audio VMS2";
}
//-----------------------------------------------------------------------------
// 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 0x04d8;// Microchip Technology Inc.
}
//-----------------------------------------------------------------------------
function getSupportedMidiDevicePID(osId)
{
	return 0x0a0b;// VMS2 AUDIO MIDI Device
}
//-----------------------------------------------------------------------------
// 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 "000170010002";
	// VMS2 full sysex reply: F0 7E 7F 06 02 00 01 70 01 00 02 00 05 02 00 00 F7
	// VMS4 full sysex reply: F0 7E 7F 06 02 00 01 70 01 00 01 00 0B 03 00 00 F7
}

//-----------------------------------------------------------------------------
// this is NOT the preferred way to identify the controller but you should use
// it if the controller does not support standard MIDI SysEx identification
// return false if the controller does not support this kind of identification
// osId -- 0 Windows; 1 MacOSX; 2 Linux; -1 unknown
function getSupportedMidiDeviceNameForInput(osId)
{
	//if(osId==1) return "American Audio VMS2";
	return false;
}
//-----------------------------------------------------------------------------
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==1) return "American Audio VMS2";
	return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// 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 VMS2 ['+uniqueId+']');// this outputs a string to DebugView app on Win
	//sendMidi(uniqueId, status, channel, data1, data2);

	// setting audio (the actual routing is done automatically for this controller so
	// we don't need to manually set "RoutingOutHeadphones", "RoutingOutDeckA", etc
	// ATTN: we first try to set ASIO mode, and only then regular CoreAudio, DirectSound audio/WDM/Wasapi
	if(setAudioDevice("ADJ ASIO|||VMS2 AUDIO MIDI Device|||Speakers (VMS2 AUDIO MIDI Device)|||Speakers (VMS2 AUDIO MIDI)|||Speakers (ADJ)"))
	{
		//debug('AudioDevice was set OK');
		setSetting('AudioDevice2', "NO AUDIO");
	}

	//deck A
	for(var deck=0; deck<2; deck++)
	{
		var i=0;
		if(deck==1) i=34;// 46 - 12 (cue code from deck B - cue code from deck A)
		//sendMidi(uniqueId,0x90,0,12+i,(getValue('cue', deck) ? 0x7F : 0x00)); //Cue
		//sendMidi(uniqueId,0x90,0,13+i,(getValue('play', deck) ? 0x7F : 0x00)); //Play/Pause A
		sendMidi(uniqueId,0x90,0,14+i, 0x00);// pause(cueplay)
		sendMidi(uniqueId,0x90,0,15+i, 0x00);// pitch bend -
		sendMidi(uniqueId,0x90,0,16+i, 0x00);// pitch bend +
		sendMidi(uniqueId,0x90,0,17+i, 0x00);// sync red
		sendMidi(uniqueId,0x90,0,18+i, (getValue('cuepos1', deck) ? 0x7F : 0x00));// hot cue 1
		sendMidi(uniqueId,0x90,0,19+i, (getValue('cuepos2', deck) ? 0x7F : 0x00));// hot cue 2
		sendMidi(uniqueId,0x90,0,20+i, (getValue('cuepos3', deck) ? 0x7F : 0x00));// hot cue 3
		sendMidi(uniqueId,0x90,0,21+i, (getValue('cuepos4', deck) ? 0x7F : 0x00));// hot cue 4
		sendMidi(uniqueId,0x90,0,22+i, 0x00);// sync blue
		sendMidi(uniqueId,0x90,0,23+i, (getValue('cuepos5', deck) ? 0x7F : 0x00));// hot cue 5
		sendMidi(uniqueId,0x90,0,24+i, (getValue('cuepos6', deck) ? 0x7F : 0x00));// hot cue 6
		sendMidi(uniqueId,0x90,0,25+i, (getValue('cuepos7', deck) ? 0x7F : 0x00));// hot cue 7
		sendMidi(uniqueId,0x90,0,26+i, (getValue('cuepos8', deck) ? 0x7F : 0x00));// hot cue 8
		call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
		sendMidi(uniqueId,0x90,0,27+i, 0x00);// fx on/off
		sendMidi(uniqueId,0x90,0,28+i, 0x00);// fx param
		sendMidi(uniqueId,0x90,0,29+i, 0x00);// sample play
		sendMidi(uniqueId,0x90,0,30+i, 0x00);// sample rec
		sendMidi(uniqueId,0x90,0,31+i, 0x00);// Loop In Red
		//sendMidi(uniqueId,0x90,0,32+i, (getValue('looping', deck) ? 0x7F : 0x00));// Loop Out Red
		//sendMidi(uniqueId,0x90,0,33+i, (getValue('looping', deck) ? 0x7F : 0x00));// Reloop Red
		sendMidi(uniqueId,0x90,0,34+i, 0x00);// Loop More Red
		sendMidi(uniqueId,0x90,0,35+i, 0x00);// Loop In Blue
		sendMidi(uniqueId,0x90,0,36+i, 0x00);// Loop Out Blue
		sendMidi(uniqueId,0x90,0,37+i, 0x00);// Reloop Blue
		sendMidi(uniqueId,0x90,0,38+i, 0x00);// Loop More Blue
		//sendMidi(uniqueId,0x90,0,39+i, (getValue('keyLock', deck) ? 0x7F : 0x00));// Vinyl (KeyLock)
		sendMidi(uniqueId,0x90,0,40+i, 0x00);// Search << (bpm/2)
		sendMidi(uniqueId,0x90,0,41+i, 0x00);// Search >> (bpm*2)
		call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
	}
	// global (not pertaining to a specific deck)
	sendMidi(uniqueId,0x90,0,42, 0x00);// Cue MidiLog 1 (not working?)
	sendMidi(uniqueId,0x90,0,43, 0x00);// Cue MidiLog 2 (not working?)
	sendMidi(uniqueId,0x90,0,44, 0x00);// Cue MidiLog 3 (not working?)
	sendMidi(uniqueId,0x90,0,45, 0x00);// Cue MidiLog 4 (not working?)
	sendMidi(uniqueId,0x90,0,76, 0x00);// Mic 1
	sendMidi(uniqueId,0x90,0,77, 0x00);// Mic 2
	//------------------------------------
	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 VMS2 ['+uniqueId+']');
	//------------------------------------
	callSendAllNotifications(uniqueId, false);// send all notifications to the controller (so that we don't have to manually de-init all leds/lcd screens)
	// set all leds off
	for(var i=12; i<=77; i++)
	{
		if(i % 10 == 0) call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
		sendMidi(uniqueId,0x90,0,i,0x00);
	}
}
//-----------------------------------------------------------------------------
// some variables necessary only for this particular script
var bReloopLastAction=[-1,-1,-1,-1];// used to properly simulate button press and release
var jogWheelsInScratchMode=0;
var jogWheelsLastMostSignificant=[-1,-1,-1,-1];// not a valid position
var jogWheelsLastValue=[-1,-1,-1,-1];// start with non-valid values
var lastCursorValue=0;
//-----------------------------------------------------------------------------
// this is your main target function to implement the controller -- this is called
// for each and every MIDI message that comes from the controller itself
// return true if the message was processed by the script and false to let it
// pass thru the software (eg. so it can be assigned by the user using the LEARN function)
function messageReceived(uniqueId,status,channel,data1,data2)
{
	var messageNotProcessed=false;
	switch(status)
	{
	//-----------------------------------------------
	// controllers: pitch sliders ('moving' things)
	case 0xE0:
		// attn: we're only using 7bit
		if(channel==0) call('setDeckPitch', 0, 0x7F-data2);
		if(channel==1) call('setDeckPitch', 1, 0x7F-data2);
		if(channel==4) call('setDeckPitch', 2, 0x7F-data2);
		if(channel==5) call('setDeckPitch', 3, 0x7F-data2);
		break;
	//-----------------------------------------------
	// controllers: sliders, knobs, jog-wheels, encoders ('moving' things)
	case 0xB0:
		switch(data1)
		{
		// global
		case 0x1F: call('setCrossfader', 0x7F-data2); break;
		//case 0x1C: call('setMasterGain', data2); break;
		case 0x1C: call('setMasterVolume', data2); break;
		//case 0x1E: call('setBoothVolume', data2); break;
		case 0x34:
			var tmpData = data2 - lastCursorValue;
			if(tmpData<-0x40) tmpData = 1;
			if(tmpData>0x40) tmpData = -1;
			call('lbMoveCursor', tmpData);
			lastCursorValue = data2;
			break;
		// controls on the DJ side/edge
		case 0x21: call('setCFCurve', data2); break;// CF curve
		case 0x22: call('setCuePgmMix', data2); break;// cue mix
		//case 0x23: call('setMonitorGain', data2); break;// headphones gain
		case 0x23: call('setMonitorVolume', data2); break;// headphones volume
		// since we only support one mic we duplicate the functionality for both mics
		case 0x18: call('setMicGain', data2); break;
		//case 0x19: call('setMicEqHigh', data2); break;
		//case 0x1A: call('setMicEqMid', data2); break;
		//case 0x1B: call('setMicEqLow', data2); break;

		// per deck
		// deck A
		case 0x01: call('setDeckEqHigh', 0, data2); break;
		case 0x02: call('setDeckEqMid', 0, data2); break;
		case 0x03: call('setDeckEqLow', 0, data2); break;
		case 0x04: call('setDeckVolume', 0, data2); break;
		//case 0x28: call('setDeckCutoff', 0, data2); break;
		case 0x00: call('setDeckGain', 0, data2); break;
		// jog-wheel
		case 0x31: //  most-significant 0x00-0x7F jog-wheel
			var deck = 0;
			jogWheelsLastMostSignificant[deck] = data2;
			break;
		case 0x30: // least-significant 0x00-0x7F jog-wheel
			var deck = 0;
			var val1 = 128 * jogWheelsLastMostSignificant[deck] + data2;// 0..16383 [128*128 = 16384 positions]
			if(jogWheelsLastValue[deck]==-1)
			{
				jogWheelsLastValue[deck] = val1;
			}else
			{
				var jwDiff = val1 - jogWheelsLastValue[deck];// new - old
				if(jwDiff<-8192) jwDiff = jwDiff + 16384;// correct if it wraps arround (cw)
				if(jwDiff> 8192) jwDiff = jwDiff - 16384;// correct if it wraps arround (ccw) -- 0x16 *128 + 0x3F
				//debug('wheel old='+jogWheelsLastValue[deck]+' new='+val1+' jwDiff='+jwDiff);
				// save new pos
				jogWheelsLastValue[deck] = val1;
				// params: (deck, increment, total positions per complete revolution)
				if(jogWheelsInScratchMode) call('scratchDeckJW', deck, jwDiff, 2730);else call('moveDeckJogWheel', deck, jwDiff, 2730);// 16384/6
			}
			break;

		//deck B
		case 0x08: call('setDeckEqLow', 1, data2); break;
		case 0x07: call('setDeckEqMid', 1, data2); break;
		case 0x06: call('setDeckEqHigh', 1, data2); break;
		case 0x09: call('setDeckVolume', 1, data2); break;
		//case 0x2D: call('setDeckCutoff', 1, data2); break;
		case 0x05: call('setDeckGain', 1, data2); break;
		// jog-wheel
		case 0x33: //  most-significant 0x00-0x7F jog-wheel
			var deck = 1;
			jogWheelsLastMostSignificant[deck] = data2;
			break;
		case 0x32: // least-significant 0x00-0x7F jog-wheel
			var deck = 1;
			var val1 = 128 * jogWheelsLastMostSignificant[deck] + data2;// 0..16383 [128*128 = 16384 positions]
			if(jogWheelsLastValue[deck]==-1)
			{
				jogWheelsLastValue[deck] = val1;
			}else
			{
				var jwDiff = val1 - jogWheelsLastValue[deck];// new - old
				if(jwDiff<-8192) jwDiff = jwDiff + 16384;// correct if it wraps arround (cw)
				if(jwDiff> 8192) jwDiff = jwDiff - 16384;// correct if it wraps arround (ccw) -- 0x16 *128 + 0x3F
				//debug('wheel old='+jogWheelsLastValue[deck]+' new='+val1+' jwDiff='+jwDiff);
				// save new pos
				jogWheelsLastValue[deck] = val1;
				// params: (deck, increment, total positions per complete revolution)
				if(jogWheelsInScratchMode) call('scratchDeckJW', deck, jwDiff, 2730);else call('moveDeckJogWheel', deck, jwDiff, 2730);// 16384/6
			}
			break;


		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		if((status==0x80)&&(data2==0x40)) data2 = 0;
		switch(data1)
		{
		// global
		//case 0x2B: if(data2>0x3F) call('lbListSelect', 1); break;
		case 0x65: if(data2>0x3F) call('deckLBLoad', 0 + channel*2, data2); break;// load deck A/C (with Shift)
		case 0x66: if(data2>0x3F) call('deckLBLoad', 1 + channel*2, data2); break;// load deck B/D (with Shift)
		//case 0x67: if(data2>0x3F) call('deckLBLoad', 0, data2); break;// left
		//case 0x68: if(data2>0x3F) call('deckLBLoad', 1, data2); break;// right
		case 0x67: if(data2>0x3F) call('lbBrowserSelect', data2); break;// left
		case 0x68: if(data2>0x3F) call('lbListSelect', data2); break;// right
		case 0x69: if(data2>0x3F) call('lbMoveCursor', -1); break;// up
		case 0x6A: if(data2>0x3F) call('lbMoveCursor', +1); break;// down
		case 0x6B: if(data2>0x3F) call('lbToggleSelect', data2); break;// encoder top press/button

		// per deck
		// deck A
		case 0x1F: call('deckLoopInEnd', 0, data2); break;
		case 0x23: call('deckLoopInEnd', 2, data2); break;
		case 0x20: call('deckLoopExit', 0, data2); break;
		case 0x24: call('deckLoopExit', 2, data2); break;
		//case 0x21: call('deckLoopReloop', 0, data2); break;
		case 0x21:
			var deck = 0;
			var looping = getValue('looping', deck);
			if(bReloopLastAction[deck]==-1) // if no action was already started
			{
				if(looping)
				{
					bReloopLastAction[deck] = 1;
					call('deckLoopExit', deck, data2);
				}else
				{
					bReloopLastAction[deck] = 0;
					call('deckLoopReloop', deck, data2);
				}
			}else
			{
				if(bReloopLastAction[deck])
					call('deckLoopExit', deck, data2);
				else
					call('deckLoopReloop', deck, data2);
				// reset
				bReloopLastAction[deck] = -1;
			}
			break;
		case 0x25:
			var deck = 2;
			var looping = getValue('looping', deck);
			if(bReloopLastAction[deck]==-1) // if no action was already started
			{
				if(looping)
				{
					bReloopLastAction[deck] = 2;
					call('deckLoopExit', deck, data2);
				}else
				{
					bReloopLastAction[deck] = 0;
					call('deckLoopReloop', deck, data2);
				}
			}else
			{
				if(bReloopLastAction[deck])
					call('deckLoopExit', deck, data2);
				else
					call('deckLoopReloop', deck, data2);
				// reset
				bReloopLastAction[deck] = -1;
			}
			break;
		case 0x22: call('deckLoopMore', 0, data2); break;
		case 0x26: call('deckLoopMore', 2, data2); break;
		case 0x0F:
			if(channel==0) {call('deckBendDown', 0, data2); sendMidi(uniqueId,0x90, 0, 0x0F,(data2 ? 0x7F : 0x00));}
			if(channel==1) {call('deckBendDown', 2, data2); sendMidi(uniqueId,0x90, 0, 0x0F,(data2 ? 0x7F : 0x00));}
			break;
		case 0x10:
			if(channel==0) {call('deckBendUp', 0, data2); sendMidi(uniqueId,0x90, 0, 0x10,(data2 ? 0x7F : 0x00));}
			if(channel==1) {call('deckBendUp', 2, data2); sendMidi(uniqueId,0x90, 0, 0x10,(data2 ? 0x7F : 0x00));}
			break;
		case 0x28:
			if(channel==0) {call('deckSearchLeft', 0, data2); sendMidi(uniqueId,0x90, 0, 0x28,(data2 ? 0x7F : 0x00));}
			if(channel==1) {call('deckSearchLeft', 2, data2); sendMidi(uniqueId,0x90, 0, 0x28,(data2 ? 0x7F : 0x00));}
			break;
		case 0x29:
			if(channel==0) {call('deckSearchRight', 0, data2); sendMidi(uniqueId,0x90, 0, 0x29,(data2 ? 0x7F : 0x00));}
			if(channel==1) {call('deckSearchRight', 2, data2); sendMidi(uniqueId,0x90, 0, 0x29,(data2 ? 0x7F : 0x00));}
			break;
		case 0x27:
			if(channel==0) call('deckKeyLock', 0, data2);
			if(channel==1) call('deckAnalogIn', 0, data2);
			break;
		case 0x0C:
			if(channel==0) call('deckCue', 0, data2);
			if(channel==1) call('deckCue', 2, data2);
			break;
		case 0x0D:
			if(channel==0) call('deckPlayPause', 0, data2);
			if(channel==1) call('deckPlayPause', 2, data2);
			break;
		case 0x0E:
			if(channel==0) call('deckCuePlay', 0, data2);
			if(channel==1) call('deckCuePlay', 2, data2);
			break;
		case 0x12: call('deckCuePos1', 0, data2); break;
		case 0x13: call('deckCuePos2', 0, data2); break;
		case 0x14: call('deckCuePos3', 0, data2); break;
		case 0x15: call('deckCuePos1', 2, data2); break;
		case 0x17: call('deckCuePos2', 2, data2); break; //cue1 deck C
		case 0x18: call('deckCuePos3', 2, data2); break; //cue2 deck C
		case 0x11: call('deckSync', 0, data2); break;
		case 0x16: call('deckSync', 2, data2); break; //sync deck C
		case 0x2A: call('deckMonitor', 0, data2); break;
		//case 0x13: call('deckAnalogIn', 0, data2); break;
		//case 0x14: loopEffectsMode[0] = data2; break;
		case 0x4F: call('holdDeckJogWheel', 0, data2); break;

		// deck B
		case 0x41: call('deckLoopInEnd', 1, data2); break;
		case 0x45: call('deckLoopInEnd', 3, data2); break;
		case 0x42: call('deckLoopExit', 1, data2); break;
		case 0x46: call('deckLoopExit', 3, data2); break;
		case 0x43:
			var deck = 1;
			var looping = getValue('looping', deck);
			if(bReloopLastAction[deck]==-1) // if no action was already started
			{
				if(looping)
				{
					bReloopLastAction[deck] = 1;
					call('deckLoopExit', deck, data2);
				}else
				{
					bReloopLastAction[deck] = 0;
					call('deckLoopReloop', deck, data2);
				}
			}else
			{
				if(bReloopLastAction[deck])
					call('deckLoopExit', deck, data2);
				else
					call('deckLoopReloop', deck, data2);
				// reset
				bReloopLastAction[deck] = -1;
			}
			break;
		case 0x47:
			var deck = 3;
			var looping = getValue('looping', deck);
			if(bReloopLastAction[deck]==-1) // if no action was already started
			{
				if(looping)
				{
					bReloopLastAction[deck] = 3;
					call('deckLoopExit', deck, data2);
				}else
				{
					bReloopLastAction[deck] = 0;
					call('deckLoopReloop', deck, data2);
				}
			}else
			{
				if(bReloopLastAction[deck])
					call('deckLoopExit', deck, data2);
				else
					call('deckLoopReloop', deck, data2);
				// reset
				bReloopLastAction[deck] = -1;
			}
			break;
		case 0x44: call('deckLoopMore', 1, data2); break;
		case 0x48: call('deckLoopMore', 3, data2); break;
		case 0x31:
			if(channel==0) {call('deckBendDown', 1, data2); sendMidi(uniqueId,0x90, 0, 0x31,(data2 ? 0x7F : 0x00));}
			if(channel==1) {call('deckBendDown', 3, data2); sendMidi(uniqueId,0x90, 0, 0x31,(data2 ? 0x7F : 0x00));}
			break;
		case 0x32:
			if(channel==0) {call('deckBendUp', 1, data2); sendMidi(uniqueId,0x90, 0, 0x32,(data2 ? 0x7F : 0x00));}
			if(channel==1) {call('deckBendUp', 3, data2); sendMidi(uniqueId,0x90, 0, 0x32,(data2 ? 0x7F : 0x00));}
			break;
		case 0x4A:
			if(channel==0) {call('deckSearchLeft', 1, data2); sendMidi(uniqueId,0x90, 0, 0x4A,(data2 ? 0x7F : 0x00));}
			if(channel==1) {call('deckSearchLeft', 3, data2); sendMidi(uniqueId,0x90, 0, 0x4A,(data2 ? 0x7F : 0x00));}
			break;
		case 0x4B:
			if(channel==0) {call('deckSearchRight', 1, data2); sendMidi(uniqueId,0x90, 0, 0x4B,(data2 ? 0x7F : 0x00));}
			if(channel==1) {call('deckSearchRight', 3, data2); sendMidi(uniqueId,0x90, 0, 0x4B,(data2 ? 0x7F : 0x00));}
			break;
		case 0x49:
			if(channel==0) call('deckKeyLock', 1, data2);
			if(channel==1) call('deckAnalogIn', 1, data2);
			break;
		case 0x2E:
			if(channel==0) call('deckCue', 1, data2);
			if(channel==1) call('deckCue', 3, data2);
			break;
		case 0x2F:
			if(channel==0) call('deckPlayPause', 1, data2);
			if(channel==1) call('deckPlayPause', 3, data2);
			break;
		case 0x30:
			if(channel==0) call('deckCuePlay', 1, data2);
			if(channel==1) call('deckCuePlay', 3, data2);
			break;
		case 0x34: call('deckCuePos1', 1, data2); break;
		case 0x35: call('deckCuePos2', 1, data2); break;
		case 0x36: call('deckCuePos3', 1, data2); break;
		case 0x37: call('deckCuePos1', 3, data2); break;
		case 0x39: call('deckCuePos2', 3, data2); break;
		case 0x3A: call('deckCuePos3', 3, data2); break;
		case 0x33: call('deckSync', 1, data2); break;
		case 0x38: call('deckSync', 3, data2); break;
		case 0x2B: call('deckMonitor', 1, data2); break;
		//case 0x27: call('deckAnalogIn', 1, data2); break;
		//case 0x28: loopEffectsMode[1] = data2; break;
		case 0x50: call('holdDeckJogWheel', 1, data2); 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 d=0; if(deck%2==1) d=34;// 46 - 12 (cue code from deck B - cue code from deck A)
	if(name=='cue') 		sendMidi(uniqueId,0x90,0,12+d,(param1 ? 0x7F : 0x00));
	if(name=='play') 		sendMidi(uniqueId,0x90,0,13+d,(param1 ? 0x7F : 0x00));
	if(name=='onbeat') 		sendMidi(uniqueId,0x90,0,17+d,(param1 ? 0x7F : 0x00));// 17 for red; 22 for blue;
	if(deck<2)
	{
		if(name=='cuepos1')	sendMidi(uniqueId,0x90,0,18+d,(param1 ? 0x7F : 0x00));// red
		if(name=='cuepos2')	sendMidi(uniqueId,0x90,0,19+d,(param1 ? 0x7F : 0x00));// red
		if(name=='cuepos3')	sendMidi(uniqueId,0x90,0,20+d,(param1 ? 0x7F : 0x00));// red
		//if(name=='cuepos4')	sendMidi(uniqueId,0x90,0,21+d,(param1 ? 0x7F : 0x00));// red
	}else
	{
		if(name=='cuepos1')	sendMidi(uniqueId,0x90,0,23+d,(param1 ? 0x7F : 0x00));// blue
		if(name=='cuepos2')	sendMidi(uniqueId,0x90,0,24+d,(param1 ? 0x7F : 0x00));// blue
		if(name=='cuepos3')	sendMidi(uniqueId,0x90,0,25+d,(param1 ? 0x7F : 0x00));// blue
		//if(name=='cuepos4')	sendMidi(uniqueId,0x90,0,26+d,(param1 ? 0x7F : 0x00));// blue
	}

	if(name=='loopBlink')
	{
		// buttons leds
		sendMidi(uniqueId,0x90,0,31+d,(param1 ? 0x7F : 0x00));// Loop In Red (on or off)
		sendMidi(uniqueId,0x90,0,32+d,(param1 ? 0x7F : 0x00));// Loop Out Red (on or off)
		//sendMidi(uniqueId,0x90,0,33+d,(param1 ? 0x7F : 0x00));// Reloop Red (on or off)
		//sendMidi(uniqueId,0x90,0,34+d,0x00);// Loop Red (off)
	}

	if(name=='keyLock')		sendMidi(uniqueId,0x90,0,39+d,(param1 ? 0x7F : 0x00));// red -- Vinyl (KeyLock)

	// justLoaded: 1,2 (loading in progress), 4 (loading ended), 8 (progress), 16 (detection started), 32 (detection finished);
	if(name=='justLoaded')
	{
		if(param1!=0 && param1!=32)
		{
			// loading
			sendMidi(uniqueId,0x90,0,(deck%2==0)?0x65:0x66,0x7F);// on
		}else
		{
			// loading finished
			sendMidi(uniqueId,0x90,0,(deck%2==0)?0x65:0x66,0x00);// off
		}
	}
}
//-----------------------------------------------------------------------------
// this is called from time to time by the software (several times per second)
// (it is guaranteed that 'timerTick' will get executed ALWAYS right after notificationReceived)
function timerTick(uniqueId)
{
	//debug('timerTick ['+uniqueId+']');
}
//-----------------------------------------------------------------------------
