//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2011. All Rights Reserved. -----------------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Vestax VCI-400'
//----------------------------------------------------------------------------- 
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 "Vestax VCI-400";
}
//----------------------------------------------------------------------------- 
// 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 "0040023002";
}
//----------------------------------------------------------------------------- 
// 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 "Vestax VCI-400";// different name on Mac
	//return "Vestax MIDI In";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	if(osId==1) return "Vestax VCI-400";// different name on Mac
	return "Vestax MIDI Out";
	//return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var jogWheelsInScratchMode=[0,0,0,0];
var jogWheelsWasDeckPlaying=[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 VCI400 ['+uniqueId+']');// this outputs a string to DebugView app on Win
	for(var i=0; i<4; i++)
	{
	
		if(i>=1)
		{
		sendMidi(uniqueId,0x90,2+i,0x19,(getValue('cue', i) ? 0x7F : 0x00));// cue
		sendMidi(uniqueId,0x90,2+i,0x1A,(getValue('play', i) ? 0x7F : 0x00));// play-pause
		}
		if(i==2)
		{
		sendMidi(uniqueId,0x90,2+i,0x1B,(getValue('cue', i) ? 0x7F : 0x00));// cue deck 3 
		sendMidi(uniqueId,0x90,2+i,0x1C,(getValue('play', i) ? 0x7F : 0x00));// play-pause deck 3
		}
		if(i==3)
		{
		sendMidi(uniqueId,0x90,2+i,0x1D,(getValue('cue', i) ? 0x7F : 0x00));// cue deck 4 
		sendMidi(uniqueId,0x90,2+i,0x1E,(getValue('play', i) ? 0x7F : 0x00));// play-pause deck 4 
		}
		sendMidi(uniqueId,0x90,2+i,0x01,(getValue('onbeat', i) ? 0x7F : 0x00));// sync
		
		
		
		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 VCI400 ['+uniqueId+']');
	sendMidi(uniqueId,0xB0,0xE,43,0);
	sendMidi(uniqueId,0xB0,0xE,44,0);
	for(var i=0; i<4; i++)
	{
		sendMidi(uniqueId,0x90,2+i,0x19,0x00);// cue
		sendMidi(uniqueId,0x90,2+i,0x1A,0x00);// play-pause
		sendMidi(uniqueId,0x90,2+i,0x1B,0x00);// cue deck 3 /4
		sendMidi(uniqueId,0x90,2+i,0x1C,0x00);// play-pause deck 3 /4
		sendMidi(uniqueId,0x90,2+i,0x1D,0x00);// cue deck 4
		sendMidi(uniqueId,0x90,2+i,0x1E,0x00);// play-pause deck 4
		
		sendMidi(uniqueId,0x90,2+i,0x01,0x00);// sync 
		sendMidi(uniqueId,0x90,2+i,0x07,0x00);// cue 1
		sendMidi(uniqueId,0x90,2+i,0x08,0x00); //cue 2
		sendMidi(uniqueId,0x90,2+i,0x09,0x00); //cue 3
		sendMidi(uniqueId,0x90,2+i,0x0A,0x00); //cue 4
		sendMidi(uniqueId,0x90,2+i,0x0B,0x00); // loop
		sendMidi(uniqueId,0x90,2+i,0x0C,0x00); // loop
		sendMidi(uniqueId,0x90,2+i,0x0D,0x00);  // loop less
		sendMidi(uniqueId,0x90,2+i,0x0E,0x00);  // loop more 
		sendMidi(uniqueId,0x90,2+i,0x15,0x00);  //loop 4
		sendMidi(uniqueId,0x90,2+i,0x16,0x00);  //loop 8
		sendMidi(uniqueId,0x90,2+i,0x17,0x00);  //loop 16
		sendMidi(uniqueId,0x90,2+i,0x18,0x00);  //loop 32
		
		sendMidi(uniqueId,0x90,2+i,0x03,0x00);  //brake
		sendMidi(uniqueId,0x90,2+i,0x04,0x00);  //reverse
		
		sendMidi(uniqueId,0x90,2+i,0x05,0x00);  //PFL
		
		

		
		

		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 0x2B: if(channel==14) call('setMasterGain', data2); break; //master volume
		case 0x14: if(channel==0) call('setCrossfader', data2); break; //crossfader
		case 0x2A: if(channel==14) call('setCuePgmMix', data2); break; //cue mix
		case 0x29: if(channel==14) call('setCFCurve', data2); break; //crossfader curve
		case 0x28: if(channel==14) call('lbMoveCursor', data2==0x7F ? -1:1); break; //library curser up down
		


		// decks
		case 0x11: call('setDeckVolume', channel-2, data2); break; //volume slider
		case 0x12: call('setDeckPitch', channel-2, data2); break; //pitch slider
		case 0x0C: call('setDeckGain', channel-2, data2); break; //trim
		case 0x0D: call('setDeckEqHigh', channel-2, data2); break; // eq high
		case 0x0E: call('setDeckEqMid', channel-2, data2); break; // eq mid
		case 0x0F: call('setDeckEqLow', channel-2, data2); break; // eq low
		
		case 0x10: call('setDeckCutoff', channel-2, data2); break; // Hp filter
		
		case 0x01: call('deckFxFlangerP1', channel-12, data2); break; //flanger deck 1/2 P2
		case 0x02: call('deckFxPhaserP1', channel-12, data2); break; //phazer deck 1/2 P2
		case 0x03: call('deckFxEchoP1', channel-12, data2); break; //echo deck 1/2 P2
		
			

	
				
		case 0x13: // params: (deck, increment, total positions per complete revolution) 
			var val1 = data2-0x40;
			if(jogWheelsInScratchMode[channel-2]) call('scratchDeckJW', channel-2, val1, 2300);else call('moveDeckJogWheel', channel-2, val1, 2300);
			//call('holdDeckJogWheel', channel-2, 1);call('holdDeckJogWheel', channel-2, 0);
			break;		
				
		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 0x6A: call('phonesSplit', data2); break; //split cue
		case 0x69: call('phonesSplit', data2); break; //split cue again button transmits 23 notes
		case 0x71: call('lbToggleSelect', data2); break; // toggle playlist / fileview
		//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 0x27: 
			jogWheelsInScratchMode[channel-2] = (jogWheelsInScratchMode[channel-2]+1) % 2; // keep the remainder to toggle between scratch mode "0" or jog bend mode "1"
			call('holdDeckJogWheel', channel-2, jogWheelsInScratchMode[channel-2]);
			//if(jogWheelsInScratchMode[channel-2]==0) jogWheelsInScratchMode[channel-2] = 1;else jogWheelsInScratchMode[channel-2] = 0; (same as line above just more) 

			break;
		
		case 0x19: call('deckCue', channel-2, data2); break; //cue decks 1/2
		case 0x1A: call('deckPlayPause', channel-2, data2); break; //play pause 1/2
		
		
		case 0x1B: if(channel==4) call('deckCue', 2, data2); break; //cue decks 3
		case 0x1C: if(channel==4) call('deckPlayPause', 2, data2); break; //play pause 3
		
		case 0x1D: if(channel==5) call('deckCue', 3, data2); break; //cue decks 4
		case 0x1E: if(channel==5) call('deckPlayPause', 3, data2); break; //play pause 4
		

		case 0x07: call('deckCuePos1', channel-2, data2); break; //cue1
		case 0x08: call('deckCuePos2', channel-2, data2); break; //cue2
		case 0x09: call('deckCuePos3', channel-2, data2); break; //cue3
		case 0x0A: call('deckCuePos4', channel-2, data2); break; //cue4
		case 0x0B: call('deckLoopInEnd', channel-2, data2); break; //loop in / out
		case 0x0C: call('deckLoopExit', channel-2, data2); break; //exit
		case 0x0D: call('deckLoopLess', channel-2, data2); break; //loop less
		case 0x0E: call('deckLoopMore', channel-2, data2); break; //loop more
		
		case 0x05: call('deckMonitor', channel-2, data2); break; // PFL
		
		case 0x15: call('deckLoop4', channel-2, data2); break; //  loop 4
		case 0x16: call('deckLoop8', channel-2, data2); break; //  loop 8
		case 0x17: call('deckLoop16', channel-2, data2); break; //  loop 16
		case 0x18: call('deckLoop32', channel-2, data2); break; //  loop 32
		
		
	    case 0x04: call('deckReverse', channel-2, data2); break; //  reverse
	    
		case 0x6B: call('deckKeyLock', 0, data2); break; //  Keylock deck1
		case 0x6C: call('deckKeyLock', 1, data2); break; //  Keylock deck2
		case 0x6D: call('deckKeyLock', 2, data2); break; //  Keylock deck3
		case 0x6E: call('deckKeyLock', 3, data2); break; //  Keylock deck4
				
		case 0x01:
			if(channel>=12) call('deckFxFlanger', channel-12, data2);else call('deckSync', channel-2, data2); break; // sync on decks 1-4 and flanger decks 1/2
			
		case 0x02:
			if(channel>=12) call('deckFxPhaser', channel-12, data2);else call('deckLBLoad', channel-2, data2); break; //phaser decks 1/2 or load decks 1-4
			
		case 0x03:
			if(channel>=12) call('deckFxEcho', channel-12, data2);else call('deckBrake', channel-2, data2); break; // echo FX decks 1/2 else Brake decks 1-4
	
		
	
		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<4)
	{
		
		if(deck<=1)
		{
		if(name=='cue') 		 sendMidi(uniqueId,0x90,2+deck,0x19,(param1 ? 0x7F : 0x00)); // cue 1/2
		if(name=='play') 		 sendMidi(uniqueId,0x90,2+deck,0x1A,(param1 ? 0x7F : 0x00)); //play 1/2
		}
		
		if(deck==2)
		{
		if(name=='cue') 		 sendMidi(uniqueId,0x90,4,0x1B,(param1 ? 0x7F : 0x00)); // cue deck 3
		if(name=='play') 		 sendMidi(uniqueId,0x90,4,0x1C,(param1 ? 0x7F : 0x00)); //play deck 3
		}
		
		if(deck==3)
		{
		if(name=='cue') 		 sendMidi(uniqueId,0x90,5,0x1D,(param1 ? 0x7F : 0x00)); // cue deck 4
		if(name=='play') 		 sendMidi(uniqueId,0x90,5,0x1E,(param1 ? 0x7F : 0x00)); //play deck 4
		}
		
		
		if(name=='monitor')  	 sendMidi(uniqueId,0x90,2+deck,0x05,(param1 ? 0x7F : 0x00)); // deck PFL
		if(name=='onbeat') 	 	 sendMidi(uniqueId,0x90,2+deck,0x01,(param1 ? 0x7F : 0x00)); //sync on beat
		
		
		if(name=='cuepos1') 	 sendMidi(uniqueId,0x90,2+deck,0x07,(param1 ? 0x7F : 0x00)); //cue 1
		if(name=='cuepos2') 	 sendMidi(uniqueId,0x90,2+deck,0x08,(param1 ? 0x7F : 0x00)); //cue 2
		if(name=='cuepos3') 	 sendMidi(uniqueId,0x90,2+deck,0x09,(param1 ? 0x7F : 0x00)); //cue 3
		if(name=='cuepos4') 	 sendMidi(uniqueId,0x90,2+deck,0x0A,(param1 ? 0x7F : 0x00)); //cue 4
		if(name=='loopIn') 	     sendMidi(uniqueId,0x90,2+deck,0x0B,(param1 ? 0x7F : 0x00)); //loop in 
		if(name=='looping') 	 sendMidi(uniqueId,0x90,2+deck,0x0C,(param1 ? 0x7F : 0x00)); //looping
		//if(name=='cuepos7') 	 sendMidi(uniqueId,0x90,2+deck,0x0D,(param1 ? 0x7F : 0x00)); // cue 7
		//if(name=='cuepos8') 	 sendMidi(uniqueId,0x90,2+deck,0x0E,(param1 ? 0x7F : 0x00)); //cue 8
		
		if(name=='keyLock')		 sendMidi(uniqueId,0x90,14,107+deck,(param1 ? 0x7F : 0x00)) //keylock
		
		if(name=='brake')		 sendMidi(uniqueId,0x90,2+deck,0x03,(param1 ? 0x7F : 0x00)) //brake
		if(name=='reverse')		 sendMidi(uniqueId,0x90,2+deck,0x04,(param1 ? 0x7F : 0x00)) //reverse
		
		if(name=='fxFlanger')	 sendMidi(uniqueId,0x90,12+deck,0x01,(param1 ? 0x7F : 0x00)) //flanger
		if(name=='fxPhaser')	 sendMidi(uniqueId,0x90,12+deck,0x02,(param1 ? 0x7F : 0x00)) //phazer
		if(name=='fxEcho')	     sendMidi(uniqueId,0x90,12+deck,0x03,(param1 ? 0x7F : 0x00)) //echo
		
		

		
		if(name=='loopSize')
		{
			// calculate the new led to power on
			var l4, l8, l16, l32;
			l4 = l8 = l16 = l32 = 0;
			switch(param1)
			{
			case  256: l4 = 0x7F; break; //loop 4
			case  512: l8 = 0x7F; break; //loop 8
			case 1024: l16 = 0x7F; break; //loop 16
			case 2048: l32 = 0x7F; break; //loop 32
			}		
			sendMidi(uniqueId,0x90,2+deck,21,l4); //loop 4
			sendMidi(uniqueId,0x90,2+deck,22,l8); //loop 8 
			sendMidi(uniqueId,0x90,2+deck,23,l16); //loop 16
			sendMidi(uniqueId,0x90,2+deck,24,l32); //loop 32
		}
		// light off the leds
		if((name=='looping')&&(!param1))
		{
			sendMidi(uniqueId,0x90,2+deck,21,0); //loop 4
			sendMidi(uniqueId,0x90,2+deck,22,0); //loop 8 
			sendMidi(uniqueId,0x90,2+deck,23,0); //loop 16
			sendMidi(uniqueId,0x90,2+deck,24,0); //loop 32
		}
		
		
	}
}
//----------------------------------------------------------------------------- 
function getMIDIValueForVU(vu_val)
{
	// translating VU-meter signal from decibels (dB) to 0..127 MIDI range
	var val = 0;
	var step = 12.7;
	if(vu_val>-44) val =  1 * step;
	if(vu_val>-36) val =  2 * step;
	if(vu_val>-28) val =  3 * step;
	if(vu_val>-20) val =  4 * step;
	if(vu_val>-16) val =  5 * step;
	if(vu_val>-12) val =  6 * step;
	if(vu_val> -8) val =  7 * step;
	if(vu_val> -4) val =  8 * step;
	if(vu_val> -2) val =  9 * step;
	if(vu_val> -1) val = 10 * step;
	return Math.floor(val);
}
//----------------------------------------------------------------------------- 
// 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+']');
	var vuMasterL = getValue('vumeterMasterL');// float dB
	var vuMasterR = getValue('vumeterMasterR');// float dB
	sendMidi(uniqueId,0xB0,0xE,43,getMIDIValueForVU(vuMasterL));
	sendMidi(uniqueId,0xB0,0xE,44,getMIDIValueForVU(vuMasterR));
	for(var i=0; i<4; i++)
	{
		var vuDeck = getValue('vumeterDeck', i);// float dB
		sendMidi(uniqueId,0xB0,i+2,17,getMIDIValueForVU(vuDeck));
	}
}
//----------------------------------------------------------------------------- 
