//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2011 XYLIO INFO SRL. All Rights Reserved. --------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'American Audio VMS4.1'
//-----------------------------------------------------------------------------
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 "American Audio VMS4.1";
}
//-----------------------------------------------------------------------------
// 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 0xFFFF;//fake vid
}
//-----------------------------------------------------------------------------
function getSupportedMidiDevicePID(osId)
{
	return 0xFFFF;//fake pid
}
//-----------------------------------------------------------------------------
// 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 "000170010001";// 12 hex numbers for American Audio controllers SysEx ID

	// full string for 4.1: 000170010001000B050000
	//string for the 4: "0001700100"
}
//-----------------------------------------------------------------------------
// 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 VMS4.1";
	return false;
}
//-----------------------------------------------------------------------------
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==1) return "American Audio VMS4";
	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 ['+uniqueId+']');// this outputs a string to DebugView app on Win
	//sendMidi(uniqueId, status, channel, data1, data2);

	//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
		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
}
//-----------------------------------------------------------------------------
// 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+']');
	// set all leds off
	for(var i=12; i<=77; i++) sendMidi(uniqueId,0x90,0,i,0x00);
}
//-----------------------------------------------------------------------------
// some variables necessary only for this particular script
var loopingInBlinkLed=[-1,-1,-1,-1];// -1 means disabled
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
//-----------------------------------------------------------------------------
// 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;
		// 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
		// since we only support one mic we duplicate the functionality for both mics
		//case 0x14:
		//case 0x18: call('setMicGain', data2); break;
		//case 0x15:
		//case 0x19: call('setMicEqHigh', data2); break;
		//case 0x16:
		//case 0x1A: call('setMicEqMid', data2); break;
		//case 0x17:
		//case 0x1B: call('setMicEqLow', data2); break;

		// per deck
		// deck A
		case 0x08: call('setDeckEqLow', 0, data2); break;
		case 0x07: call('setDeckEqMid', 0, data2); break;
		case 0x06: call('setDeckEqHigh', 0, data2); break;
		case 0x09: call('setDeckVolume', 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', 0, jwDiff, 2730);else call('moveDeckJogWheel', 0, jwDiff, 2730);// 16384/6
			}
			break;

		//deck B
		case 0x0D: call('setDeckEqLow', 1, data2); break;
		case 0x0C: call('setDeckEqMid', 1, data2); break;
		case 0x0B: call('setDeckEqHigh', 1, data2); break;
		case 0x0E: call('setDeckVolume', 1, data2); break;
		case 0x0A: 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', 1, jwDiff, 2730);else call('moveDeckJogWheel', 1, jwDiff, 2730);// 16384/6
			}
			break;

		//deck C
		case 0x03: call('setDeckEqLow', 2, data2); break;
		case 0x01: call('setDeckEqHigh', 2, data2); break;
		case 0x02: call('setDeckEqMid', 2, data2); break;
		case 0x04: call('setDeckVolume', 2, data2); break;
		case 0x00: call('setDeckGain', 2, data2); break;

		//deck D
		case 0x12: call('setDeckEqLow', 3, data2); break;
		case 0x11: call('setDeckEqMid', 3, data2); break;
		case 0x10: call('setDeckEqHigh', 3, data2); break;
		case 0x13: call('setDeckVolume', 3, data2); break;
		case 0x0F: call('setDeckGain', 3, data2); break;

		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	//case 0x81:
	//case 0x91:
		//if((status==0x81)&&(data2==0x40)) data2 = 0;
		//switch(data1)
		//{
		//case 0x27: call('deckAnalogIn', 0, data2); break;
		//case 0x49: call('deckAnalogIn', 1, data2); 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 0x29: if(data2>0x3F) call('lbMoveCursor', -1); break;
		//case 0x2A: if(data2>0x3F) call('lbMoveCursor', +1); break;
		//case 0x2B: if(data2>0x3F) call('lbListSelect', 1); break;
		//case 0x2C: if(data2>0x3F) call('lbBrowserSelect', 1); break;
		//case 0x2D: if(data2>0x3F){ jogWheelsInScratchMode = (jogWheelsInScratchMode+1)%2; sendMidi(uniqueId,0x90,0,0x2D,(jogWheelsInScratchMode ? 0x7F : 0x00));} break;
		//case 0x4C: if(data2==0x7F) call('setMicOnAir', 1);else call('setMicOnAir', 0); break;// it's a switch (0x40 off, 0x7F on)
		//case 0x4D: if(data2==0x7F) call('setMicTalkOver', 1);else call('setMicTalkOver', 0); break;// it's a switch (0x40 off, 0x7F on)
		// per deck
		// deck A
		case 0x1F: call('deckLoopInEnd', 0, data2); break;
		case 0x20: call('deckLoopExit', 0, 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 0x22: call('deckLoop4', 0, data2); break;
		case 0x0F: call('deckBendDown', 0, data2); sendMidi(uniqueId,0x90,0,15,(data2 ? 0x7F : 0x00)); break;
		case 0x10: call('deckBendUp', 0, data2); sendMidi(uniqueId,0x90,0,16,(data2 ? 0x7F : 0x00)); break;
		case 0x28: if(channel==1) call('deckLoopLess', 0, data2);else
					call('deckSearchLeft', 0, data2); sendMidi(uniqueId,0x90,0,40,(data2 ? 0x7F : 0x00)); break;
		case 0x29: if(channel==1) call('deckLoopMore', 0, data2);else
					call('deckSearchRight', 0, data2); sendMidi(uniqueId,0x90,0,41,(data2 ? 0x7F : 0x00)); break;
		case 0x27: if(channel==1) call('deckKeyLock', 0, data2);else
					if(data2>0x3F){ jogWheelsInScratchMode = (jogWheelsInScratchMode+1)%2;}
					break;
		case 0x0C: call('deckCue', 0, data2); break;
		case 0x0D: call('deckPlayPause', 0, data2); break;
		case 0x0E: call('deckCuePlay', 0, 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('deckCuePos4', 0, data2); break;
		case 0x17: call('deckCuePos5', 0, data2); break;
		case 0x18: call('deckCuePos6', 0, data2); break;
		case 0x19: call('deckCuePos7', 0, data2); break;
		case 0x1A: call('deckCuePos8', 0, data2); break;
		case 0x11: call('deckSync', 0, data2); break;
		case 0x2A: call('deckMonitor', 0, data2); break;
		case 0x65: if(data2>0x3F) call('deckLBLoad', 0, 0); break;// load deck A
		//case 0x13: call('deckAnalogIn', 0, data2); break;
		//case 0x14: loopEffectsMode[0] = data2; break;

		// deck B
		case 0x41: call('deckLoopInEnd', 1, data2); break;
		case 0x42: call('deckLoopExit', 1, data2); break;
		//case 0x43: call('deckLoopReloop', 1, 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 0x44: call('deckLoop4', 1, data2); break;
		//case 0x19: call('deckFxFlanger', 1, data2); break;
		//case 0x1A: call('deckFxEcho', 1, data2); break;
		//case 0x1B: call('deckFxDelay', 1, data2); break;
		//case 0x1C: call('deckFxReverb', 1, data2); break;
		case 0x31: call('deckBendDown', 1, data2); sendMidi(uniqueId,0x90,0,15+34,(data2 ? 0x7F : 0x00)); break;
		case 0x32: call('deckBendUp', 1, data2); sendMidi(uniqueId,0x90,0,16+34,(data2 ? 0x7F : 0x00)); break;
		case 0x4A: if(channel==1) call('deckLoopLess', 1, data2);else
					call('deckSearchLeft', 1, data2); sendMidi(uniqueId,0x90,0,40+34,(data2 ? 0x7F : 0x00)); break;
		case 0x4B: if(channel==1) call('deckLoopMore', 1, data2);else
					call('deckSearchRight', 1, data2); sendMidi(uniqueId,0x90,0,41+34,(data2 ? 0x7F : 0x00)); break;
		case 0x49: if(channel==1) call('deckKeyLock', 1, data2);else
					if(data2>0x3F){ jogWheelsInScratchMode = (jogWheelsInScratchMode+1)%2;}
					break;
		case 0x2E: call('deckCue', 1, data2); break;
		case 0x2F: call('deckPlayPause', 1, data2); break;
		case 0x30: call('deckCuePlay', 1, 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('deckCuePos4', 1, data2); break;
		case 0x39: call('deckCuePos5', 1, data2); break;
		case 0x3A: call('deckCuePos6', 1, data2); break;
		case 0x3B: call('deckCuePos7', 1, data2); break;
		case 0x3C: call('deckCuePos8', 1, data2); break;
		case 0x33: call('deckSync', 1, data2); break;
		//case 0x2B: call('deckMonitor', 1, data2); break;
		case 0x66: if(data2>0x3F) call('deckLBLoad', 1, 0); break;// load deck B
		//case 0x27: call('deckAnalogIn', 1, data2); break;
		//case 0x28: loopEffectsMode[1] = data2; break;

		//deck C
		//case 0x2C: call('deckMonitor', 2, data2); break;

		//deck D
		//case 0x2D: call('deckMonitor', 3, 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==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(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
	if(name=='cuepos5')		sendMidi(uniqueId,0x90,0,23+d,(param1 ? 0x7F : 0x00));// blue
	if(name=='cuepos6')		sendMidi(uniqueId,0x90,0,24+d,(param1 ? 0x7F : 0x00));// blue
	if(name=='cuepos7')		sendMidi(uniqueId,0x90,0,25+d,(param1 ? 0x7F : 0x00));// blue
	if(name=='cuepos8')		sendMidi(uniqueId,0x90,0,26+d,(param1 ? 0x7F : 0x00));// blue

	if(name=='loopIn')
	{
		sendMidi(uniqueId,0x90,0,31+d,(param1 ? 0x7F : 0x00));// Loop In Red (on or off)
		// 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,31+d,0x00);// Loop In Red (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)
}
//-----------------------------------------------------------------------------
// 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+']');
	for(var i=0; i<2; i++)
	{
		// loop in led
		if(loopingInBlinkLed[i]!=-1)
		{
			// actually set the led
			sendMidi(uniqueId,0x90,0,31+(i*34),((loopingInBlinkLed[i]<2) ? 0x7F : 0x00));// Loop In Red (on or off)
			loopingInBlinkLed[i]++;
			if(loopingInBlinkLed[i]>=4) loopingInBlinkLed[i] = 0;
		}
	}
}
//-----------------------------------------------------------------------------
