//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2011. All Rights Reserved. -----------------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller ' Novation Twitch'
//author: Brandon James --Rev 1.0 -- 2013 07 03
//----------------------------------------------------------------------------- 
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 "Novation Twitch";
}
//----------------------------------------------------------------------------- 
// 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 (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 "Twitch";

}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	return "Twitch";
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var DeckPitch=[63,63];
var jogWheelsInScratchMode=[0,0];


function setVUmeter(uniqueId, ch, dbValue)
{
	var vuCurrent=0;// temp
	if(dbValue>-40) vuCurrent = 8; //1
	if(dbValue>-35) vuCurrent = 16; //2
	if(dbValue>-30) vuCurrent = 24; //3
	if(dbValue>-28) vuCurrent = 32; //4
	if(dbValue>-25)  vuCurrent = 40; //5
	if(dbValue>-22)  vuCurrent = 48; //6
	if(dbValue>-18) vuCurrent = 56; //7
	if(dbValue>-14) vuCurrent = 64; //8
	if(dbValue>-10) vuCurrent = 72; //9
	if(dbValue>-8) vuCurrent = 80; //10
	if(dbValue>-6)  vuCurrent = 88; //11
	if(dbValue>-4)  vuCurrent = 96; //12
	if(dbValue>-3) vuCurrent = 104; //13
	if(dbValue>-2)  vuCurrent = 112; //14
	if(dbValue>-1)  vuCurrent = 120; //15
	
	
	sendMidi(uniqueId,0x90,7+ch,0x5F,vuCurrent);

}

