//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2013 XYLIO INFO SRL. All Rights Reserved. --------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Reloop Digital Jockey 2 CE/IE' (script v2)
//----------------------------------------------------------------------------- 
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 "Reloop Digital Jockey 2 CE/IE";
}
//----------------------------------------------------------------------------- 
// 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 0x0A4A;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDevicePID(osId)
{
	return 0xAA06;// Reloop Digital Jockey 2 IE
}
//----------------------------------------------------------------------------- 
// 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 "4850020200050003";// IE
	//F0 7E 00 06 02 48 50 02 02 00 05 00 03 00 00 00 F7 // full sysex id reply for IE
}
//----------------------------------------------------------------------------- 
function getSupportedMidiSysExIdString2(osId)
{
	return "485002020005000207";// CE
}
//----------------------------------------------------------------------------- 
// 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 "Digital Jockey -IE2-";
//	return "Digital Jockey - IE2";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForInput2(osId)
{
	//return "Reloop IE2";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
//	return "Digital Jockey -IE2-";
//	return "Digital Jockey - IE2";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput2(osId)
{
	//return "Reloop IE2";
	return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var loopingInBlinkLed=[-1,-1,-1,-1];// -1 means disabled
var jogWheelsInSearchMode=[0,0,0,0];// this has priority over scratch
var jogWheelsInScratchMode=[0,0,0,0];
//----------------------------------------------------------------------------- 
// 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 Reloop Digital Jockey 2 CE/IE ['+uniqueId+']');// this outputs a string to DebugView app on Win
	// manually set some of the leds (the ones that don't have notifications)
	for(var i=0; i<2; i++)
	{
		sendMidi(uniqueId,0x90,0,(i==0?0x1A:0x56),(jogWheelsInSearchMode[i] ? 0x7F : 0x00));// search mode
		sendMidi(uniqueId,0x90,0,(i==0?0x1B:0x57),(jogWheelsInScratchMode[i] ? 0x7F : 0x00));// scratch mode
	}
	//------------------------------------
	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 Reloop Digital Jockey 2 CE/IE ['+uniqueId+']');
	// manually let some of the leds (the ones that don't have notifications)
	for(var i=0; i<2; i++)
	{
		sendMidi(uniqueId,0x90,0,(i==0?0x1A:0x56),0x00);// search mode
		sendMidi(uniqueId,0x90,0,(i==0?0x1B:0x57),0x00);// scratch mode
	}
	//------------------------------------
	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 messageNotProcessed=false;
	switch(status)
	{
	//-----------------------------------------------
	// controllers: pitch sliders ('moving' things)
	case 0xE0:
		// attn: we're only using 7bit	
		if(channel==0) call('setDeckPitch', 0, data2);
		if(channel==1) call('setDeckPitch', 1, data2);
		break;
	//-----------------------------------------------
	// controllers: sliders, knobs, jog-wheels, encoders ('moving' things)
	case 0xB0:
		switch(data1)
		{
		//global
		case 0x0F: call('setMasterGain', data2); break;
		case 0x10: call('setMonitorGain', data2); break;
		case 0x11: call('setCuePgmMix', data2); break;
		case 0x12: call('setCrossfader', data2); break;
		case 0x13: call('lbMoveCursor', data2-0x40); break;
		case 0x15: call('setCFCurve', data2); break;

		//deck A
		case 0x01: call('setDeckPitch', 0, data2); break;
		case 0x02: call('setDeckGain', 0, data2); break;
		case 0x03: call('setDeckEqHigh', 0, data2); break;
		case 0x04: call('setDeckEqMid', 0, data2); break;
		case 0x05: call('setDeckEqLow', 0, data2); break;
		case 0x06: call('setDeckVolume', 0, data2); break;

		case 0x0B: // params: (deck, increment, total positions per complete revolution) 
		case 0x0E: // when fx dry/wet is activated
			var val1 = data2-0x40;
			call('moveDeckJogWheel', 0, val1, 150);
			/*
			if(jogWheelsInSearchMode[0])
			{
				call('searchDeckJW', 0, val1, 150);
			}else
			{
				if(jogWheelsInScratchMode[0]) 
					call('scratchDeckJW', 0, val1, 150);
				else 
					call('moveDeckJogWheel', 0, val1, 150);
			}
			*/
			break;
		case 0x0C: // scratch only
			var val1 = data2-0x40;
			call('scratchDeckJW', 0, val1, 150);
			break;
		case 0x0D: // search only
			var val1 = data2-0x40;
			call('searchDeckJW', 0, val1, 150);
			break;

		//deck B
		case 0x3D: call('setDeckPitch', 1, data2); break;
		case 0x3E: call('setDeckGain', 1, data2); break;
		case 0x3F: call('setDeckEqHigh', 1, data2); break;
		case 0x40: call('setDeckEqMid', 1, data2); break;
		case 0x41: call('setDeckEqLow', 1, data2); break;
		case 0x42: call('setDeckVolume', 1, data2); break;

		case 0x47: // params: (deck, increment, total positions per complete revolution) 
		case 0x4A: // when fx dry/wet is activated
			var val1 = data2-0x40;
			call('moveDeckJogWheel', 1, val1, 150);
			/*
			if(jogWheelsInSearchMode[1])
			{
				call('searchDeckJW', 1, val1, 150);
			}else
			{
				if(jogWheelsInScratchMode[1]) 
					call('scratchDeckJW', 1, val1, 150);
				else 
					call('moveDeckJogWheel', 1, val1, 150);
			}
			*/
			break;
		case 0x48: // scratch only
			var val1 = data2-0x40;
			call('scratchDeckJW', 1, val1, 150);
			break;
		case 0x49: // search only
			var val1 = data2-0x40;
			call('searchDeckJW', 1, val1, 150);
			break;

		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		switch(data1)
		{
		//global
		case 0x1E: if(data2>0x3F) call('lbToggleSelect', data2); break;

		//deck A
		case 0x01: call('deckSync', 0, data2); break;
		case 0x02: call('deckKeyLock', 0, data2); break;
		case 0x03: call('deckBendDown', 0, data2); sendMidi(uniqueId,0x90,0,0x03,data2); break;
		case 0x04: call('deckBendUp', 0, data2); sendMidi(uniqueId,0x90,0,0x04,data2); break;
		case 0x09: call('deckFxFlanger', 0, data2); break;// fx 1
		case 0x0E: call('deckFxBeatwaw', 0, data2); break;// fx 2

		case 0x13: if(data2>0x3F) call('deckLBLoad', 0, data2); break;
		case 0x14: call('deckEqHighKill', 0, data2); break;
		case 0x15: call('deckEqMidKill', 0, data2); break;
		case 0x16: call('deckEqLowKill', 0, data2); break;
		case 0x17: call('deckCuePlay', 0, data2); break;
		case 0x18: call('deckCue', 0, data2); break;
		case 0x19: call('deckPlayPause', 0, data2); break;
		case 0x1A: // enable search mode 
			if(data2>0x3F) 
			{
				jogWheelsInSearchMode[0] = (jogWheelsInSearchMode[0]+1) % 2; sendMidi(uniqueId,0x90,0,0x1A,(jogWheelsInSearchMode[0] ? 0x7F : 0x00)); 
			}
			break;
		case 0x1B: // enable scratch mode 
			if(data2>0x3F) 
			{
				jogWheelsInScratchMode[0] = (jogWheelsInScratchMode[0]+1) % 2; sendMidi(uniqueId,0x90,0,0x1B,(jogWheelsInScratchMode[0] ? 0x7F : 0x00)); 
			}
			break;
		case 0x1C: call('deckMonitor', 0, data2); break;
		case 0x1D: if(jogWheelsInScratchMode[0] && !jogWheelsInSearchMode[0]) call('holdDeckJogWheel', 0, data2); break;

		case 0x07: call('deckCuePos1', 0, data2); break;// activate 1
		case 0x0C: call('deckCuePos2', 0, data2); break;// activate 2
		// loop functions
		case 0x0F: call('deckLoopInEnd', 0, data2); break;
		case 0x10: // loop close/exit (loop out)
			var deck = 0;
			if(data2>0x3F)
			{ 
				if(getValue('looping', deck)) 
				{
					call('deckLoopExit', deck, 0x7F);
					call('deckLoopExit', deck, 0x00);
				}else
				{
					call('deckLoopInEnd', deck, 0x7F);
					call('deckLoopInEnd', deck, 0x00);
				}
			}
			break;
		case 0x11: call('deckLoopMore', 0, data2); break;
		case 0x12: // reloop/exit
			var deck = 0;
			if(data2>0x3F)
			{ 
				if(getValue('looping', deck)) 
				{
					call('deckLoopExit', deck, 0x7F);
					call('deckLoopExit', deck, 0x00);
				}else
				{
					call('deckLoopReloop', deck, 0x7F);
					call('deckLoopReloop', deck, 0x00);
				}
			}
			break;

		
		//deck B
		case 0x3D: call('deckSync', 1, data2); break;
		case 0x3E: call('deckKeyLock', 1, data2); break;
		case 0x3F: call('deckBendDown', 1, data2); sendMidi(uniqueId,0x90,0,0x3F,data2); break;
		case 0x40: call('deckBendUp', 1, data2); sendMidi(uniqueId,0x90,0,0x40,data2); break;
		case 0x4A: call('deckFxFlanger', 1, data2); break;// fx 1		
		case 0x45: call('deckFxBeatwaw', 1, data2); break;// fx 2
		
		case 0x4F: if(data2>0x3F) call('deckLBLoad', 1, data2); break;
		case 0x50: call('deckEqHighKill', 1, data2); break;
		case 0x51: call('deckEqMidKill', 1, data2); break;
		case 0x52: call('deckEqLowKill', 1, data2); break;
		case 0x53: call('deckCuePlay', 1, data2); break;
		case 0x54: call('deckCue', 1, data2); break;
		case 0x55: call('deckPlayPause', 1, data2); break;
		case 0x56: // enable search mode
			if(data2>0x3F) 
			{
				jogWheelsInSearchMode[1] = (jogWheelsInSearchMode[1]+1) % 2; sendMidi(uniqueId,0x90,0,0x56,(jogWheelsInSearchMode[1] ? 0x7F : 0x00)); 
			}
			break;
		case 0x57: // enable scratch mode
			if(data2>0x3F) 
			{
				jogWheelsInScratchMode[1] = (jogWheelsInScratchMode[1]+1) % 2; sendMidi(uniqueId,0x90,0,0x57,(jogWheelsInScratchMode[1] ? 0x7F : 0x00)); 
			}
			break;
		case 0x58: call('deckMonitor', 1, data2); break;
		case 0x59: if(jogWheelsInScratchMode[1] && !jogWheelsInSearchMode[1]) call('holdDeckJogWheel', 1, data2); break;
			
		case 0x48: call('deckCuePos1', 1, data2); break;// activate 1
		case 0x43: call('deckCuePos2', 1, data2); break;// activate 2
		// loop functions
		case 0x4B: call('deckLoopInEnd', 1, data2); break;
		case 0x4C: // loop close/exit (loop out)
			var deck = 1;
			if(data2>0x3F)
			{ 
				if(getValue('looping', deck)) 
				{
					call('deckLoopExit', deck, 0x7F);
					call('deckLoopExit', deck, 0x00);
				}else
				{
					call('deckLoopInEnd', deck, 0x7F);
					call('deckLoopInEnd', deck, 0x00);
				}
			}
			break;
		case 0x4D: call('deckLoopMore', 1, data2); break;
		case 0x4E: // reloop/exit
			var deck = 1;
			if(data2>0x3F)
			{ 
				if(getValue('looping', deck)) 
				{
					call('deckLoopExit', deck, 0x7F);
					call('deckLoopExit', deck, 0x00);
				}else
				{
					call('deckLoopReloop', deck, 0x7F);
					call('deckLoopReloop', deck, 0x00);
				}
			}
			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)
{
	if(deck>=2) return;
	//debug('notif ['+uniqueId+'] name='+name + ' deck='+deck + ' p1(int)='+param1 + ' p2(float)='+param2 + ' p3(str)='+param3);
	if(name=='cue') 		 sendMidi(uniqueId,0x90,0,(deck==0)?0x18:0x54,(param1 ? 0x7F : 0x00));
	if(name=='play') 		 sendMidi(uniqueId,0x90,0,(deck==0)?0x19:0x55,(param1 ? 0x7F : 0x00));
	if(name=='onbeat') 		 sendMidi(uniqueId,0x90,0,(deck==0)?0x01:0x3D,(param1 ? 0x7F : 0x00));// actually it's sync
	if(name=='keyLock') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x02:0x3E,(param1 ? 0x7F : 0x00));
	if(name=='killHigh') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x14:0x50,(param1 ? 0x7F : 0x00));
	if(name=='killMid') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x15:0x51,(param1 ? 0x7F : 0x00));
	if(name=='killLow') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x16:0x52,(param1 ? 0x7F : 0x00));
	if(name=='monitor') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x1C:0x58,(param1 ? 0x7F : 0x00));// it says FX Dry/Wet on the buttons
	if(name=='fxFlanger') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x09:0x4A,(param1 ? 0x7F : 0x00));// fx1
	if(name=='fxBeatwaw') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x0E:0x45,(param1 ? 0x7F : 0x00));// fx2
	if(name=='cuepos1')		 sendMidi(uniqueId,0x90,0,(deck==0)?0x07:0x48,(param1 ? 0x7F : 0x00));
	if(name=='cuepos2')		 sendMidi(uniqueId,0x90,0,(deck==0)?0x0C:0x43,(param1 ? 0x7F : 0x00));
	if(name=='loopIn')
	{
		sendMidi(uniqueId,0x90,0,(deck==0)?0x0F:0x4B,(param1 ? 0x7F : 0x00));// Loop In (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,(deck==0)?0x0F:0x4B,(param1 ? 0x7F : 0x00));// Loop In (on or off)		
		sendMidi(uniqueId,0x90,0,(deck==0)?0x10:0x4C,(param1 ? 0x7F : 0x00));// Loop Out (on or off)
		sendMidi(uniqueId,0x90,0,(deck==0)?0x11:0x4D,(param1 ? 0x7F : 0x00));// Loop more (on or off)
		sendMidi(uniqueId,0x90,0,(deck==0)?0x12:0x4E,0x00);// Reloop (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+']');
	for(var i=0; i<2; i++)
	{
		// loop in led
		if(loopingInBlinkLed[i]!=-1)
		{
			// actually set the led
			sendMidi(uniqueId,0x90,0,(i==0)?0x0F:0x4B,((loopingInBlinkLed[i]<2) ? 0x7F : 0x00));// Loop In (on or off)
			loopingInBlinkLed[i]++;
			if(loopingInBlinkLed[i]>=4) loopingInBlinkLed[i] = 0;
		}
	}
}
//----------------------------------------------------------------------------- 
