//-----------------------------------------------------------------------------
//-------- Copyright (c)2003-2013 XYLIO INFO SRL. All Rights Reserved. --------
//-----------------------------------------------------------------------------
// file implementing HID controller 'PCDJ DAC-3' (version 2)
//----------------------------------------------------------------------------- 
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 "PCDJ DAC-3";
}
//----------------------------------------------------------------------------- 
// 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 'hid';
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// 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 0x0925;
}
//----------------------------------------------------------------------------- 
function getSupportedHidDevicePID(osId)
{
	return 0x1234;
}
//----------------------------------------------------------------------------- 
// 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)
{
	//if(osId==1) return "PCDJ DAC-3 USBHIDIO";
	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 "00014E0200";
	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)
{
	//if(osId==1) return "DJ Console Mk4";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==1) return "DJ Console Mk4";
	return false;
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// some variables necessary only for this particular script
var timeStatus=[0,0,0,0];// 0 means elapsed, 1 remained
var jogDrumMode=[0,0,0,0];// 0 means disabled
var samplerStatus=[0,0,0,0];// 0 means disabled
var loopingInBlinkLed=[-1,-1,-1,-1];// -1 means disabled
var shiftStatus=[0,0,0,0];// 0 means disabled
//----------------------------------------------------------------------------- 
// helper functions (this controller accepts 4 bytes to it's HID end point (output))
function _sendHid(uniqueId, d1,d2,d3,d4)
{
	var hidData = new Array(4);
	hidData[0] = d1;
	hidData[1] = d2;
	hidData[2] = d3;
	hidData[3] = d4;
	sendHid(uniqueId, hidData, 4);
}
//----------------------------------------------------------------------------- 
// value is a float (seconds)
function showTimeValue(uniqueId, deck, value)
{
	// split time in min/sec/100ths
	var sec = Math.floor(value);
	var hsec = Math.floor((value - sec)*100);
	var min = Math.floor(sec/60);
	var sec = sec % 60;
	//debug('showTimeValue ['+uniqueId+'] = '+min+':'+sec+'.'+hsec);
	// each hexa digit (in the number) represents a digit in the display
	// eg. 0x02 means X2; 0x43 means 43; 0x0A means X-; 0xAA means --; 0xBB means __;
	// convert to hexa
	min  = 16*Math.floor(min/10)  + (min%10);
	sec  = 16*Math.floor(sec/10)  + (sec%10);
	hsec = 16*Math.floor(hsec/10) + (hsec%10);
	// display
	if(deck==0)
	{
		_sendHid(uniqueId, 0x00, 0x80, 0x07, min);// LCD time min A
		_sendHid(uniqueId, 0x00, 0x80, 0x08, sec);// LCD time sec A
		_sendHid(uniqueId, 0x00, 0x80, 0x09, hsec);//LCD time  ms A
	}else
	{
		_sendHid(uniqueId, 0x00, 0x80, 0x01, min);// LCD time min B
		_sendHid(uniqueId, 0x00, 0x80, 0x02, sec);// LCD time sec B
		_sendHid(uniqueId, 0x00, 0x80, 0x03, hsec);//LCD time  ms B
	}
}
//----------------------------------------------------------------------------- 
// while this function is working perfectly, it's not used (this display is used for BPM instead)
// an integer from -9999 to +9999 (representing -99.99% .. +99.99% pitch)
function showPitchValue(uniqueId, deck, value)
{
	// split pitch value
	var p1 = Math.floor(Math.abs(value)/100);
	var p2 = Math.floor(Math.abs(value)%100);
	// set values depending on deck number
	var signOnVal = 0x1C;
	if(deck==1) signOnVal = 0x1B;
	var signOffVal = signOnVal - 0x10;
	// display sign value
	if(value==0) 
	{
		// set to off the sign
		_sendHid(uniqueId, 0x00, 0x90, signOffVal, 0x07);// LCD pitch sign horiz
		_sendHid(uniqueId, 0x00, 0x90, signOffVal, 0x08);// LCD pitch sign vert
	}else 
	{
		if(value>0)
		{
			// set to plus sign
			_sendHid(uniqueId, 0x00, 0x90, signOnVal, 0x07);// LCD pitch sign horiz
			_sendHid(uniqueId, 0x00, 0x90, signOnVal, 0x08);// LCD pitch sign vert
		}else
		{
			// set to plus sign
			_sendHid(uniqueId, 0x00, 0x90, signOnVal,  0x07);// LCD pitch sign horiz
			_sendHid(uniqueId, 0x00, 0x90, signOffVal, 0x08);// LCD pitch sign vert
		}
	}
	// we have 3.1 digits (p1.p2) so we have to re-distribute the p1 and p2 values
	var dd1 = Math.floor(p1/10);
	var dd2 = 16*(p1%10) + Math.floor(p2/10);
	// display the value
	if(deck==0)
	{
		_sendHid(uniqueId, 0x00, 0x80, 0x0A, dd1);// LCD pitch first 2 A
		_sendHid(uniqueId, 0x00, 0x80, 0x0B, dd2);// LCD pitch next 2  A	
	}else
	{
		_sendHid(uniqueId, 0x00, 0x80, 0x04, dd1);// LCD pitch first 2 B
		_sendHid(uniqueId, 0x00, 0x80, 0x05, dd2);// LCD pitch next 2  B
	}
}
//----------------------------------------------------------------------------- 
// value is a float (seconds)
function showBpmValue(uniqueId, deck, value)
{
	if(value<0)
	{
		if(deck==0)
		{
			_sendHid(uniqueId, 0x00, 0x80, 0x0A, 0xBB);// LCD pitch first 2 A
			_sendHid(uniqueId, 0x00, 0x80, 0x0B, 0xBB);// LCD pitch next 2  A	
		}else
		{
			_sendHid(uniqueId, 0x00, 0x80, 0x04, 0xBB);// LCD pitch first 2 B
			_sendHid(uniqueId, 0x00, 0x80, 0x05, 0xBB);// LCD pitch next 2  B
		}
	}
	// split pitch value
	var p1 = Math.floor(value);
	var p2 = Math.floor((value - p1)*10);// keep 1 digit
	// we have 3.1 digits (p1.p2) so we have to re-distribute the p1 and p2 values
	// get the last digit from p1 and the (only) one from p2
	var dd2 = 16*(p1%10) + p2;
	// discard the last digit from p1 now
	p1 = Math.floor(p1/10);
	var dd1 = 16*Math.floor(p1/10) + (p1%10)
	// display the value
	if(deck==0)
	{
		_sendHid(uniqueId, 0x00, 0x80, 0x0A, dd1);// LCD pitch first 2 A
		_sendHid(uniqueId, 0x00, 0x80, 0x0B, dd2);// LCD pitch next 2  A	
	}else
	{
		_sendHid(uniqueId, 0x00, 0x80, 0x04, dd1);// LCD pitch first 2 B
		_sendHid(uniqueId, 0x00, 0x80, 0x05, dd2);// LCD pitch next 2  B
	}
}
//----------------------------------------------------------------------------- 
// an integer 0..100 representing percents
function showTrackPosition(uniqueId, deck, value)
{
	//debug('showTrackPosition ['+uniqueId+'] deck='+deck+' value='+value);
	for(var i=0; i<10; i++)	
	{
		var state = 0;
		if(value <= i*10) state = 0x10;
		if(deck==0)
			_sendHid(uniqueId, 0x00, 0x60, 0x1B - state, 0x00+i);// LCD position A
		else
			_sendHid(uniqueId, 0x00, 0x60, 0x1A - state, 0x00+i);// LCD position B
	}
}
//----------------------------------------------------------------------------- 
function showTrackNumber(uniqueId, deck, value)
{
	if(value<0) 
	{
		value = 0xBB;
	}else
	{
		value  = 16*Math.floor(value/10)  + (value%10);
	}
	if(deck==0)
		_sendHid(uniqueId, 0x00, 0x80, 0x06, value);// LCD track A
	else
		_sendHid(uniqueId, 0x00, 0x80, 0x00, value);// LCD track B
}
//----------------------------------------------------------------------------- 
function showPlayStatus(uniqueId, deck, value)
{
	var state=0;
	if(value>0) state = 0x10;
	if(deck==0)
	{
		_sendHid(uniqueId, 0x00, 0x70, 0x0A+state, 0x0B);// play A
		_sendHid(uniqueId, 0x00, 0x90, 0x0C+state, 0x00);// LCD play A
		_sendHid(uniqueId, 0x00, 0x90, 0x1C-state, 0x01);// LCD pause A
	}else
	{
		_sendHid(uniqueId, 0x00, 0x70, 0x0B+state, 0x0B);// play B
		_sendHid(uniqueId, 0x00, 0x90, 0x0B+state, 0x00);// LCD play B
		_sendHid(uniqueId, 0x00, 0x90, 0x1B-state, 0x01);// LCD pause B
	}
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// 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 Hid DAC-3 ['+uniqueId+']');// this outputs a string to DebugView app on Win
	//------------------------------------
	// defineHidPage(uniqueId, pageName, isInputPage);
	// defineHidPageDetect(uniqueId, pageName, typeName, offset, size, value);
	// defineHidPageAction(uniqueId, pageName, actionName, deck, typeName, offset[,size,min,max,specialTypeName]);
	// define pages, how to detect them and the actions
	//------------------------------------
	for(var deck=0; deck<2; deck++)
	{
		var pageNameBase = 'deck'+String.fromCharCode(65+deck);// this makes names like 'deckA', 'deckB', 'deckC', etc
		//------------------------------------
		// define a new HID page (and specify how to recognize/detect it)
		defineHidPage(uniqueId, pageNameBase, true);
		switch(deck)
		{
		case 0: // deck A
			defineHidPageDetect(uniqueId, pageNameBase, 'ubyte', 0, 1, 0x00);// pageName, typeName, offset, size, value
			break;
		case 1: // deck B
			defineHidPageDetect(uniqueId, pageNameBase, 'ubyte', 0, 1, 0xFF);
			break;
		}
		// actions
		defineHidPageAction(uniqueId, pageNameBase, 'bendup', deck, 'bit', 8);
		defineHidPageAction(uniqueId, pageNameBase, 'benddown', deck, 'bit', 9);
		defineHidPageAction(uniqueId, pageNameBase, 'sync', deck, 'bit', 10);// this is actually 'match'
		defineHidPageAction(uniqueId, pageNameBase, 'keylock', deck, 'bit', 11);
		defineHidPageAction(uniqueId, pageNameBase, 'reverse', deck, 'bit', 12);		
		defineHidPageAction(uniqueId, pageNameBase, 'waitlist', deck, 'bit', 13);
		defineHidPageAction(uniqueId, pageNameBase, 'toggletime', deck, 'bit', 14);
		defineHidPageAction(uniqueId, pageNameBase, 'loadtrack', deck, 'bit', 15);
		// cue points
		defineHidPageAction(uniqueId, pageNameBase, 'cue1', deck, 'bit', 23);
		defineHidPageAction(uniqueId, pageNameBase, 'cue2', deck, 'bit', 22);
		defineHidPageAction(uniqueId, pageNameBase, 'cue3', deck, 'bit', 21);
		defineHidPageAction(uniqueId, pageNameBase, 'cue4', deck, 'bit', 20);
		defineHidPageAction(uniqueId, pageNameBase, 'cue5', deck, 'bit', 19);
		defineHidPageAction(uniqueId, pageNameBase, 'cue6', deck, 'bit', 18);
		defineHidPageAction(uniqueId, pageNameBase, 'cue7', deck, 'bit', 17);
		defineHidPageAction(uniqueId, pageNameBase, 'cue8', deck, 'bit', 16);
		defineHidPageAction(uniqueId, pageNameBase, 'cue9', deck, 'bit', 31);
		defineHidPageAction(uniqueId, pageNameBase, 'cue10',deck, 'bit', 30);
		// others
		defineHidPageAction(uniqueId, pageNameBase, 'automix', deck, 'bit', 24);// this is 'auto'
		defineHidPageAction(uniqueId, pageNameBase, 'apply', deck, 'bit', 25);
		defineHidPageAction(uniqueId, pageNameBase, 'fx', deck, 'bit', 26);
		defineHidPageAction(uniqueId, pageNameBase, 'reloop', deck, 'bit', 27);// reloop		
		defineHidPageAction(uniqueId, pageNameBase, 'loopexit', deck, 'bit', 28);// out
		defineHidPageAction(uniqueId, pageNameBase, 'loopinout', deck, 'bit', 29);// in
		//
		defineHidPageAction(uniqueId, pageNameBase, 'playpause', deck, 'bit', 32);
		defineHidPageAction(uniqueId, pageNameBase, 'cue', deck, 'bit', 33);
		defineHidPageAction(uniqueId, pageNameBase, 'cueplay', deck, 'bit', 34);
		defineHidPageAction(uniqueId, pageNameBase, 'browsertoggle', deck, 'bit', 35);// folder
		defineHidPageAction(uniqueId, pageNameBase, 'browseup', deck, 'bit', 36);// up arrow
		defineHidPageAction(uniqueId, pageNameBase, 'browsedown', deck, 'bit', 37);// down arrow
		defineHidPageAction(uniqueId, pageNameBase, 'fwd', deck, 'bit', 38);
		defineHidPageAction(uniqueId, pageNameBase, 'rev', deck, 'bit', 39);

		defineHidPageAction(uniqueId, pageNameBase, 'SHIFT', deck, 'bit', 47);// ATTN -- maybe do a special SHIFT page instead?
		
		// sliders,knobs,encoders
		defineHidPageAction(uniqueId, pageNameBase, 'pitch', deck, 'ubyte', 8);
		defineHidPageAction(uniqueId, pageNameBase, 'jogwheel', deck, 'ubyte', 6, 1, 0, 0x4A, 'jogwheel');// 75 distinct positions/rotation
		
		// global (even if it's in the first HID page)
		defineHidPageAction(uniqueId, pageNameBase, 'mixnow', -1, 'bit', 46);
		//------------------------------------
	}

	// leds on
	_sendHid(uniqueId, 0x00, 0x70, getValue('reverse', 0)?0x1A:0x0A, 0x00);// reverse A
	_sendHid(uniqueId, 0x00, 0x70, getValue('reverse', 1)?0x1B:0x0B, 0x00);// reverse B
	_sendHid(uniqueId, 0x00, 0x70, getValue('keyLock', 0)?0x1A:0x0A, 0x01);// keylock (mt) A
	_sendHid(uniqueId, 0x00, 0x70, getValue('keyLock', 1)?0x1B:0x0B, 0x01);// keylock (mt) B
	_sendHid(uniqueId, 0x00, 0x70, 0x0A, 0x02);// match A (0x1A on)
	_sendHid(uniqueId, 0x00, 0x70, 0x0B, 0x02);// match B (0x1B on)
	_sendHid(uniqueId, 0x00, 0x70, 0x0A, 0x03);// mix now (0x1A on)
	_sendHid(uniqueId, 0x00, 0x70, getValue('looping', 0)?0x1A:0x0A, 0x04);// reloop A
	_sendHid(uniqueId, 0x00, 0x70, getValue('looping', 1)?0x1B:0x0B, 0x04);// reloop B
	_sendHid(uniqueId, 0x00, 0x70, getValue('looping', 0)?0x1A:0x0A, 0x05);// loop out A
	_sendHid(uniqueId, 0x00, 0x70, getValue('looping', 1)?0x1B:0x0B, 0x05);// loop out B
	_sendHid(uniqueId, 0x00, 0x70, 0x0A, 0x06);// loop in A (0x1A on)
	_sendHid(uniqueId, 0x00, 0x70, 0x0B, 0x06);// loop in B (0x1B on)
	_sendHid(uniqueId, 0x00, 0x70, getValue('fxFlanger', 0)?0x1A:0x0A, 0x07);// fx A
	_sendHid(uniqueId, 0x00, 0x70, getValue('fxFlanger', 1)?0x1B:0x0B, 0x07);// fx B
	_sendHid(uniqueId, 0x00, 0x70, getValue('autoDj', 0)?0x1A:0x0A, 0x08);// autopilot A
	_sendHid(uniqueId, 0x00, 0x70, getValue('autoDj', 1)?0x1B:0x0B, 0x08);// autopilot B
	_sendHid(uniqueId, 0x00, 0x70, getValue('random', 0)?0x1A:0x0A, 0x09);// shuffle A
	_sendHid(uniqueId, 0x00, 0x70, getValue('random', 1)?0x1B:0x0B, 0x09);// shuffle B
	_sendHid(uniqueId, 0x00, 0x70, getValue('cue', 0)?0x1A:0x0A, 0x0A);// cue A
	_sendHid(uniqueId, 0x00, 0x70, getValue('cue', 1)?0x1B:0x0B, 0x0A);// cue B
	
	// LCD
	_sendHid(uniqueId, 0x00, 0x90, 0x0C, 0x02);// LCD continue A (not used) (0x1C on)
	_sendHid(uniqueId, 0x00, 0x90, 0x0B, 0x02);// LCD continue B (not used) (0x1B on)
	_sendHid(uniqueId, 0x00, 0x90, 0x1C, 0x03);// LCD autocue A (aprinde si 'single') -- always on
	_sendHid(uniqueId, 0x00, 0x90, 0x1B, 0x03);// LCD autocue B (aprinde si 'single') -- always on
	_sendHid(uniqueId, 0x00, 0x90, getValue('brake', 0)?0x1C:0x0C, 0x04);// LCD brake A
	_sendHid(uniqueId, 0x00, 0x90, getValue('brake', 1)?0x1B:0x0B, 0x04);// LCD brake B
	_sendHid(uniqueId, 0x00, 0x90, 0x0C, 0x05);// LCD beat A (0x1C on)
	_sendHid(uniqueId, 0x00, 0x90, 0x0B, 0x05);// LCD beat B (0x1B on)
	_sendHid(uniqueId, 0x00, 0x90, 0x0C, 0x06);// LCD pitch A (not used) (0x1C on)
	_sendHid(uniqueId, 0x00, 0x90, 0x0B, 0x06);// LCD pitch B (not used) (0x1B on)
	_sendHid(uniqueId, 0x00, 0x90, 0x0C, 0x07);// LCD pitch sign horiz (not used) (0x1C on)
	_sendHid(uniqueId, 0x00, 0x90, 0x0B, 0x08);// LCD pitch sign vert (not used) (0x1B on)
	_sendHid(uniqueId, 0x00, 0x90, 0x0C, 0x0A);// LCD remain A (0x1C on)
	_sendHid(uniqueId, 0x00, 0x90, 0x0B, 0x0A);// LCD remain B (0x1B on)
	_sendHid(uniqueId, 0x00, 0x90, getValue('looping', 0)?0x1C:0x0C, 0x0B);// LCD loop A
	_sendHid(uniqueId, 0x00, 0x90, getValue('looping', 0)?0x1B:0x0B, 0x0B);// LCD loop B
	_sendHid(uniqueId, 0x00, 0x90, 0x0C, 0x0C);// LCD outro A (not used) (0x1C on)
	_sendHid(uniqueId, 0x00, 0x90, 0x0B, 0x0C);// LCD outro B (not used) (0x1B on)
	showPlayStatus(uniqueId, 0, getValue('play', 0));
	showPlayStatus(uniqueId, 1, getValue('play', 1));
	// set to on the pitch/bpm . separator
	_sendHid(uniqueId, 0x00, 0x90, 0x1C, 0x09);// LCD pitch dot A
	_sendHid(uniqueId, 0x00, 0x90, 0x1B, 0x09);// LCD pitch dot B
	// set to on the time : separators
	_sendHid(uniqueId, 0x00, 0x90, 0x1C, 0x0D);// LCD time : 1 A
	_sendHid(uniqueId, 0x00, 0x90, 0x1B, 0x0D);// LCD time : 1 B
	_sendHid(uniqueId, 0x00, 0x90, 0x1C, 0x0E);// LCD time : 2 A
	_sendHid(uniqueId, 0x00, 0x90, 0x1B, 0x0E);// LCD time : 2 B
	// time
	showTimeValue(uniqueId, 0, 0);
	showTimeValue(uniqueId, 1, 0);
	// position
	showTrackPosition(uniqueId, 0, 0);// 0..100
	showTrackPosition(uniqueId, 1, 0);
	// pitch/bpm
	showBpmValue(uniqueId, 0, 0.0);
	showBpmValue(uniqueId, 1, 0.0);
	// track
	showTrackNumber(uniqueId, 0, -1);// disable it
	showTrackNumber(uniqueId, 1, -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 Hid ['+uniqueId+']');
	// leds off
	for(var i=0; i<=0x0B; i++)
	{
		_sendHid(uniqueId, 0x00, 0x70, 0x0A, i);// reverse A
		_sendHid(uniqueId, 0x00, 0x70, 0x0B, i);// reverse B
	}
	// LCD off
	for(var i=0; i<=0x0E; i++)
	{
		_sendHid(uniqueId, 0x00, 0x90, 0x0C, i);// LCD play A
		_sendHid(uniqueId, 0x00, 0x90, 0x0B, i);// LCD play B
	}
	// LCD position
	for(var i=0; i<10; i++)	
	{
		_sendHid(uniqueId, 0x00, 0x60, 0x0B, 0x00+i);// LCD position A
		_sendHid(uniqueId, 0x00, 0x60, 0x0A, 0x00+i);// LCD position B
	}
	// LCD time+pitch+track
	for(var i=0; i<=0x0B; i++)	
	{
		_sendHid(uniqueId, 0x00, 0x80, i, 0xBB);// (0x0A means X-; 0xAA means --; 0xBB means __;)
	}
}
//----------------------------------------------------------------------------- 
// 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)
{
	//debug('messageReceived Midi ['+uniqueId+']');
	// this is not used with HID controllers
	return false;
}
//----------------------------------------------------------------------------- 
// received for both HID/MIDI as/if needed/used
// ATTN: this implementation is 'almost' generic but still includes several PCDJ DAC-3 specific things
function actionReceived(uniqueId,name,deck,value)
{
	// for some strange reason this controller sends 0 when a button is pressed (instead of 1)
	if((name!='pitch')&&(name!='jogwheel')) value = 1.0 - value;
	//debug('actionReceived ['+uniqueId+'] name='+name+' deck='+deck+' value='+value);
	// value is [0..1] or +/-n for encoders/jogwheels
	// ATTN: we multiply by 0x7F almost all the values to simulate MIDI (which our DJ app conforms to)
	switch(name)
	{
	// decks
	case 'looplessmore':
		// since we don't receive press+release notifications for encoders, we need to simulate a fast press+release
		if(value<0) 
		{
			call('deckLoopLess', deck, 0x7F);
			call('deckLoopLess', deck, 0x00);
		}else 
		{
			call('deckLoopMore', deck, 0x7F);
			call('deckLoopMore', deck, 0x00);
		}
		break;
	case 'beat_encoder': // encoder (marked 'beat' on the silkscreen)
	case 'loopprevnext':
		// since we don't receive press+release notifications for encoders, we need to simulate a fast press+release
		if(value<0) 
		{
			call('deckLoopPrev', deck, 0x7F);
			call('deckLoopPrev', deck, 0x00);
		}else 
		{
			call('deckLoopNext', deck, 0x7F);
			call('deckLoopNext', deck, 0x00);
		}
		break;
	case 'pitch': call('setDeckPitch', deck, (1.0-value)*0x7F); break;// reversed slider
	case 'leveldepth_knob': call('setDeckPitchKey', deck, value*0x7F); break;// knob (marked 'level depth' on the silkscreen)
	// buttons
	case 'playpause': call('deckPlayPause', deck, value*0x7F); break;
	case 'cue': call('deckCue', deck, value*0x7F); break;
	case 'cueplay': call('deckCuePlay', deck, value*0x7F); break;
	case 'reverse': call('deckReverse', deck, value*0x7F); break;
	case 'backtotop': call('deckBackToTop', deck, value*0x7F); break;
	case 'sync': call('deckSync', deck, value*0x7F); break;
	case 'match': call('deckMatch', deck, value*0x7F); break;
	case 'keylock': call('deckKeyLock', deck, value*0x7F); break;
	
	case 'loopinout': call('deckLoopInEnd', deck, value*0x7F); break;
	case 'loopexit': call('deckLoopExit', deck, value*0x7F); break;
	//case 'loopinout': if(getValue('looping', deck)) call('deckLoopExit', deck, value*0x7F);else call('deckLoopInEnd', deck, value*0x7F); break;
	//case 'reloop': if(getValue('looping', deck)) call('deckLoopExit', deck, value*0x7F);else call('deckLoopReloop', deck, value*0x7F); break;
	// the above is not correct since the 'looping' status changes between a press/release cycle so we send the wrong signal so instead we simulate a fast press/release
	case 'reloop': 
		if(value>0)
		{ 
			if(getValue('looping', deck)) 
			{
				call('deckLoopExit', deck, 0x7F);
				call('deckLoopExit', deck, 0x00);
			}else 
			{
				call('deckLoopReloop', deck, 0x7F);
				call('deckLoopReloop', deck, 0x00);
			}
		}
		break;
	case 'loadtrack': if(value>0) call('deckLBLoad', deck, 1); break;
	case 'pitchrange': 
		// 1 = 4%; 2 = 8%; 3 = 16%; 4 = 24%; 5 = 32%; 6 = 50%;
		if(value>0)
		{ 
			var currentPitchRange = getValue('pitchRange');
			currentPitchRange = currentPitchRange + 1; if(currentPitchRange>6) currentPitchRange = 1;
			call('setPitchRange', currentPitchRange);
		}
		break;
	case 'toggletime':
		if(value>0)
		{ 
			timeStatus[deck] = (timeStatus[deck] + 1) % 2; 
			callHidPageAction(uniqueId, 'lcd_remain', deck, timeStatus[deck]?1:0);
			_sendHid(uniqueId, 0x00, 0x90, timeStatus[deck]?(deck?0x1B:0x1C):(deck?0x0B:0x0C), 0x0A);// LCD remain A (0x1C on)
		}
		break;
	case 'enable-jogdrum': 
		if(value>0)
		{ 
			jogDrumMode[deck] = (jogDrumMode[deck] + 1) % 2; 
			callHidPageAction(uniqueId, 'jogdrum', deck, jogDrumMode[deck]?1:0);
			if(jogDrumMode[deck])
			{
				callHidPageAction(uniqueId, 'jw_blue', deck, 0);
				callHidPageAction(uniqueId, 'jw_red', deck, 1);
			}else
			{
				callHidPageAction(uniqueId, 'jw_blue', deck, 1);
				callHidPageAction(uniqueId, 'jw_red', deck, 0);
			}
		}
		break;
	case 'enable-sampler': 
		if(value>0)
		{ 
			samplerStatus[deck] = (samplerStatus[deck] + 1) % 2; 
			callHidPageAction(uniqueId, 'samplelaunch', deck, samplerStatus[deck]?1:0);
		}
		break;	
	//-------------------------------------
	// when in Sampler mode the effects buttons are used to play samples instead
	case 'beatwaw':
		if(samplerStatus[deck])
		{
			if(value>0) call('samplerPlay', (deck%2==0)?1:5);
		}else
		{
			call('deckFxBeatwaw', deck, value*0x7F); 
		}
		break;
	case 'flanger':
		if(samplerStatus[deck])
		{
			if(value>0) call('samplerPlay', (deck%2==0)?2:6);
		}else
		{
			call('deckFxFlanger', deck, value*0x7F); 
		}
		break;
	case 'echo':
		if(samplerStatus[deck])
		{
			if(value>0) call('samplerPlay', (deck%2==0)?3:7);
		}else
		{
			call('deckFxEcho', deck, value*0x7F); 
		}
		break;
	case 'pan':
		if(samplerStatus[deck])
		{
			if(value>0) call('samplerPlay', (deck%2==0)?4:8);
		}else
		{
			call('deckFxPan', deck, value*0x7F); 
		}
		break;
	//-------------------------------------
	case 'cue1': call('deckCuePos1', deck, value*0x7F); break;
	case 'cue2': call('deckCuePos2', deck, value*0x7F); break;
	case 'cue3': call('deckCuePos3', deck, value*0x7F); break;
	case 'cue4': call('deckCuePos4', deck, value*0x7F); break;
	case 'cue5': call('deckCuePos5', deck, value*0x7F); break;
	case 'cue6': call('deckCuePos6', deck, value*0x7F); break;
	case 'cue7': call('deckCuePos7', deck, value*0x7F); break;
	case 'cue8': call('deckCuePos8', deck, value*0x7F); break;
	case 'cue9': call('deckCuePos9', deck, value*0x7F); break;
	case 'cue10': call('deckCuePos10', deck, value*0x7F); break;
	
	case 'rev': call('deckSearchLeft', deck, value*0x7F); break;
	case 'fwd': call('deckSearchRight', deck, value*0x7F); break;
	case 'benddown': call('deckBendDown', deck, value*0x7F); break;
	case 'bendup': call('deckBendUp', deck, value*0x7F); break;

	case 'waitlist': break;// not used
	case 'apply': call('deckBrake', deck, value*0x7F); break;
	case 'fx': call('deckFxFlanger', deck, value*0x7F); break;
	case 'SHIFT': shiftStatus[deck] = value; break;
	
	// mixer (decks)
	case 'gain': call('setDeckGain', deck, value*0x7F); break;
	case 'eq-high': call('setDeckEqHigh', deck, value*0x7F); break;
	case 'eq-mid': call('setDeckEqMid', deck, value*0x7F); break;
	case 'eq-low': call('setDeckEqLow', deck, value*0x7F); break;
	case 'filter': call('setDeckCutoff', deck, value*0x7F); break;
	case 'volume': call('setDeckVolume', deck, value*0x7F); break;
	// buttons
	case 'monitor': call('deckMonitor', deck, value*0x7F); break;
	
	// mixer (global)
	case 'crossfader': call('setCrossfader', value*0x7F); break;
	case 'cuepgmmix': call('setCuePgmMix', value*0x7F); break;
	case 'monitorgain': call('setMonitorGain', value*0x7F); break;
	case 'browser': call('lbMoveCursor', value); break;
	case 'cfcurve': // 0x80 thru = ~0.50; 0x40 cross = ~0.25; 0x20 flat = ~0.125; -- only these 3 possible settings (and in this order)
		if((value>0.49)&&(value<0.51)) call('setCFCurve', 0x7F);// thru (hard curve set)
		if((value>0.24)&&(value<0.26)) call('setCFCurve', 0x3F);// cross (normal curve set)
		if((value>0.11)&&(value<0.13)) call('setCFCurve', 0x5F);// flat (medium hard curve set)
		break;
	// buttons
	case 'browsertoggle': if(value>0) call('lbToggleSelect', 0); break;
	case 'browseup': if(value>0) call('lbMoveCursor', -1); break;
	case 'browsedown': if(value>0) call('lbMoveCursor', +1); break;
	case 'automix': call('setAutoDj', value?0x7F:0x00); break;
	case 'mixnow': call('mixNext', value*0x7F); _sendHid(uniqueId, 0x00, 0x70, value?0x1A:0x0A, 0x03); break;
	
	// jog-wheels
	case 'jogwheel': if(shiftStatus[deck]) call('searchDeckJW', deck, value, 75);else call('moveDeckJogWheel', deck, value, 75); break;
	//case 'jogwheel': call('moveDeckJogWheel', deck, value, 75); break;
	case 'jogwheel-search': call('searchDeckJW', deck, value, 75); break;
	// buttons
	case 'jogwheel-touch': 
		if(jogDrumMode[deck])
			call('deckCue', deck, value*0x7F); 
		else
			call('holdDeckJogWheel', deck, value*0x7F); 
		break;
	case 'jogwheel-search-touch': break;// nothing to do	
	}
}
//----------------------------------------------------------------------------- 
// 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 Hid ['+uniqueId+'] name='+name + ' deck='+deck + ' p1(int)='+param1 + ' p2(float)='+param2 + ' p3(str)='+param3);
	if(name=='play') 	showPlayStatus(uniqueId, deck, param1);
	if(name=='cue') 	_sendHid(uniqueId, 0x00, 0x70, param1?(deck?0x1B:0x1A):(deck?0x0B:0x0A), 0x0A);
	if(name=='onbeat')	
	{
		_sendHid(uniqueId, 0x00, 0x70, param1?(deck?0x1B:0x1A):(deck?0x0B:0x0A), 0x02);// match LED
		// the LCD beat pulsing doesn't look to good so we don't use this
		//_sendHid(uniqueId, 0x00, 0x90, param1?(deck?0x1B:0x1C):(deck?0x0B:0x0C), 0x05);// LCD beat (0x1C A on / 0x1B B on)
	}
	if(name=='reverse') 	_sendHid(uniqueId, 0x00, 0x70, param1?(deck?0x1B:0x1A):(deck?0x0B:0x0A), 0x00);// R led
	if(name=='keyLock') 	_sendHid(uniqueId, 0x00, 0x70, param1?(deck?0x1B:0x1A):(deck?0x0B:0x0A), 0x01);// MT led
	if(name=='brake') 		_sendHid(uniqueId, 0x00, 0x90, param1?(deck?0x1B:0x1C):(deck?0x0B:0x0C), 0x04);// LCD brake (0x1C A on / 0x1B B on)
	if(name=='fxFlanger') 	_sendHid(uniqueId, 0x00, 0x70, param1?(deck?0x1B:0x1A):(deck?0x0B:0x0A), 0x07);// fx led
	if(name=='autoDj')
	{
		_sendHid(uniqueId, 0x00, 0x70, param1?0x1A:0x0A, 0x08);// autopilot A
		_sendHid(uniqueId, 0x00, 0x70, param1?0x1B:0x0B, 0x08);// autopilot B
	}
	if(name=='random')
	{
		_sendHid(uniqueId, 0x00, 0x70, param1?0x1A:0x0A, 0x09);// shuffle A
		_sendHid(uniqueId, 0x00, 0x70, param1?0x1B:0x0B, 0x09);// shuffle B
	}
	if(name=='loopIn')
	{
		_sendHid(uniqueId, 0x00, 0x70, param1?(deck?0x1B:0x1A):(deck?0x0B:0x0A), 0x06);// loop in led (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')
	{
		_sendHid(uniqueId, 0x00, 0x90, param1?(deck?0x1B:0x1C):(deck?0x0B:0x0C), 0x0B);// LCD loop (0x1C A on / 0x1B B on)
		_sendHid(uniqueId, 0x00, 0x70, (deck?0x0B:0x0A), 0x06);// loop in led (off)
		_sendHid(uniqueId, 0x00, 0x70, param1?(deck?0x1B:0x1A):(deck?0x0B:0x0A), 0x05);// loop out led (on or off)
		_sendHid(uniqueId, 0x00, 0x70, param1?(deck?0x1B:0x1A):(deck?0x0B:0x0A), 0x04);// reloop led (on or off)
	}
}
//----------------------------------------------------------------------------- 
var lastBpm=[0,0];
// 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 Hid ['+uniqueId+']');
	for(var i=0; i<2; i++)
	{
		// getValue() -- 2 params -- name[,deck]
		var timeElapsed = getValue('timeElapsed', i);// float seconds
		var timeRemain = getValue('timeRemain', i);// float seconds
		var bpm = getValue('currentBpm', i);// float bpm
		//debug('deck '+i+'  timeElapsed='+timeElapsed+'s  timeRemain='+timeRemain+'s  BPM='+bpm+'  pitch='+pitch);
		if(lastBpm[i] != bpm) showBpmValue(uniqueId, i, bpm);
		if(timeStatus[i]==0) showTimeValue(uniqueId, i, timeElapsed);else showTimeValue(uniqueId, i, timeRemain);
		var pos = 0;
		if(timeElapsed + timeRemain > 0) pos = Math.floor(100*timeElapsed/(timeElapsed + timeRemain));
		showTrackPosition(uniqueId, i, pos);
		lastBpm[i] = bpm;
		// loop in led
		if(loopingInBlinkLed[i]!=-1)
		{
			// actually set the led
			_sendHid(uniqueId, 0x00, 0x70, (loopingInBlinkLed[i]<5)?(i?0x1B:0x1A):(i?0x0B:0x0A), 0x06);// loop in led (on or off)
			loopingInBlinkLed[i]++;
			if(loopingInBlinkLed[i]>=10) loopingInBlinkLed[i] = 0;
		}
	}
}
//----------------------------------------------------------------------------- 