//-----------------------------------------------------------------------------
/*function setSongPositionLedStrip(uniqueId, deck, pos) // pos is [0..1]
{
	var tmp, threshold=0;
	var startLedId = 0x07; if(deck>0) startLedId = 0x36;
	for(var i=0; i<11; i++)
	{
		// see if this led is to be on or off
		if(pos>threshold) tmp = 0x7F;else tmp = 0; 
		// we test with the previous led state so we don't send led midi messages for no reason
		if(tmp != posLedStrip[deck][i]) 
		{
			// save new led state
			posLedStrip[deck][i] = tmp;
			// change led state
			sendMidi(uniqueId, 0x90, 0, startLedId + i, tmp);
		}
		// increase the threshold
		threshold += 0.0909;
	}
}
*/
//----------------------------------------------------------------------------- 
// 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 Twitch ['+uniqueId+']');// this outputs a string to DebugView app on Win
	callSendAllNotifications(uniqueId, true);
	for(var i=0; i<2; i++)
	{

		sendMidi(uniqueId,0xB0,7,0x00,0x01); // request fader / controller positions
		sendMidi(uniqueId,0x90,7,0x0D,0x00); //deck fx LED off	
		sendMidi(uniqueId,0x90,8,0x0D,0x00); //deck fx LED off
		
		
		call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
	}
}
//----------------------------------------------------------------------------- 
// called just before the controller will be deinitialized and the control will go away from the script
// use to power off any powered leds, etc
function controllerDeinit(uniqueId)
{
	debug('controllerDeinit Twitch ['+uniqueId+']');
	callSendAllNotifications(uniqueId, false);
	for(var i=0; i<2; i++)
	{
		
	    sendMidi(uniqueId,0x90,7,0x0D,0x00);	//deck fx LED off
		sendMidi(uniqueId,0x90,8,0x0D,0x00);	//deck fx LED off	
		
		
	

		call('sleep', 10);// delay for 10ms to allow the MIDI out buffer to flush
	}
}
//----------------------------------------------------------------------------- 
// this is your main target function to implement the controller -- this is called 
// for each and every MIDI message that comes from the controller itself
// return true if the message was processed by the script and false to let it 
// pass thru the software (eg. so it can be assigned by the user using the LEARN function)
function messageReceived(uniqueId,status,channel,data1,data2)
{
	var messageNotProcessed=false;
	switch(status)
	{
	//-----------------------------------------------
	// controllers: sliders, knobs, jog-wheels, encoders ('moving' things)
	case 0xB0:
		switch(data1)
		{
		// global
		case 0x55: call('lbMoveCursor', data2==0x01 ? 1:-1); break; //library curser up down
		case 0x08: call('setCrossfader', data2); break; //library curser up down		


		// decks
		case 0x07: call('setDeckVolume', channel-7, data2); break; // deck volume slider
		case 0x09: call('setDeckGain', channel-7, data2); break; // deck trim 
		case 0x03:  //deck pitch knob
			
			if(data2==0x7F) DeckPitch[channel-7] = DeckPitch[channel-7] + 1;// to left
			if(data2==0x01) DeckPitch[channel-7] = DeckPitch[channel-7] - 1;// to right
			if(DeckPitch[channel-7]<0) DeckPitch[channel-7] = 0;
			if(DeckPitch[channel-7]>0x7F) DeckPitch[channel-7] = 0x7F;
			call('setDeckPitch', channel-7, DeckPitch[channel-7]);
			break;

		case 0x48: call('setDeckEqHigh', channel-7, data2); break; // deck eq high
		case 0x47: call('setDeckEqMid', channel-7, data2); break; // deck eq mid
		case 0x46: call('setDeckEqLow', channel-7, data2); break; // deck eq low
		case 0x34: call('setDeckPos', channel-7, data2); break; //needle drop
		case 0x35: // params: (deck, increment, total positions per complete revolution) 
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			call('scratchDeckJW', channel-7, val1, 127);
			break;
		
		case 0x06: if(data2<0x40) call('deckFx1Next', channel-7);else call('deckFx1Prev', channel-7); break; //deck fx next
		
		
		/*
		case 0x04: //depth knob
		case 0x05: //mod knob (encoder 7F / 01
		case 0x06: // beats knob (encoder 7F / 01
		case 0x07: // Mic / AUX level Knob
		
		
		*/		

		default: messageNotProcessed = true;
		}
		
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		//if(status==0x90) data2 = 0x7F;
		//if(status==0x80) data2 = 0;
		switch(data1)
		{
		// global
		
		case 0x52: call('deckLBLoad', 0, data2); break; //load deck A
		case 0x53: call('deckLBLoad', 1, data2); break; //load deck B
		
		case 0x54: if(data2>0x3F) call('lbBrowserSelect', data2); break; // back button
		case 0x56: if(data2>0x3F) call('lbListSelect', data2); break; //forward
		case 0x55: if((status==0x90)&&(data2>0x3F)) call('lbToggleSelect', data2); break; // press scroll knob to toggle
		
		//

		
		// decks
		

		
		case 0x17: call('deckPlayPause', channel-7, data2); break; //play pause 1/2
		case 0x16: call('deckCue', channel-7, data2); break; //Cue 1/2

		case 0x13: call('deckSync', channel-7, data2); break; //sync
		case 0x12: call('deckKeyLock', channel-7, data2); break; //key lock
		case 0x0A: call('deckMonitor', channel-7, data2); break; // deck pfl
		case 0x03: call('deckResetPitch', channel-7, data2); break; // deck reset pitch
		
		//case 0x38: Hot cue mode switch
		// hot cue mode buttons 1-8
		case 0x60: call('deckCuePos1', channel-7, data2); break; //cue 1
		case 0x61: call('deckCuePos2', channel-7, data2); break; //cue 2
		case 0x62: call('deckCuePos3', channel-7, data2); break; //cue 3
		case 0x63: call('deckCuePos4', channel-7, data2); break; //cue 4
		case 0x64: call('deckCuePos5', channel-7, data2); break; //cue 5
		case 0x65: call('deckCuePos6', channel-7, data2); break; //cue 6
		case 0x66: call('deckCuePos7', channel-7, data2); break; //cue 7
		case 0x67: call('deckCuePos8', channel-7, data2); break; //cue 8
		
		//case 0x39: Slicer mode button switch
		//slicer mode buttons 1-8 using sampler instead both decks 
		
		case 0x68: call('samplerPlay', 1); break;// 1
		case 0x69: call('samplerPlay', 2); break;// 2
		case 0x6A: call('samplerPlay', 3); break;// 3
		case 0x6B: call('samplerPlay', 4); break;// 4
		case 0x6C: call('samplerPlay', 5); break;// 5
		case 0x6D: call('samplerPlay', 6); break;// 6
		case 0x6E: call('samplerPlay', 7); break;// 7
		case 0x6F: call('samplerPlay', 8); break;// 8
		
		//case 0x3A: auto loop mode button switch
		// auto loop mode buttons 1-8
		case 0x70: call('deckLoop1_16', channel-7, data2); break; // auto loop button 1
		case 0x71: call('deckLoop1_8', channel-7, data2); break; // auto loop button 2
		case 0x72: call('deckLoop1_4', channel-7, data2); break; // auto loop button 3
		case 0x73: call('deckLoop1_2', channel-7, data2); break; // auto loop button 4
		case 0x74: call('deckLoop1', channel-7, data2); break; // auto loop button 5
		case 0x75: call('deckLoop2', channel-7, data2); break; // auto loop button 6
		case 0x76: call('deckLoop4', channel-7, data2); break; // auto loop button 7
		case 0x77: call('deckLoop8', channel-7, data2); break; // auto loop button 8
		
		//case 0x3B: loop roll button switch
		// loop roll mode buttons 1-8
		/*
		case 0x78: call('', channel-7, data2); break; // 1
		case 0x79: call('', channel-7, data2); break; // 2
		case 0x7A: call('', channel-7, data2); break; // 3
		case 0x7B: call('', channel-7, data2); break; // 4
		case 0x7C: call('', channel-7, data2); break; // 5
		case 0x7D: call('', channel-7, data2); break; // 6
		case 0x7E: call('', channel-7, data2); break; // 7
		case 0x7F: call('', channel-7, data2); break; // 8
		*/
		
		case 0x06: if(data2>0x3F) 
					{
					call('deckFx1ToggleOn', channel-7, data2); 
					sendMidi(uniqueId,0x90,channel,0x0D, (getValue("fx1DryWet", channel-7) ? 0x7F : 0x00));
					} break; //deck fx on (this action is a trigger)
		
		
		case 0x0D:	if(data2>0x3F) 
					{
					call('deckFx1ToggleOn', channel-7, data2); 
					sendMidi(uniqueId,0x90,channel,0x0D, (getValue("fx1DryWet", channel-7) ? 0x7F : 0x00));
					} break; //deck fx on (this action is a trigger)
		
		
		/*
		case 0x20: //FX <- button
		case 0x21: //FX -> button
		case 0x22: //on / off button
		case 0x23: //headphone FX on  / off
		case 0x1D: //deck A button
		case 0x1E: //deck B button
		case 0x1C: //AUX button
		
		case 0x10: // set / clr button
		case 0x11: adjust / slip button
		case 0x00: // shift
		case 0x13: // sync auto
		
		*/
		
		
		
		//case 0x47: call('holdDeckJogWheel', channel-7, data2); break	// ask ionut

		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);
	if(deck<2)
	{
		if(name=='cue') 		 sendMidi(uniqueId,0x90,deck+7,0x16,(param1 ? 0x0F : 0x00)); // cue
		if(name=='play') 		 sendMidi(uniqueId,0x90,deck+7,0x17,(param1 ? 0x7F : 0x00)); //play
		if(name=='keyLock') 	 sendMidi(uniqueId,0x90,deck+7,0x12,(param1 ? 0x7F : 0x00)); //keylock
		if(name=='monitor') 	 sendMidi(uniqueId,0x90,deck+7,0x0A,(param1 ? 0x7F : 0x00)); // deck PFL
		
		if(name=='cuepos1') 	 sendMidi(uniqueId,0x90,deck+7,0x60,(param1 ? 0x4F : 0x40)); //cue 1
		if(name=='cuepos2') 	 sendMidi(uniqueId,0x90,deck+7,0x61,(param1 ? 0x4F : 0x40)); //cue 2
		if(name=='cuepos3') 	 sendMidi(uniqueId,0x90,deck+7,0x62,(param1 ? 0x4F : 0x40)); //cue 3
		if(name=='cuepos4') 	 sendMidi(uniqueId,0x90,deck+7,0x63,(param1 ? 0x4F : 0x40)); //cue 4
		if(name=='cuepos5') 	 sendMidi(uniqueId,0x90,deck+7,0x64,(param1 ? 0x4F : 0x40)); //cue 5
		if(name=='cuepos6') 	 sendMidi(uniqueId,0x90,deck+7,0x65,(param1 ? 0x4F : 0x40)); //cue 6
		if(name=='cuepos7') 	 sendMidi(uniqueId,0x90,deck+7,0x66,(param1 ? 0x4F : 0x40)); //cue 5
		if(name=='cuepos8') 	 sendMidi(uniqueId,0x90,deck+7,0x67,(param1 ? 0x4F : 0x40)); //cue 6
		
		if(name=='sampler1') 	 
								{
								sendMidi(uniqueId,0x90,7,0x68,(param1 ? 0x07 : 0x00)); //sampler 1 deck A
								sendMidi(uniqueId,0x90,8,0x68,(param1 ? 0x07 : 0x00)); //sampler 1 deck B
								}
		if(name=='sampler2') 	 
								{
								sendMidi(uniqueId,0x90,7,0x69,(param1 ? 0x07 : 0x00)); //sampler 2 deck A
								sendMidi(uniqueId,0x90,8,0x69,(param1 ? 0x07 : 0x00)); //sampler 2 deck B
								}
		if(name=='sampler3') 	 
								{
								sendMidi(uniqueId,0x90,7,0x6A,(param1 ? 0x07 : 0x00)); //sampler 3 deck A
								sendMidi(uniqueId,0x90,8,0x6A,(param1 ? 0x07 : 0x00)); //sampler 3 deck B
								}
		if(name=='sampler4') 	 
								{
								sendMidi(uniqueId,0x90,7,0x6B,(param1 ? 0x07 : 0x00)); //sampler 4 deck A
								sendMidi(uniqueId,0x90,8,0x6B,(param1 ? 0x07 : 0x00)); //sampler 4 deck B
								}
		if(name=='sampler5') 	 
								{
								sendMidi(uniqueId,0x90,7,0x6C,(param1 ? 0x07 : 0x00)); //sampler 5 deck A
								sendMidi(uniqueId,0x90,8,0x6C,(param1 ? 0x07 : 0x00)); //sampler 5 deck B
								}
		if(name=='sampler6') 	 
								{
								sendMidi(uniqueId,0x90,7,0x6D,(param1 ? 0x07 : 0x00)); //sampler 6 deck A
								sendMidi(uniqueId,0x90,8,0x6D,(param1 ? 0x07 : 0x00)); //sampler 6 deck B
								}
		if(name=='sampler7') 	 
								{
								sendMidi(uniqueId,0x90,7,0x6E,(param1 ? 0x07 : 0x00)); //sampler 7 deck A
								sendMidi(uniqueId,0x90,8,0x6E,(param1 ? 0x07 : 0x00)); //sampler 7 deck B
								}
		if(name=='sampler8') 	 
								{
								sendMidi(uniqueId,0x90,7,0x6F,(param1 ? 0x07 : 0x00)); //sampler 8 deck A
								sendMidi(uniqueId,0x90,8,0x6F,(param1 ? 0x07 : 0x00)); //sampler 8 deck B
								}
	    if(name=='loopSize')
			{
			
			// calculate the new led to power on
				var l1, l2, l3, l4, l5, l6, l7, l8;
				l1 = l2 = l3 = l4 = l5 = l6 = l7 = l8 = 0;
				switch(param1)
				{
				case  4: l1 = 0x7F; break; //loop 1/16 button 1
				case  8: l2 = 0x7F; break; //loop 1/8  button 2
				case  16: l3 = 0x7F; break; //loop 1/4 button 3
				case  32: l4 = 0x7F; break; //loop 1/2 button 4
				case  64: l5 = 0x7F; break; //loop 1   button 5			
				case  128: l6 = 0x7F; break; //loop 2  button 6
				case  256: l7 = 0x7F; break; //loop 4  button 7
				case  512: l8 = 0x7F; break; //loop 8  button 8
			
			
				}
				if(deck<2)
					{	
					sendMidi(uniqueId,0x90,deck+7,0x70,l1); //loop 1/16 button 1
					sendMidi(uniqueId,0x90,deck+7,0x71,l2); //loop 1/8 button 2
					sendMidi(uniqueId,0x90,deck+7,0x72,l3); //loop 1/4 button 3
					sendMidi(uniqueId,0x90,deck+7,0x73,l4); //loop 1/2 button 4
					sendMidi(uniqueId,0x90,deck+7,0x74,l5); //loop 1 button 5
					sendMidi(uniqueId,0x90,deck+7,0x75,l6); //loop 2 button 6
					sendMidi(uniqueId,0x90,deck+7,0x76,l7); //loop 4 button 7
					sendMidi(uniqueId,0x90,deck+7,0x77,l8); //loop 8 button 8
					}
		
			}
			// light off the leds
			if((name=='looping')&&(!param1))
			{
				if(deck<2)
				{
					sendMidi(uniqueId,0x90,deck+7,0x70,0x00); //loop 1/16 button 1
					sendMidi(uniqueId,0x90,deck+7,0x71,0x00); //loop 1/8 button 2
					sendMidi(uniqueId,0x90,deck+7,0x72,0x00); //loop 1/4 button 3
					sendMidi(uniqueId,0x90,deck+7,0x73,0x00); //loop 1/2 button 4
					sendMidi(uniqueId,0x90,deck+7,0x74,0x00); //loop 1 button 5
					sendMidi(uniqueId,0x90,deck+7,0x75,0x00); //loop 2 button 6
					sendMidi(uniqueId,0x90,deck+7,0x76,0x00); //loop 4 button 7
					sendMidi(uniqueId,0x90,deck+7,0x77,0x00); //loop 8 button 8
				}
			}

	}
}
//----------------------------------------------------------------------------- 

//----------------------------------------------------------------------------- 
// 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++)
	{
		var dbValue = getValue('vumeterDeck', i);// float dB
		setVUmeter(uniqueId, i, dbValue);
	
	}
}
//----------------------------------------------------------------------------- 
