//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2012. All Rights Reserved. -----------------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Vestax Typhoon'
//----------------------------------------------------------------------------- 
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 "TYPHOON DJ-Controller";
}
//----------------------------------------------------------------------------- 
// 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 "0040022001";// short 5-bytes sysex id (we don't use this since it's the same as 'Vestax VCI-100 MK2')
	return "0040022001000000290020";// the 'almost' full sysex id

	// ATTN: normally we will only use 5 bytes (10 hexa characters) but there are cases when 2 otherwise 
	// very different controllers (even from different companies) have the exact same sysex id for the first 5 bytes
	// example:
	//return "0040022001000000290020F7";// full sysex id reply from 'Vestax Typhoon'
	//return "0040022001000000290013F7";// full sysex id reply from 'Vestax VCI-100 MK2'
	//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 "TYPHOON DJ-Controller";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	//return "TYPHOON DJ-Controller";
	return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var jogWheelsInScratchMode=[0,0,0,0];
var jogWheelsWasDeckPlaying=[0,0,0,0];
var filterOn=[0,0];
var filterValue=[127,127];
//----------------------------------------------------------------------------- 
// 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 TYPHOON ['+uniqueId+']');// this outputs a string to DebugView app on Win
	for(var i=0; i<2; i++)
	{
		
		sendMidi(uniqueId,0x90,i,0x32,(getValue('play', i) ? 0x7F : 0x00));// play-pause
		sendMidi(uniqueId,0x90,i,0x35,(getValue('cue', i) ? 0x7F : 0x00));// cue
		sendMidi(uniqueId,0x90,i,0x33,(getValue('cuepos1', i) ? 0x7F : 0x00));// cup
		sendMidi(uniqueId,0x90,i,0x25,(getValue('monitor', i) ? 0x7F : 0x00));// monitor
		sendMidi(uniqueId,0x90,i,0x46,(getValue('onbeat', i) ? 0x7F : 0x00));// sync
		sendMidi(uniqueId,0x90,i,0x20,(getValue('looping', i) ? 0x7F : 0x00));// loop
		
		
		
		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 MC1000 ['+uniqueId+']');
	
	for(var i=0; i<2; i++)
	{
		
		sendMidi(uniqueId,0x90,i,0x32,0x00);// play-pause
		sendMidi(uniqueId,0x90,i,0x35,0x00);// cue 
		sendMidi(uniqueId,0x90,i,0x33,0x00);// cup 
		sendMidi(uniqueId,0x90,i,0x46,0x00);// sync
		sendMidi(uniqueId,0x90,i,0x25,0x00);// headphone
		sendMidi(uniqueId,0x90,i,0x20,0x00);// loop
		sendMidi(uniqueId,0x90,2,0x28,0x00);// flanger 1
		sendMidi(uniqueId,0x90,2,0x29,0x00);// flanger 2
		
		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 0x23: call('lbMoveCursor', data2==0x41 ? 1:-1); break; //library curser up down
		
		case 0x08: if(channel==2) call('setCrossfader', data2); break; //crossfader


		// decks
		case 0x0C: call('setDeckVolume', channel, data2); break; // deck volume slider
		case 0x14: call('setDeckGain', channel, data2); break; // deck gain slider
		case 0x0E: call('setDeckPitch', channel, 0x7F-data2); break; //pitch slider
		case 0x15: call('setDeckEqHigh', channel, data2); break; // eq high
		case 0x16: call('setDeckEqMid', channel, data2); break; // eq mid
		case 0x17: call('setDeckEqLow', channel, data2); break; // eq low
		
		case 0x51: if(channel==2) call('deckFxFlangerDW', 0, data2); break; // deck 1 flanger dw
		case 0x52: if(channel==2) call('deckFxFlangerDW', 1, data2); break; // deck 2 flanger dw
		
		case 0x10: // params: (deck, increment, total positions per complete revolution) 
			var val1 = data2-0x40;
			if(filterOn[channel])
			{
				// modify the value
				filterValue[channel] += val1;
				// make sure the value stays between 0 and 127
				if(filterValue[channel]<0) filterValue[channel] = 0;
				if(filterValue[channel]>127*2) filterValue[channel] = 127*2;
				// use the value (to set the cutoff filter)
				call('setDeckCutoff', channel, filterValue[channel]/2);
			}else
			{
				if(jogWheelsInScratchMode[channel]) call('scratchDeckJW', channel, val1, 330);else call('moveDeckJogWheel', channel, val1, 330);
				//call('holdDeckJogWheel', channel-2, 1);call('holdDeckJogWheel', channel-2, 0);
			}
			break;	
				

		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	
	case 0x90:
		//if(status==0x90) data2 = 0x7F;
		//if(status==0x80) data2 = 0;
		switch(data1)
		{
		// global
		
		case 0x5C: if(data2>0x3F) call('lbMoveCursor', -1); break; // lb curser up
		case 0x5D: if(data2>0x3F) call('lbMoveCursor', +1); break; //lb curser down
		case 0x26: call('lbToggleSelect', data2); break; //toggle playlist / file browser
		
		//case 0x00: if((status==0x90)&&(data2>0x3F)) call('lbToggleSelect', data2); break;
		//case 0x59: if(data2>0x3F) call('lbBrowserSelect', data2); break;
		//case 0x5A: if(data2>0x3F) call('lbListSelect', data2); break;

		
		// decks
		case 0x24: if(data2>0x3F) filterOn[channel] = 1;else filterOn[channel] = 0; break;
		
		case 0x60: call('deckLBLoad', 0, data2); break; //load deck 1
		case 0x61: call('deckLBLoad', 1, data2); break; //load deck 2
		case 0x25: call('deckMonitor', channel, data2); break; // deck headphone cue
		
		
		case 0x32: call('deckPlayPause', channel, data2); break; //play pause
		case 0x35: call('deckCue', channel, data2); break; //cue 
		case 0x33: call('deckCuePos1', channel, data2); break; //cue 1
		
		case 0x36: call('deckReverse', channel, data2); break; //shift + = deck Play reverse
		case 0x37: call('deckRev', channel, data2); break; //shift + Cue = deck rev
		case 0x38: call('deckFwd', channel, data2); break; // shif + Cup== deck FFW
		
		case 0x46: call('deckSync', channel, data2); break;
		case 0x22: call('deckBendDown', channel, data2); break;
		case 0x22: call('deckBendDown', channel, data2); break;
		
		case 0x20: if(!getValue('looping', channel)) call('deckLoopInEnd', channel, data2);else call('deckLoopExit', channel, data2); break; // loop 4 beat ask about if statement
		case 0x21: call('deckLoopLess', channel, data2); break;
		case 0x42: call('deckLoopMore', channel, data2); break;
		
		case 0x29: if(channel==2) call('deckFxFlanger', 0, data2); break; //flanger deck 1 on/off
		case 0x28: if(channel==2) call('deckFxFlanger', 1, data2); break; //flanger deck 2 on/off

		case 0x2E: 
			jogWheelsInScratchMode[channel] = (jogWheelsInScratchMode[channel]+1) % 2; // keep the remainder to toggle between scratch mode "0" or jog bend mode "1"
			call('holdDeckJogWheel', channel, jogWheelsInScratchMode[channel]);	
			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);
	
	if(deck<2)
	{
	
	if(deck==0)
		{
		if(name=='fxFlanger')	 sendMidi(uniqueId,0x90,2,0x29,(param1 ? 0x7F : 0x00)); //Flanger deck 1 
		}
		
	if(deck==1)
		{
		if(name=='fxFlanger')	 sendMidi(uniqueId,0x90,2,0x28,(param1 ? 0x7F : 0x00)); //Flanger deck 2
		}	
		
		if(name=='play') 		 sendMidi(uniqueId,0x90,deck,0x32,(param1 ? 0x7F : 0x00)); // cue
		if(name=='cue') 	 	 sendMidi(uniqueId,0x90,deck,0x35,(param1 ? 0x7F : 0x00)); // cue
		if(name=='cuepos1') 	 sendMidi(uniqueId,0x90,deck,0x33,(param1 ? 0x7F : 0x00)); // cue 1
		if(name=='onbeat')		 sendMidi(uniqueId,0x90,deck,0x46,(param1 ? 0x7F : 0x00)); // sync 
		if(name=='monitor')		 sendMidi(uniqueId,0x90,deck,0x25,(param1 ? 0x7F : 0x00)); // headphone
		if(name=='looping')		 sendMidi(uniqueId,0x90,deck,0x20,(param1 ? 0x7F : 0x00)); //looping
		
		
	}
}
//----------------------------------------------------------------------------- 
var vuLast=[[0,0,0,0,0],[0,0,0,0,0]];// used to save the last state
//-----------------------------------------------------------------------------
function setVUValueForDeck(uniqueId, deck, dbValue)
{
	var vuCurrent=[0,0,0,0,0];// temp
	if(dbValue>-36) vuCurrent[0] = 0x7F;
	if(dbValue>-20) vuCurrent[1] = 0x7F;
	if(dbValue>-12) vuCurrent[2] = 0x7F;
	if(dbValue>-4)  vuCurrent[3] = 0x7F;
	if(dbValue>-1)  vuCurrent[4] = 0x7F;
	for(var i=0; i<5; i++)
	{
		if(vuLast[deck][i] != vuCurrent[i])
		{
			vuLast[deck][i] = vuCurrent[i];
			sendMidi(uniqueId,0x90,deck,0x29+i,vuLast[deck][i]);// 0x2D bottom .. 0x29 top
		}
	}	
}
//----------------------------------------------------------------------------- 
// 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 deck=0; deck<2; deck++)
	{
		var dbValue = getValue('vumeterDeck', deck);// float dB
		setVUValueForDeck(uniqueId, deck, dbValue);
	}
}
//----------------------------------------------------------------------------- 
//sendMidi(uniqueId,0x90,i,0x20,(getValue('looping', i) ? 0x7F : 0x00));// loop
