//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2011 XYLIO INFO SRL. All Rights Reserved. --------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Numark TotalTrack Control'
//----------------------------------------------------------------------------- 
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 "Numark TotalTrack Control";
}
//----------------------------------------------------------------------------- 
// 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 "00013F2100";
}
//----------------------------------------------------------------------------- 
// 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 "TotalTrack Control";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==1) return "TotalTrack Control";
	return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var jogWheelsInScratchMode=0;
var timer1=0;
var loopInPulse=0;
var loopInEnabled=[0,0];
var fxParams=[0,0,0,0,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 ['+uniqueId+']');// this outputs a string to DebugView app on Win
	// global
	sendMidi(uniqueId,0x90,0,0x56,0x00);// Scratch Mode

	// deck A
	sendMidi(uniqueId,0x90,0,0x35,(getValue('monitor', 0) ? 0x7F : 0x00));// PFL (A)
	sendMidi(uniqueId,0x90,0,0x36,(getValue('keyLock', 0) ? 0x7F : 0x00));// Keylock (A)
	sendMidi(uniqueId,0x90,0,0x37,0x00);// Sync (A)
	sendMidi(uniqueId,0x90,0,0x33,(getValue('fxBeatwaw', 0) ? 0x7F : 0x00));// Effect 1 (A)
	sendMidi(uniqueId,0x90,0,0x31,(getValue('fxFlanger', 0) ? 0x7F : 0x00));// Effect 2 (A)
	sendMidi(uniqueId,0x90,0,0x32,(getValue('fxPan', 0) ? 0x7F : 0x00));// Effect 3 (A)
	sendMidi(uniqueId,0x90,0,0x30,(getValue('fxEcho', 0) ? 0x7F : 0x00));// Effect 4 (A)
	sendMidi(uniqueId,0x90,0,0x3A,(getValue('looping', 0) ? 0x7F : 0x00));// Loop In (A)
	sendMidi(uniqueId,0x90,0,0x3B,(getValue('looping', 0) ? 0x7F : 0x00));// Loop Out(A)
	sendMidi(uniqueId,0x90,0,0x50,(getValue('killHigh', 0) ? 0x7F : 0x00));// Treble (A)
	sendMidi(uniqueId,0x90,0,0x51,(getValue('killMid', 0) ? 0x7F : 0x00));// Mid (A)
	sendMidi(uniqueId,0x90,0,0x52,(getValue('killLow', 0) ? 0x7F : 0x00));// Low (A)
	sendMidi(uniqueId,0x90,0,0x3C,(getValue('cue', 0) ? 0x7F : 0x00));// Cue (A)
	sendMidi(uniqueId,0x90,0,0x3E,(getValue('play', 0) ? 0x7F : 0x00));// Play/Pause (A)
	//sendMidi(uniqueId,0x90,0,0x3D,0x7F);// Pause/Cue-Play (A)
	//sendMidi(uniqueId,0x90,0,0x3F,0x7F);// Load (A)	
	//sendMidi(uniqueId,0x90,0,0x34,0x7F);// Pitch (A)
	//sendMidi(uniqueId,0x90,0,0x38,0x7F);// search left (A)
	//sendMidi(uniqueId,0x90,0,0x39,0x7F);// search right (A)

	// deck B
	sendMidi(uniqueId,0x90,0,0x40,(getValue('monitor', 1) ? 0x7F : 0x00));// PFL (B)
	sendMidi(uniqueId,0x90,0,0x41,(getValue('keyLock', 1) ? 0x7F : 0x00));// Keylock (B)
	sendMidi(uniqueId,0x90,0,0x42,0x00);// Sync (B)
	sendMidi(uniqueId,0x90,0,0x44,(getValue('fxBeatwaw', 1) ? 0x7F : 0x00));// Effect 1 (B)
	sendMidi(uniqueId,0x90,0,0x45,(getValue('fxFlanger', 1) ? 0x7F : 0x00));// Effect 2 (B)
	sendMidi(uniqueId,0x90,0,0x46,(getValue('fxPan', 1) ? 0x7F : 0x00));// Effect 3 (B)
	sendMidi(uniqueId,0x90,0,0x47,(getValue('fxEcho', 1) ? 0x7F : 0x00));// Effect 4 (B)
	sendMidi(uniqueId,0x90,0,0x4A,(getValue('looping', 1) ? 0x7F : 0x00));// Loop In (B)
	sendMidi(uniqueId,0x90,0,0x4B,(getValue('looping', 1) ? 0x7F : 0x00));// Loop Out (B)
	sendMidi(uniqueId,0x90,0,0x53,(getValue('killHigh', 1) ? 0x7F : 0x00));// Treble (B)
	sendMidi(uniqueId,0x90,0,0x54,(getValue('killMid', 1) ? 0x7F : 0x00));// Mid (B)
	sendMidi(uniqueId,0x90,0,0x55,(getValue('killLow', 1) ? 0x7F : 0x00));// Low (B)
	sendMidi(uniqueId,0x90,0,0x4C,(getValue('cue', 1) ? 0x7F : 0x00));// Cue (B)
	sendMidi(uniqueId,0x90,0,0x4E,(getValue('play', 1) ? 0x7F : 0x00));// Play/Pause (B)
	//sendMidi(uniqueId,0x90,0,0x4D,0x7F);// Pause/Cue-play (B)
	//sendMidi(uniqueId,0x90,0,0x4F,0x7F);// Load (B)
	//sendMidi(uniqueId,0x90,0,0x43,0x7F);// Pitch (B)
	//sendMidi(uniqueId,0x90,0,0x48,0x7F);// Search Left (B)
	//sendMidi(uniqueId,0x90,0,0x49,0x7F);// Search Right (B)
	
	fxParams[0] = getValue('fxBeatwawDryWet', 0);
	fxParams[1] = getValue('fxFlangerDryWet', 0);
	fxParams[2] = getValue('fxPanDryWet', 0);
	fxParams[3] = getValue('fxEchoDryWet', 0);
	fxParams[4] = getValue('fxBeatwawDryWet', 1);
	fxParams[5] = getValue('fxFlangerDryWet', 1);
	fxParams[6] = getValue('fxPanDryWet', 1);
	fxParams[7] = getValue('fxEchoDryWet', 1);
}
//----------------------------------------------------------------------------- 
// 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+']');
	sendMidi(uniqueId,0x90,0,0x38,0x00);// search left (A)
	sendMidi(uniqueId,0x90,0,0x39,0x00);// search right (A)
	sendMidi(uniqueId,0x90,0,0x35,0x00);// PFL (A)
	sendMidi(uniqueId,0x90,0,0x36,0x00);// Keylock (A)
	sendMidi(uniqueId,0x90,0,0x37,0x00);// Sync (A)
	sendMidi(uniqueId,0x90,0,0x33,0x00);// Effect 1 (A)
	sendMidi(uniqueId,0x90,0,0x31,0x00);// Effect 2 (A)
	sendMidi(uniqueId,0x90,0,0x32,0x00);// Effect 3 (A)
	sendMidi(uniqueId,0x90,0,0x30,0x00);// Effect 4 (A)
	sendMidi(uniqueId,0x90,0,0x34,0x00);// Pitch (A)
	sendMidi(uniqueId,0x90,0,0x3A,0x00);// Loop In (A)
	sendMidi(uniqueId,0x90,0,0x3B,0x00);// Loop Out(A)
	sendMidi(uniqueId,0x90,0,0x50,0x00);// Treble (A)
	sendMidi(uniqueId,0x90,0,0x51,0x00);// Mid (A)
	sendMidi(uniqueId,0x90,0,0x52,0x00);// Low (A)
	sendMidi(uniqueId,0x90,0,0x3C,0x00);// Cue (A)
	sendMidi(uniqueId,0x90,0,0x3D,0x00);// Pause/Cue-Play (A)
	sendMidi(uniqueId,0x90,0,0x3E,0x00);// Play/Pause (A)
	sendMidi(uniqueId,0x90,0,0x3F,0x00);// Load (A)
	sendMidi(uniqueId,0x90,0,0x56,0x00);// Scratch Mode
	sendMidi(uniqueId,0x90,0,0x53,0x00);// Treble (B)
	sendMidi(uniqueId,0x90,0,0x54,0x00);// Mid (B)
	sendMidi(uniqueId,0x90,0,0x55,0x00);// Low (B)
	sendMidi(uniqueId,0x90,0,0x4F,0x00);// Load (B)
	sendMidi(uniqueId,0x90,0,0x44,0x00);// Effect 1 (B)
	sendMidi(uniqueId,0x90,0,0x45,0x00);// Effect 2 (B)
	sendMidi(uniqueId,0x90,0,0x46,0x00);// Effect 3 (B)
	sendMidi(uniqueId,0x90,0,0x47,0x00);// Effect 4 (B)
	sendMidi(uniqueId,0x90,0,0x40,0x00);// PFL (B)
	sendMidi(uniqueId,0x90,0,0x41,0x00);// Keylock (B)
	sendMidi(uniqueId,0x90,0,0x42,0x00);// Sync (B)
	sendMidi(uniqueId,0x90,0,0x48,0x00);// Search Left (B)
	sendMidi(uniqueId,0x90,0,0x49,0x00);// Search Right (B)
	sendMidi(uniqueId,0x90,0,0x43,0x00);// Pitch (B)
	sendMidi(uniqueId,0x90,0,0x4A,0x00);// Loop In (B)
	sendMidi(uniqueId,0x90,0,0x4B,0x00);// Loop Out (B)
	sendMidi(uniqueId,0x90,0,0x4C,0x00);// Cue (B)
	sendMidi(uniqueId,0x90,0,0x4D,0x00);// Pause/Cue-play (B)
	sendMidi(uniqueId,0x90,0,0x4E,0x00);// Play/Pause (B)
}
//----------------------------------------------------------------------------- 
function getEncoderValue(encoder, data2)
{
	var val1 = (data2>0x40 ? data2-0x80 : data2); 
	fxParams[encoder] = Math.min(Math.max(0, fxParams[encoder] + val1 * 2), 127); 
	return fxParams[encoder];
}
//----------------------------------------------------------------------------- 
// 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 0x0A: call('setCrossfader', 127-data2); break;
		case 0x17: call('setMasterGain', data2); break;
		case 0x16: call('setCuePgmMix', data2); break;
		case 0x0F: call('setMonitorGain', data2); break;
		

		case 0x1A: // move track
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			call('lbMoveCursor', val1);
			break;		

		// per deck
		// deck A
		case 0x08: call('setDeckVolume', 0, data2); break;
		case 0x14: call('setDeckEqLow', 0, data2); break;
		case 0x12: call('setDeckEqMid', 0, data2); break;
		case 0x10: call('setDeckEqHigh', 0, data2); break;
		case 0x0B: call('setDeckPitch', 0, data2); break;
		case 0x0D: call('setDeckGain', 0, data2); break;

		case 0x00: call('deckFxBeatwawDW', 0, getEncoderValue(0, data2)); break;
		case 0x01: call('deckFxFlangerDW', 0, getEncoderValue(1, data2)); break;
		case 0x02: call('deckFxPanDW', 0, getEncoderValue(2, data2)); break;
		case 0x03: call('deckFxEchoDW', 0, getEncoderValue(3, data2)); break;
		

		case 0x19: // params: (deck, increment, total positions per complete revolution) 
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			if(jogWheelsInScratchMode) call('scratchDeckJW', 0, val1, 300);else call('moveDeckJogWheel', 0, val1, 300);
			break;		
		
		// deck B
		case 0x09: call('setDeckVolume', 1, data2); break;
		case 0x15: call('setDeckEqLow', 1, data2); break;
		case 0x13: call('setDeckEqMid', 1, data2); break;
		case 0x11: call('setDeckEqHigh', 1, data2); break;
		case 0x0C: call('setDeckPitch', 1, data2); break;
		case 0x0E: call('setDeckGain', 1, data2); break;

		case 0x04: call('deckFxBeatwawDW', 1, getEncoderValue(4, data2)); break;
		case 0x05: call('deckFxFlangerDW', 1, getEncoderValue(5, data2)); break;
		case 0x06: call('deckFxPanDW', 1, getEncoderValue(6, data2)); break;
		case 0x07: call('deckFxEchoDW', 1, getEncoderValue(7, data2)); break;		
		
		case 0x18: // params: (deck, increment, total positions per complete revolution) 
			var val1 = (data2>0x40 ? data2-0x80 : data2);
			if(jogWheelsInScratchMode) call('scratchDeckJW', 1, val1, 300);else call('moveDeckJogWheel', 1, val1, 300);
			break;		

		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		switch(data1)
		{
		// global
		case 0x48: if(data2>0x3F){ jogWheelsInScratchMode = (jogWheelsInScratchMode+1)%2; sendMidi(uniqueId,0x90,0,0x56,(jogWheelsInScratchMode ? 0x7F : 0x00));} break;
		case 0x4F: if(data2>0x3F) call('lbToggleSelect', data2); break;

		// per deck
		// deck A
		case 0x4B: if(data2>0x3F) call('deckLBLoad', 0, data2); break;
		case 0x43: call('deckPlayPause', 0, data2); break;
		case 0x33: call('deckCue', 0, data2); break;
		case 0x49: call('deckLoopInEnd', 0, data2); break;
		case 0x4A: if(getValue('looping', 0) || getValue('loopIn', 0)) call('deckLoopExit', 0, data2);else call('deckLoopReloop', 0, data2); break;
		case 0x40: call('deckSync', 0, data2); break;
		case 0x38: call('deckKeyLock', 0, data2); break;
		case 0x41: call('deckSearchLeft', 0, data2); break;
		case 0x42: call('deckSearchRight', 0, data2); break;
		case 0x31: call('deckFxBeatwaw', 0, data2); break;// fx 1
		case 0x32: call('deckFxFlanger', 0, data2); break;// fx 2
		case 0x39: call('deckFxPan', 0, data2); break;// fx 3
		case 0x3A: call('deckFxEcho', 0, data2); break;// fx 4
		case 0x53: call('deckEqLowKill', 0, data2); break;
		case 0x55: call('deckEqMidKill', 0, data2); break;
		case 0x50: call('deckEqHighKill', 0, data2); break;
		case 0x3B: call('deckCuePlay', 0, data2); break;
		case 0x30: call('deckMonitor', 0, data2); break;
		
		// deck B
		case 0x34: if(data2>0x3F) call('deckLBLoad', 1, data2); break;
		case 0x4C: call('deckPlayPause', 1, data2); break;
		case 0x3C: call('deckCue', 1, data2); break;
		case 0x4D: call('deckLoopInEnd', 1, data2); break;
		case 0x4E: if(getValue('looping', 1) || getValue('loopIn', 1)) call('deckLoopExit', 1, data2);else call('deckLoopReloop', 1, data2); break;
		case 0x47: call('deckSync', 1, data2); break;
		case 0x3F: call('deckKeyLock', 1, data2); break;
		case 0x45: call('deckSearchLeft', 1, data2); break;
		case 0x46: call('deckSearchRight', 1, data2); break;
		case 0x35: call('deckFxBeatwaw', 1, data2); break;// fx 1
		case 0x36: call('deckFxFlanger', 1, data2); break;// fx 2
		case 0x3D: call('deckFxPan', 1, data2); break;// fx 3
		case 0x3E: call('deckFxEcho', 1, data2); break;// fx 4
		case 0x54: call('deckEqLowKill', 1, data2); break;
		case 0x51: call('deckEqMidKill', 1, data2); break;
		case 0x52: call('deckEqHighKill', 1, data2); break;
		case 0x44: call('deckCuePlay', 1, data2); break;
		case 0x37: call('deckMonitor', 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)
{
	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)?0x3C:0x4C,(param1 ? 0x7F : 0x00));
	if(name=='play') 		 sendMidi(uniqueId,0x90,0,(deck==0)?0x3E:0x4E,(param1 ? 0x7F : 0x00));
	if(name=='fxBeatwaw')  { sendMidi(uniqueId,0x90,0,(deck==0)?0x33:0x44,(param1 ? 0x7F : 0x00)); fxParams[4*deck + 0] = 127*param1;}
	if(name=='fxFlanger')  { sendMidi(uniqueId,0x90,0,(deck==0)?0x31:0x45,(param1 ? 0x7F : 0x00)); fxParams[4*deck + 1] = 127*param1;}
	if(name=='fxPan') 	   { sendMidi(uniqueId,0x90,0,(deck==0)?0x32:0x46,(param1 ? 0x7F : 0x00)); fxParams[4*deck + 2] = 127*param1;}
	if(name=='fxEcho') 	   { sendMidi(uniqueId,0x90,0,(deck==0)?0x30:0x47,(param1 ? 0x7F : 0x00)); fxParams[4*deck + 3] = 127*param1;}
	if(name=='monitor') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x35:0x40,(param1 ? 0x7F : 0x00));
	if(name=='onbeat') 		 sendMidi(uniqueId,0x90,0,(deck==0)?0x37:0x42,(param1 ? 0x7F : 0x00));
	if(name=='keyLock') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x36:0x41,(param1 ? 0x7F : 0x00));
	if(name=='killHigh') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x50:0x53,(param1 ? 0x7F : 0x00));
	if(name=='killMid') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x51:0x54,(param1 ? 0x7F : 0x00));
	if(name=='killLow') 	 sendMidi(uniqueId,0x90,0,(deck==0)?0x52:0x55,(param1 ? 0x7F : 0x00));
	
	if(name=='loopIn')
	{
		// buttons leds
		sendMidi(uniqueId,0x90,0,(deck==0)?0x3A:0x4A,(param1 ? 0x7F : 0x00));// loop in
		loopInEnabled[deck] = param1;
	}
	if(name=='looping')
	{
		// buttons leds
		sendMidi(uniqueId,0x90,0,(deck==0)?0x3A:0x4A,(param1 ? 0x7F : 0x00));// loop in
		sendMidi(uniqueId,0x90,0,(deck==0)?0x3B:0x4B,(param1 ? 0x7F : 0x00));// loop exit(out)
	}	
}
//----------------------------------------------------------------------------- 
// 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+']');
	// limit the amount of time this gets executed
	timer1++; if(timer1<4) return;else timer1 = 0;
	//
	for(var deck=0; deck<2; deck++)
	{
		if(loopInEnabled[deck]) sendMidi(uniqueId,0x90,0,(deck==0)?0x3A:0x4A,(loopInPulse ? 0x7F : 0x00));// loop in
	}
	if(loopInPulse==0) loopInPulse = 1;else loopInPulse = 0;
}
//----------------------------------------------------------------------------- 
