//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2011. All Rights Reserved. -----------------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller ' Akiyama Quattro "
//version 1.01 2013 11 6
//Author: Brandon James 
//----------------------------------------------------------------------------- 
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 "Akiyama Quattro";
}
//----------------------------------------------------------------------------- 
// 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 "00206E190F";
	// complete string 00  F0 7E 00 06 02 00 20 6E  19 0F 07 07 00 02 00 03 10  F7
	//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 "Midi port name ";// !!! 
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	//return " midi port name ";// !!! MAKE SURE THIS IS INDEED OK !!!
	return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var shiftButton=0;
var loopingInBlinkLed=[-1,-1,-1,-1]; // -1 means disabled
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 Quattro ['+uniqueId+']');// this outputs a string to DebugView app on Win
	callSendAllNotifications(uniqueId, true);
	
	for(var i=0; i<4; i++)
	{
		
	
		sendMidi(uniqueId,0x90,i,0x2F,0x00); //scratch 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 Quattro ['+uniqueId+']');
	callSendAllNotifications(uniqueId, false);
	

	
	for(var i=0; i<4; i++)
	{
		
		
		sendMidi(uniqueId,0x90,i,0x2F,0x00); //scratch 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 0xE0:
		if(channel==0) call('setDeckPitch', 0, data2);
		if(channel==1) call('setDeckPitch', 1, data2);
		if(channel==2) call('setDeckPitch', 2, data2);
		if(channel==3) call('setDeckPitch', 3, data2);
		if(channel==4) call('setCrossfader', data2);
		break; // crossfader / deck pitch sliders / encoders
		
	case 0xB0:
		switch(data1)
		{
		
		// global
		case 0x05: call('lbMoveCursor', data2==0x41 ? 1:-1); break; //library curser up down		
		case 0x3A: call('setCuePgmMix', data2); break; //pfl split
		case 0x3B: call('setMonitorGain', data2); break; //PFL Volume
		case 0x39: call('setMasterGain', data2); break; //PFL Volume

		// decks
		//Deck A
		case 0x26: call('setDeckVolume', 0, data2); break; //volume slider
		case 0x21: call('setDeckGain', 0, data2); break; //pitch slider
		case 0x22: call('setDeckEqHigh', 0, data2); break; // eq high
		case 0x23: call('setDeckEqMid', 0, data2); break; // eq mid
		case 0x24: call('setDeckEqLow', 0, data2); break; // eq low
		case 0x25: call('setDeckCutoff', 0, data2); break; // Hp filter
		
		//Deck B
		case 0x2C: call('setDeckVolume', 1, data2); break; //volume slider
		case 0x27: call('setDeckGain', 1, data2); break; //pitch slider
		case 0x28: call('setDeckEqHigh', 1, data2); break; // eq high
		case 0x29: call('setDeckEqMid', 1, data2); break; // eq mid
		case 0x2A: call('setDeckEqLow', 1, data2); break; // eq low
		case 0x2B: call('setDeckCutoff', 1, data2); break; // Hp filter
		
		//Deck C
		case 0x32: call('setDeckVolume', 2, data2); break; //volume slider
		case 0x2D: call('setDeckGain', 2, data2); break; //pitch slider
		case 0x2E: call('setDeckEqHigh', 2, data2); break; // eq high
		case 0x2F: call('setDeckEqMid', 2, data2); break; // eq mid
		case 0x30: call('setDeckEqLow', 2, data2); break; // eq low
		case 0x31: call('setDeckCutoff', 2, data2); break; // Hp filter
		
		//Deck D
		case 0x38: call('setDeckVolume', 3, data2); break; //volume slider
		case 0x33: call('setDeckGain', 3, data2); break; //pitch slider
		case 0x34: call('setDeckEqHigh', 3, data2); break; // eq high
		case 0x35: call('setDeckEqMid', 3, data2); break; // eq mid
		case 0x36: call('setDeckEqLow', 3, data2); break; // eq low
		case 0x37: call('setDeckCutoff', 3, data2); break; // Hp filter
		
		// per deck by channel
		
		
		case 0x11: if(shiftButton<0x7F) call('deckFxFlangerP1', channel, data2);else call('deckFxFlangerDW', channel, data2); break; // deck flanger DW / P1 hold shift
		case 0x12: if(shiftButton<0x7F) call('deckFxBeatwawP1', channel, data2);else call('deckFxBeatwawDW', channel, data2); break; // deck beat waw DW / P1 hold shift
		case 0x13: if(shiftButton<0x7F) call('deckFxDelayP1', channel, data2);else call('deckFxDelayDW', channel, data2); break; // deck delay DW / P1 hold shift
		case 0x14: if(shiftButton<0x7F) call('setDeckPitchKey', channel, data2);else call('deckFxPhaserP1', channel, data2); break; // deck Key / Phaser p1 + hold shift
		
		
		case 0x10: 
				var val1 = data2-0x40;
				if(jogWheelsInScratchMode[channel]) call('scratchDeckJW', channel, val1, 600);else call('moveDeckJogWheel', channel, val1, 600);
				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 0x60: if(data2>0x3F) call('lbToggleSelect', data2); break; //slection knob press
		case 0x72: shiftButton = data2; sendMidi(uniqueId,0x90,4,0x72,data2); break; //shift button
		case 0x65: call('mixNext', data2); break;
		
		case 0x73: call('deckAnalogIn', 2, data2); break; //analog in deck C
		case 0x74: call('deckAnalogIn', 3, data2); break; //analog in deck D
		
		//sampler ->L / R buttons above channel faders, L to R
		case 0x6E: call('samplerPlay', 1); break; //sampler play 1
		case 0x6F: call('samplerPlay', 2); break; //sampler play 2
		case 0x6A: call('samplerPlay', 3); break; //sampler play 3
		case 0x6B: call('samplerPlay', 4); break; //sampler play 4
		case 0x6C: call('samplerPlay', 5); break; //sampler play 5
		case 0x6D: call('samplerPlay', 6); break; //sampler play 6
		case 0x70: call('samplerPlay', 7); break; //sampler play 7
		case 0x71: call('samplerPlay', 8); break; //sampler play 8
		//case 0x72: ; break; //sampler play 8
		// decks
		

		case 0x28: if(shiftButton<0x7F) call('deckCuePos1', channel, data2);else call('deckCuePos1Clear', channel, data2); break; //cue1
		case 0x29: if(shiftButton<0x7F) call('deckCuePos2', channel, data2);else call('deckCuePos2Clear', channel, data2); break; //cue2
		case 0x2A: if(shiftButton<0x7F) call('deckCuePos3', channel, data2);else call('deckCuePos3Clear', channel, data2); break; //cue3
		case 0x2B: if(shiftButton<0x7F) call('deckCuePos4', channel, data2);else call('deckCuePos4Clear', channel, data2); break; //cue4
		
		case 0x31: call('deckSync', channel, data2); break; //sync
		case 0x32: call('deckCue', channel, data2); break; // deck cue
		case 0x33: call('deckPlayPause', channel, data2); break; //play pause
		case 0x53: call('deckReverse', channel, data2); break; //reverse (play button + Shift diff note)
		case 0x52: call('deckBrake', channel, data2); break; //brake (cue button + Shift diff note)
		case 0x51: call('deckMatch', channel, data2); break; //match (cue button + Shift diff note)
		
		case 0x20: if(shiftButton<0x7F) call('deckBendDown', channel, data2);else
					if(data2>0x3F)
					{ 
					var currentPitchRange = getValue('pitchRange');
					currentPitchRange = currentPitchRange + 1; if(currentPitchRange>6) currentPitchRange = 1;
					call('setPitchRange', currentPitchRange);
					} break; //bend down / pitch range select decks left
					
		case 0x21: if(shiftButton<0x7F) call('deckBendUp', channel, data2);else
					if(data2>0x3F)
					{ 
					var currentPitchRange = getValue('pitchRange');
					currentPitchRange = currentPitchRange + 1; if(currentPitchRange>6) currentPitchRange = 1;
					call('setPitchRange', currentPitchRange);
					} break; //bend up / pitch range select decks left
		case 0x22: if(shiftButton<0x7F) call('deckBendDown', channel, data2);else
					if(data2>0x3F)
					{ 
					var currentPitchRange = getValue('pitchRange');
					currentPitchRange = currentPitchRange + 1; if(currentPitchRange>6) currentPitchRange = 1;
					call('setPitchRange', currentPitchRange);
					} break; //bend down / pitch range select decks right
					
		case 0x23: if(shiftButton<0x7F) call('deckBendUp', channel, data2);else
					if(data2>0x3F)
					{ 
					var currentPitchRange = getValue('pitchRange');
					currentPitchRange = currentPitchRange + 1; if(currentPitchRange>6) currentPitchRange = 1;
					call('setPitchRange', currentPitchRange);
					} break; //bend up / pitch range select decks right (why are they different? lol)
					
					
		case 0x2D: call('deckLoopInEnd', channel, data2); break; //Loop In / end
		case 0x2E: call('deckLoopExitOrReloop', channel, data2); break; //Loop exit or reloop
		case 0x35: call('deckLoopLess', channel, data2); break; //Loop /2
		case 0x36: call('deckLoopMore', channel, data2); break; //Loop X2
		case 0x30: if(shiftButton<0x7F) call('deckKeyLock', channel, data2);else call('deckResetPitch', channel, data2); break; //MT / reset pitch
		
		case 0x61: call('deckLBLoad', 0, data2); break; //load deck A
		case 0x62: call('deckLBLoad', 1, data2); break; //load deck B
		case 0x63: call('deckLBLoad', 2, data2); break; //load deck C
		case 0x64: call('deckLBLoad', 3, data2); break; //load deck D
		
		case 0x66: call('deckMonitor', 0, data2); break; //pfl deck A
		case 0x67: call('deckMonitor', 1, data2); break; //pfl deck B
		case 0x68: call('deckMonitor', 2, data2); break; //pfl deck C
		case 0x69: call('deckMonitor', 3, data2); break; //pfl deck D
		
		case 0x2F: if(shiftButton<0x7F)
					 {
						if(data2>0x3F) 
						{
						jogWheelsInScratchMode[channel] = (jogWheelsInScratchMode[channel]+1)%2; sendMidi(uniqueId,0x90,channel,0x2F,(jogWheelsInScratchMode[channel] ? 0x7F : 0x00)); 
						}
						else call('deckTapBpm', channel, data2); // tap BPM
					}break; 

		
		case 0x24: call('deckFxFlanger', channel, data2); break; //deck flanger on/ off FX button 1
		case 0x25: call('deckFxBeatwaw', channel, data2); break; //deck beat waw on/ off FX button 2
		case 0x26: call('deckFxDelay', channel, data2); break; //deck beat waw on/ off FX button 3
		case 0x27: call('deckFxPhaser', channel, data2); break; //deck phaser on/ off FX button 3 note: no P1 as the knob above is for key
		
		case 0x3B: if(jogWheelsInScratchMode[channel]) call('holdDeckJogWheel', channel, 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)
{
	//debug('notif ['+uniqueId+'] name='+name + ' deck='+deck + ' p1(int)='+param1 + ' p2(float)='+param2 + ' p3(str)='+param3);
	if(deck<4)
	{
		if(name=='onbeat') 		 sendMidi(uniqueId,0x90,deck,0x31,(param1 ? 0x7F : 0x00)); //play
		if(name=='cue') 		 sendMidi(uniqueId,0x90,deck,0x32,(param1 ? 0x7F : 0x00)); // cue
		if(name=='play') 		 sendMidi(uniqueId,0x90,deck,0x33,(param1 ? 0x7F : 0x00)); //play
	
		if(name=='cuepos1') 	 sendMidi(uniqueId,0x90,deck,0x28,(param1 ? 0x7F : 0x00)); //cue 1
		if(name=='cuepos2') 	 sendMidi(uniqueId,0x90,deck,0x29,(param1 ? 0x7F : 0x00)); //cue 2
		if(name=='cuepos3') 	 sendMidi(uniqueId,0x90,deck,0x2A,(param1 ? 0x7F : 0x00)); //cue 3
		if(name=='cuepos4') 	 sendMidi(uniqueId,0x90,deck,0x2B,(param1 ? 0x7F : 0x00)); //cue 4
		if(name=='keyLock') 	 sendMidi(uniqueId,0x90,deck,0x30,(param1 ? 0x7F : 0x00)); //cue 3
		
		if(name=='sampler1') 	 sendMidi(uniqueId,0x90,4,0x6E,(param1 ? 0x7F : 0x00)); //sampler 1
		if(name=='sampler2') 	 sendMidi(uniqueId,0x90,4,0x6F,(param1 ? 0x7F : 0x00)); //sampler 2
		if(name=='sampler3') 	 sendMidi(uniqueId,0x90,4,0x6A,(param1 ? 0x7F : 0x00)); //sampler 3
		if(name=='sampler4') 	 sendMidi(uniqueId,0x90,4,0x6B,(param1 ? 0x7F : 0x00)); //sampler 4
		if(name=='sampler5') 	 sendMidi(uniqueId,0x90,4,0x6C,(param1 ? 0x7F : 0x00)); //sampler 5
		if(name=='sampler6') 	 sendMidi(uniqueId,0x90,4,0x6D,(param1 ? 0x7F : 0x00)); //sampler 6
		if(name=='sampler7') 	 sendMidi(uniqueId,0x90,4,0x70,(param1 ? 0x7F : 0x00)); //sampler 7
		if(name=='sampler8') 	 sendMidi(uniqueId,0x90,4,0x71,(param1 ? 0x7F : 0x00)); //sampler 8
		
		if(name=='fxFlanger') 	 sendMidi(uniqueId,0x90,deck,0x24,(param1 ? 0x7F : 0x00)); //deck flanger
		if(name=='fxBeatwaw') 	 sendMidi(uniqueId,0x90,deck,0x25,(param1 ? 0x7F : 0x00)); //deck beatwaw
		if(name=='fxDelay') 	 sendMidi(uniqueId,0x90,deck,0x26,(param1 ? 0x7F : 0x00)); //deck delay
		if(name=='fxPhaser') 	 sendMidi(uniqueId,0x90,deck,0x27,(param1 ? 0x7F : 0x00)); //deck phazer
		
		if(name=='pitchIs0')	if(deck==0)
								{
								 sendMidi(uniqueId,0x90,0,0x3A,(param1 ? 0x7F : 0x00)); //left deck bend down led
								 sendMidi(uniqueId,0x90,0,0x3B,(param1 ? 0x7F : 0x00)); //left deck bend up led
								 }
								if(deck==1)
								{
								 sendMidi(uniqueId,0x90,1,0x3C,(param1 ? 0x7F : 0x00)); //left deck bend down led
								 sendMidi(uniqueId,0x90,1,0x3D,(param1 ? 0x7F : 0x00)); //left deck bend up led
								 }
								 if(deck==2)
								{
								 sendMidi(uniqueId,0x90,2,0x3A,(param1 ? 0x7F : 0x00)); //left deck bend down led
								 sendMidi(uniqueId,0x90,2,0x3B,(param1 ? 0x7F : 0x00)); //left deck bend up led
								 }
								 if(deck==3)
								{
								 sendMidi(uniqueId,0x90,3,0x3C,(param1 ? 0x7F : 0x00)); //left deck bend down led
								 sendMidi(uniqueId,0x90,3,0x3D,(param1 ? 0x7F : 0x00)); //left deck bend up led
								 } 
								 								 
							 
		
		if(name=='monitor') //pfl led
				{
					if(deck==0)	sendMidi(uniqueId,0x90,4,0x66,(param1 ? 0x7F : 0x00));	
					if(deck==1)	sendMidi(uniqueId,0x90,4,0x67,(param1 ? 0x7F : 0x00));
					if(deck==2)	sendMidi(uniqueId,0x90,4,0x68,(param1 ? 0x7F : 0x00));
					if(deck==3)	sendMidi(uniqueId,0x90,4,0x69,(param1 ? 0x7F : 0x00));
				}
		
		if(name=='loopIn')
			{
			sendMidi(uniqueId,0x90,deck,0x2D,(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,deck,0x2D,0x00);// Loop In (off)		
			sendMidi(uniqueId,0x90,deck,0x2E,(param1 ? 0x7F : 0x00));// Loop Out (on / off)
			sendMidi(uniqueId,0x90,deck,0x34,(param1 ? 0x7F : 0x00));// loop less (on / off)
			sendMidi(uniqueId,0x90,deck,0x35,(param1 ? 0x7F : 0x00));// Loop more (on / 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<4; i++)
	{
	
			// loop in led
		if(loopingInBlinkLed[i]!=-1)
		{
			// actually set the led
			sendMidi(uniqueId,0x90,i,0x2D,((loopingInBlinkLed[i]<2) ? 0x7F : 0x00));// Loop In (on or off)
			loopingInBlinkLed[i]++;
			if(loopingInBlinkLed[i]>=4) loopingInBlinkLed[i] = 0;
		}
	
	
	
	}
}
//----------------------------------------------------------------------------- 
