//-----------------------------------------------------------------------------
//----------------- (c)2003-2016 Xylio. All Rights Reserved. ------------------
//-----------------------------------------------------------------------------
// file implementing HID controller 'Pioneer XDJ-AERO'
//----------------------------------------------------------------------------- 
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 "Pioneer XDJ-AERO";
}
//----------------------------------------------------------------------------- 
// 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 0x08E4;
}
//----------------------------------------------------------------------------- 
function getSupportedHidDevicePID(osId)
{
	return 0x0172;
}
//----------------------------------------------------------------------------- 
// 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 "PIONEER XDJ-AERO HID";
	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 "PIONEER XDJ-AERO MIDI";
	return false;
}
//----------------------------------------------------------------------------- 
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==1) return "PIONEER XDJ-AERO MIDI";
	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
//----------------------------------------------------------------------------- 
// value is a float (seconds)
function showTimeValue(uniqueId, deck, value, totalTime)
{
	// split time in min/sec/100ths
	// current time
	var sec = Math.floor(value);
	var hsec = Math.floor((value - sec)*100);
	var min = Math.floor(sec/60);
	sec = sec % 60;
	callHidPageAction(uniqueId, 'time_min', deck, min);
	callHidPageAction(uniqueId, 'time_sec', deck, sec);
	callHidPageAction(uniqueId, 'time_msec', deck, hsec);
	//-------------
	// total time
	sec = Math.floor(totalTime);
	hsec = Math.floor((totalTime - sec)*100);
	min = Math.floor(sec/60);
	sec = sec % 60;
	callHidPageAction(uniqueId, 'totaltime_min', deck, min);
	callHidPageAction(uniqueId, 'totaltime_sec', deck, sec);
	callHidPageAction(uniqueId, 'totaltime_msec', deck, hsec);
}
//----------------------------------------------------------------------------- 
function showMainText(uniqueId, deck, clearDisplay)
{
	//----------------------------------------------------
	/*
	//var artist = getValue('artist', deck);
	//var title = getValue('title', deck);
	//var artistData = getValue('artist-title', deck);
	// simple mode
	var hidData = new Array(33);
	for(var i=0; i<33; i++)	hidData[i] = 0;
	hidData[0+1] = 0x03;//0-7
	hidData[1+1] = 0;//8-15
	// this writes AABBC on the display (simple ASCII mode -- or maybe this is UTF8 mode ?!)
	hidData[4+1] = 65;// A
	hidData[5+1] = 65;// A
	hidData[6+1] = 66;// B
	hidData[7+1] = 66;// B
	hidData[8+1] = 67;// C
	sendHid(uniqueId, hidData, 33);
	*/
	//----------------------------------------------------	
	/*
	// advanced mode - using a single packet and UTF-16
	var hidData = new Array(33);
	for(var i=0; i<33; i++)	hidData[i] = 0;
	hidData[0+1] = 0x03;//0-7
	hidData[1+1] = 0;//8-15
	// this writes ABC on the display (UTF-16 little endian)
	hidData[2+1] = (2 << 4) + 6;//16-23 -- this enables UTF-16 mode (first 4 bits specify the deck, the next 4 bits specify the UTF-16 mode)
	hidData[3+1] = (1 << 4) + 1;//17;//24-31 -- this specifies the packet number (first 4 bits, 1-based index) and the total number of packets (last 4 bits)
	// actual text
	hidData[4+1] = 65;// A
	hidData[5+1] = 0;//
	hidData[6+1] = 66;// B
	hidData[7+1] = 0;//
	hidData[8+1] = 67;// C
	hidData[7+1] = 0;//	
	sendHid(uniqueId, hidData, 33);
	*/
	//----------------------------------------------------	
	// advanced mode 2 -- using multiple packets and UTF-16
	var artistData = getValueUTF16('artist-title', deck);
	if(clearDisplay)
	{
		artistData[0] = 0;
		artistData[1] = 0;
	}
	var artistDataIndex = 0;
	// the text is split into packets (of 14 chars utf-16 type) and sent to the controller
	// for every packet (which starts at byte 4) we specify the packet number and the total packets number (in byte 3)
	var hidData = new Array(33);
	var totalPackets = 4;
	var packetSizeUtf16 = 14;
	for(var packet=0; packet<totalPackets; packet++)
	{
		for(var i=0; i<33; i++)	hidData[i] = 0;
		if(deck%2==0)
			hidData[0+1] = 0x03;// bits 0-7 -- identifies decks A,C
		else
			hidData[0+1] = 0x04;// bits 0-7 -- identifies decks B,D
		hidData[1+1] = 0;// bits 8-15
		if(deck<2)
			hidData[2+1] = (2 << 4) + 6;// bits 16-23 -- this enables UTF-16 mode (first 4 bits specify the deck, the next 4 bits specify the UTF-16 mode) -- identifies decks A,B
		else
			hidData[2+1] = (4 << 4) + 6;// -- identifies decks C,D
		// write the current packet number and the total packets number
		hidData[3+1] = ((packet+1) << 4) + totalPackets;// bits 24-31 -- this specifies the packet number (first 4 bits, 1-based index) and the total number of packets (last 4 bits)
		// actual text (we can fit max.14chars/packet) (because we start at byte 4 and we have a max of 32 bytes => 28bytes but UTF16 takes 2 bytes/character => 14 characters/packet)
		for(var i=0; i<packetSizeUtf16; i++)	
		{
			//hidData[4+1+2*i+0] = 65 + artistDataIndex; artistDataIndex++;
			//hidData[4+1+2*i+1] = 0;
			hidData[4+1+2*i+0] = artistData[artistDataIndex]; artistDataIndex++;
			hidData[4+1+2*i+1] = artistData[artistDataIndex]; artistDataIndex++;
		}
		//debug('packet '+packet+' last char='+String.fromCharCode(packet*packetSizeUtf16 + 13 + 65));
		sendHid(uniqueId, hidData, 33);
	}
	//----------------------------------------------------
}
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
// 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 XDJ-AERO ['+uniqueId+']');// this outputs a string to DebugView app on Win
	// send some HID init data
	var initHidData = new Array(33);
	for(var i=0; i<33; i++)	initHidData[i] = 0;
	initHidData[0+1] = 0x08;
	initHidData[1+1] = 0x01;
	sendHid(uniqueId, initHidData, 33);
	
	// 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<4; 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, 0x01);
			defineHidPageDetect(uniqueId, pageNameBase, 'ubyte', 2, 1, 0x80);
			break;
		case 1: // deck B
			defineHidPageDetect(uniqueId, pageNameBase, 'ubyte', 0, 1, 0x02);
			defineHidPageDetect(uniqueId, pageNameBase, 'ubyte', 2, 1, 0x80);
			break;
		case 2: // deck C
			defineHidPageDetect(uniqueId, pageNameBase, 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, pageNameBase, 'ubyte', 2, 1, 0x40);
			break;
		case 3: // deck D
			defineHidPageDetect(uniqueId, pageNameBase, 'ubyte', 0, 1, 0x02);
			defineHidPageDetect(uniqueId, pageNameBase, 'ubyte', 2, 1, 0x40);
			break;
		}
		// actions
		defineHidPageAction(uniqueId, pageNameBase, 'playpause', deck, 'bit', 0x20);
		defineHidPageAction(uniqueId, pageNameBase, 'cue', deck, 'bit', 0x21);
		defineHidPageAction(uniqueId, pageNameBase, 'pitchrange', deck, 'bit', 0x22);
		defineHidPageAction(uniqueId, pageNameBase, 'keylock', deck, 'bit', 0x23);
		defineHidPageAction(uniqueId, pageNameBase, 'toggletime', deck, 'bit', 0x24);// autocue / time toggle / quantize (all marked on the silkscreen)
		defineHidPageAction(uniqueId, pageNameBase, 'browsertoggle', deck, 'bit', 0x25);// back/top
		defineHidPageAction(uniqueId, pageNameBase, 'loadtrack', deck, 'bit', 0x26);// the button on the encoder for browse
		defineHidPageAction(uniqueId, pageNameBase, 'loopinout', deck, 'bit', 0x28);
		defineHidPageAction(uniqueId, pageNameBase, 'enable-jogdrum', deck, 'bit', 0x29);
		defineHidPageAction(uniqueId, pageNameBase, 'enable-sampler', deck, 'bit', 0x2A);		
		defineHidPageAction(uniqueId, pageNameBase, 'beatwaw', deck, 'bit', 0x2B);// trans / cue1
		defineHidPageAction(uniqueId, pageNameBase, 'flanger', deck, 'bit', 0x2C);// flanger / cue2
		defineHidPageAction(uniqueId, pageNameBase, 'echo', deck, 'bit', 0x2D);// echo / cue3
		defineHidPageAction(uniqueId, pageNameBase, 'pan', deck, 'bit', 0x2E);// roll / cue4 (delay,reverb,pan,phaser)
		defineHidPageAction(uniqueId, pageNameBase, 'sync', deck, 'bit', 0x30);
		// sliders,knobs,encoders
		defineHidPageAction(uniqueId, pageNameBase, 'pitch', deck, 'word', 0x05, 1, -1000, 1000);
		defineHidPageAction(uniqueId, pageNameBase, 'leveldepth_knob', deck, 'uword', 0x07, 1, 0, 1023);// knob (marked 'level depth' on the silkscreen)
		defineHidPageAction(uniqueId, pageNameBase, 'browser', deck, 'uword', 0x04, 1, 0, 65535, 'encoder');
		defineHidPageAction(uniqueId, pageNameBase, 'looplessmore', deck, 'ubyte', 0x0C, 1, 0, 255, 'encoder');
		defineHidPageAction(uniqueId, pageNameBase, 'beat_encoder', deck, 'ubyte', 0x0D, 1, 0, 255, 'encoder');// encoder (marked 'beat' on the silkscreen)

		//------------------------------------
		defineHidPage(uniqueId, pageNameBase+'-shift', true);
		switch(deck)
		{
		case 0: // deck A
			defineHidPageDetect(uniqueId, pageNameBase+'-shift', 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, pageNameBase+'-shift', 'ubyte', 2, 1, 0x81);
			break;
		case 1: // deck B
			defineHidPageDetect(uniqueId, pageNameBase+'-shift', 'ubyte', 0, 1, 0x02);
			defineHidPageDetect(uniqueId, pageNameBase+'-shift', 'ubyte', 2, 1, 0x81);
			break;
		case 2: // deck C
			defineHidPageDetect(uniqueId, pageNameBase+'-shift', 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, pageNameBase+'-shift', 'ubyte', 2, 1, 0x41);
			break;
		case 3: // deck D
			defineHidPageDetect(uniqueId, pageNameBase+'-shift', 'ubyte', 0, 1, 0x02);
			defineHidPageDetect(uniqueId, pageNameBase+'-shift', 'ubyte', 2, 1, 0x41);
			break;
		}
		// actions
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'reverse', deck, 'bit', 0x20);// playpause
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'cueplay', deck, 'bit', 0x21);// cueplay
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'pitchrange', deck, 'bit', 0x22);// [same as without Shift]
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'keylock', deck, 'bit', 0x23);// [same as without Shift]
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'toggletime', deck, 'bit', 0x24);// autocue / time toggle / quantize (all marked on the silkscreen)// [same as without Shift]
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'backtotop', deck, 'bit', 0x25);// back/top -- not sent for Shift
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'loadtrack', deck, 'bit', 0x26);// the button on the encoder for browse// [same as without Shift]
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'reloop', deck, 'bit', 0x28);// loopinend
		//defineHidPageAction(uniqueId, pageNameBase+'-shift', 'enable-jogdrum', deck, 'bit', 0x29);// [same as without Shift] not sent for Shift
		//defineHidPageAction(uniqueId, pageNameBase+'-shift', 'enable-sampler', deck, 'bit', 0x2A);// [same as without Shift] not sent for Shift		
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'cue1', deck, 'bit', 0x2B);// trans / cue1
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'cue2', deck, 'bit', 0x2C);// flanger / cue2
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'cue3', deck, 'bit', 0x2D);// echo / cue3
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'cue4', deck, 'bit', 0x2E);// roll / cue4
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'sync', deck, 'bit', 0x30);// [same as without Shift]
		// sliders,knobs,encoders
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'pitch', deck, 'word', 0x05, 1, -1000, 1000);// [same as without Shift]
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'leveldepth_knob', deck, 'uword', 0x07, 1, 0, 1023);// knob (marked 'level depth' on the silkscreen)
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'browser', deck, 'uword', 0x04, 1, 0, 65535, 'encoder');// [same as without Shift]
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'loopprevnext', deck, 'ubyte', 0x0C, 1, 0, 255, 'encoder');//looplessmore
		defineHidPageAction(uniqueId, pageNameBase+'-shift', 'beat_encoder', deck, 'ubyte', 0x0D, 1, 0, 255, 'encoder');// encoder (marked 'beat' on the silkscreen)
			
		//------------------------------------
		defineHidPage(uniqueId, pageNameBase+'-jw', true);
		switch(deck)
		{
		case 0: // deck A
			defineHidPageDetect(uniqueId, pageNameBase+'-jw', 'ubyte', 0, 1, 0x03);
			defineHidPageDetect(uniqueId, pageNameBase+'-jw', 'ubyte', 2, 1, 0x80);
			break;
		case 1: // deck B
			defineHidPageDetect(uniqueId, pageNameBase+'-jw', 'ubyte', 0, 1, 0x04);
			defineHidPageDetect(uniqueId, pageNameBase+'-jw', 'ubyte', 2, 1, 0x80);
			break;
		case 2: // deck C
			defineHidPageDetect(uniqueId, pageNameBase+'-jw', 'ubyte', 0, 1, 0x03);
			defineHidPageDetect(uniqueId, pageNameBase+'-jw', 'ubyte', 2, 1, 0x40);
			break;
		case 3: // deck D
			defineHidPageDetect(uniqueId, pageNameBase+'-jw', 'ubyte', 0, 1, 0x04);
			defineHidPageDetect(uniqueId, pageNameBase+'-jw', 'ubyte', 2, 1, 0x40);
			break;
		}
		// actions
		defineHidPageAction(uniqueId, pageNameBase+'-jw', 'jogwheel-touch', deck, 'bit', 0x22);
		defineHidPageAction(uniqueId, pageNameBase+'-jw', 'jogwheel', deck, 'uword', 0x03, 1, 0, 65535, 'jogwheel');//~1200 distinct positions/rotation
		//------------------------------------
		defineHidPage(uniqueId, pageNameBase+'-shift-jw', true);
		switch(deck)
		{
		case 0: // deck A
			defineHidPageDetect(uniqueId, pageNameBase+'-shift-jw', 'ubyte', 0, 1, 0x03);
			defineHidPageDetect(uniqueId, pageNameBase+'-shift-jw', 'ubyte', 2, 1, 0x81);
			break;
		case 1: // deck B
			defineHidPageDetect(uniqueId, pageNameBase+'-shift-jw', 'ubyte', 0, 1, 0x04);
			defineHidPageDetect(uniqueId, pageNameBase+'-shift-jw', 'ubyte', 2, 1, 0x81);
			break;
		case 2: // deck C
			defineHidPageDetect(uniqueId, pageNameBase+'-shift-jw', 'ubyte', 0, 1, 0x03);
			defineHidPageDetect(uniqueId, pageNameBase+'-shift-jw', 'ubyte', 2, 1, 0x41);
			break;
		case 3: // deck D
			defineHidPageDetect(uniqueId, pageNameBase+'-shift-jw', 'ubyte', 0, 1, 0x04);
			defineHidPageDetect(uniqueId, pageNameBase+'-shift-jw', 'ubyte', 2, 1, 0x41);
			break;
		}
		// actions
		defineHidPageAction(uniqueId, pageNameBase+'-shift-jw', 'jogwheel-search-touch', deck, 'bit', 0x22);
		defineHidPageAction(uniqueId, pageNameBase+'-shift-jw', 'jogwheel-search', deck, 'uword', 0x03, 1, 0, 65535, 'jogwheel');//~1200 distinct positions/rotation

		//------------------------------------
		defineHidPage(uniqueId, pageNameBase+'-mixer', true);
		switch(deck)
		{
		case 0: // deck A
			defineHidPageDetect(uniqueId, pageNameBase+'-mixer', 'ubyte', 0, 1, 0x05);
			defineHidPageDetect(uniqueId, pageNameBase+'-mixer', 'ubyte', 2, 1, 0x80);
			break;
		case 1: // deck B
			defineHidPageDetect(uniqueId, pageNameBase+'-mixer', 'ubyte', 0, 1, 0x06);
			defineHidPageDetect(uniqueId, pageNameBase+'-mixer', 'ubyte', 2, 1, 0x80);
			break;
		case 2: // deck C
			defineHidPageDetect(uniqueId, pageNameBase+'-mixer', 'ubyte', 0, 1, 0x05);
			defineHidPageDetect(uniqueId, pageNameBase+'-mixer', 'ubyte', 2, 1, 0x40);
			break;
		case 3: // deck D
			defineHidPageDetect(uniqueId, pageNameBase+'-mixer', 'ubyte', 0, 1, 0x06);
			defineHidPageDetect(uniqueId, pageNameBase+'-mixer', 'ubyte', 2, 1, 0x40);
			break;
		}
		// actions
		defineHidPageAction(uniqueId, pageNameBase+'-mixer', 'gain', deck, 'uword', 0x02, 1, 0, 1023);
		defineHidPageAction(uniqueId, pageNameBase+'-mixer', 'eq-high', deck, 'uword', 0x03, 1, 0, 1023);
		defineHidPageAction(uniqueId, pageNameBase+'-mixer', 'eq-mid', deck, 'uword', 0x04, 1, 0, 1023);
		defineHidPageAction(uniqueId, pageNameBase+'-mixer', 'eq-low', deck, 'uword', 0x05, 1, 0, 1023);
		defineHidPageAction(uniqueId, pageNameBase+'-mixer', 'filter', deck, 'uword', 0x06, 1, 0, 1023);
		defineHidPageAction(uniqueId, pageNameBase+'-mixer', 'volume', deck, 'uword', 0x07, 1, 0, 1023);
		//------------------------------------
		//------------------------------------
		//------------------------------------
		// output pages
		defineHidPage(uniqueId, 'out-'+pageNameBase, false);
		switch(deck)
		{
		case 0: // deck A
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase, 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase, 'ubyte', 2, 1, 0x80);
			break;
		case 1: // deck B
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase, 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase, 'ubyte', 16, 1, 0x80);
			break;
		case 2: // deck C
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase, 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase, 'ubyte', 2, 1, 0x40);
			break;
		case 3: // deck D
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase, 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase, 'ubyte', 16, 1, 0x40);
			break;
		}
		// actions
		if(deck%2==0)// for decks A/C
		{
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'play', deck, 'bit', 0x20);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'cue', deck, 'bit', 0x21);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'keyLock', deck, 'bit', 0x23);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'looping', deck, 'bit', 0x28);// autoloop	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'jogdrum', deck, 'bit', 0x29);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'samplelaunch', deck, 'bit', 0x2A);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'fxBeatwaw', deck, 'bit', 0x2B);// trans / cue1
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'fxFlanger', deck, 'bit', 0x2C);// flanger / cue2
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'fxEcho', deck, 'bit', 0x2D);// echo / cue3
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'fxPan', deck, 'bit', 0x2E);// roll / cue4 (delay,reverb,pan,phaser)
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'onbeat', deck, 'bit', 0x30);// sync	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'master', deck, 'bit', 0x31);// ATTN: not used yet
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'jw_blue', deck, 'bit', 0x32);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'jw_red', deck, 'bit', 0x33);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'monitor', deck, 'bit', 0x38);// pfl
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'autoDj', -1, 'bit', 0xF0);// ATTN: we do need to declare this for every page even though it's global
		}else// for decks B/D
		{
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'play', deck, 'bit', 0x90);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'cue', deck, 'bit', 0x91);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'keyLock', deck, 'bit', 0x93);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'looping', deck, 'bit', 0x98);// autoloop	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'jogdrum', deck, 'bit', 0x99);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'samplelaunch', deck, 'bit', 0x9A);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'fxBeatwaw', deck, 'bit', 0x9B);// trans / cue1
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'fxFlanger', deck, 'bit', 0x9C);// flanger / cue2
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'fxEcho', deck, 'bit', 0x9D);// echo / cue3
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'fxPan', deck, 'bit', 0x9E);// roll / cue4 (delay,reverb,pan,phaser)
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'onbeat', deck, 'bit', 0xA0);// sync	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'master', deck, 'bit', 0xA1);// ATTN: not used yet
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'jw_blue', deck, 'bit', 0xA2);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'jw_red', deck, 'bit', 0xA3);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'monitor', deck, 'bit', 0xA8);// pfl
			defineHidPageAction(uniqueId, 'out-'+pageNameBase, 'autoDj', -1, 'bit', 0xF0);// ATTN: we do need to declare this for every page even though it's global
		}
		//------------------------------------
		defineHidPage(uniqueId, 'out-'+pageNameBase+'-shift', false);
		switch(deck)
		{
		case 0: // deck A
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-shift', 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-shift', 'ubyte', 2, 1, 0x81);
			break;
		case 1: // deck B
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-shift', 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-shift', 'ubyte', 16, 1, 0x81);
			break;
		case 2: // deck C
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-shift', 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-shift', 'ubyte', 2, 1, 0x41);
			break;
		case 3: // deck D
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-shift', 'ubyte', 0, 1, 0x01);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-shift', 'ubyte', 16, 1, 0x41);
			break;
		}
		// actions
		if(deck%2==0)// for decks A/C
		{
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'play', deck, 'bit', 0x20);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cue', deck, 'bit', 0x21);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'keyLock', deck, 'bit', 0x23);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'looping', deck, 'bit', 0x28);// autoloop	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'jogdrum', deck, 'bit', 0x29);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'samplelaunch', deck, 'bit', 0x2A);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cuepos1', deck, 'bit', 0x2B);// trans / cue1
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cuepos2', deck, 'bit', 0x2C);// flanger / cue2
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cuepos3', deck, 'bit', 0x2D);// echo / cue3
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cuepos4', deck, 'bit', 0x2E);// roll / cue4 (delay,reverb,pan,phaser)
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'onbeat', deck, 'bit', 0x30);// sync	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'master', deck, 'bit', 0x31);// ATTN: not used yet
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'jw_blue', deck, 'bit', 0x32);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'jw_red', deck, 'bit', 0x33);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'monitor', deck, 'bit', 0x38);// pfl
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'autoDj', -1, 'bit', 0xF0);// ATTN: we do need to declare this for every page even though it's global
		}else// for decks B/D
		{
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'play', deck, 'bit', 0x90);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cue', deck, 'bit', 0x91);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'keyLock', deck, 'bit', 0x93);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'looping', deck, 'bit', 0x98);// autoloop	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'jogdrum', deck, 'bit', 0x99);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'samplelaunch', deck, 'bit', 0x9A);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cuepos1', deck, 'bit', 0x9B);// trans / cue1
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cuepos2', deck, 'bit', 0x9C);// flanger / cue2
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cuepos3', deck, 'bit', 0x9D);// echo / cue3
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'cuepos4', deck, 'bit', 0x9E);// roll / cue4 (delay,reverb,pan,phaser)
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'onbeat', deck, 'bit', 0xA0);// sync	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'master', deck, 'bit', 0xA1);// ATTN: not used yet
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'jw_blue', deck, 'bit', 0xA2);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'jw_red', deck, 'bit', 0xA3);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'monitor', deck, 'bit', 0xA8);// pfl
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-shift', 'autoDj', -1, 'bit', 0xF0);// ATTN: we do need to declare this for every page even though it's global
		}
		//------------------------------------
		defineHidPage(uniqueId, 'out-'+pageNameBase+'-screen', false);
		switch(deck)
		{
		case 0: // deck A
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-screen', 'ubyte', 0, 1, 0x02);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-screen', 'ubyte', 2, 1, 0x80);
			break;
		case 1: // deck B
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-screen', 'ubyte', 0, 1, 0x02);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-screen', 'ubyte', 16, 1, 0x80);
			break;
		case 2: // deck C
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-screen', 'ubyte', 0, 1, 0x02);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-screen', 'ubyte', 2, 1, 0x40);
			break;
		case 3: // deck D
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-screen', 'ubyte', 0, 1, 0x02);
			defineHidPageDetect(uniqueId, 'out-'+pageNameBase+'-screen', 'ubyte', 16, 1, 0x40);
			break;
		}
		// actions
		if(deck%2==0)// for decks A/C
		{
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_remain', deck, 'bit', 0x20);
			// ATTN: we do need to declare these for every page even though it's global
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_tempo_6', -1, 'bit', 0x21);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_tempo_10', -1, 'bit', 0x22);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_tempo_16', -1, 'bit', 0x23);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_tempo_wide', -1, 'bit', 0x24);
			// the current and total time of the track
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'time_min', deck, 'ubyte', 0x06);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'time_sec', deck, 'ubyte', 0x07);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'time_msec', deck, 'ubyte', 0x08);	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'totaltime_min', deck, 'ubyte', 0x09);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'totaltime_sec', deck, 'ubyte', 0x0A);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'totaltime_msec', deck, 'ubyte', 0x0B);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'pitch', deck, 'word', 0x07, 1, -10000, 10000);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'bpm', deck, 'uword', 0x06);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'bpm_dot', deck, 'bit', 0x3B);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'vumeter', deck, 'ubyte', 0x05);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'vumeter_enable', deck, 'bit', 0x28);
		}else// for decks B/D
		{
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_remain', deck, 'bit', 0x90);
			// ATTN: we do need to declare these for every page even though it's global
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_tempo_6', -1, 'bit', 0x91);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_tempo_10', -1, 'bit', 0x92);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_tempo_16', -1, 'bit', 0x93);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'lcd_tempo_wide', -1, 'bit', 0x94);
			// the current and total time of the track
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'time_min', deck, 'ubyte', 0x14);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'time_sec', deck, 'ubyte', 0x15);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'time_msec', deck, 'ubyte', 0x16);	
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'totaltime_min', deck, 'ubyte', 0x17);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'totaltime_sec', deck, 'ubyte', 0x18);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'totaltime_msec', deck, 'ubyte', 0x19);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'pitch', deck, 'word', 0x0E, 1, -10000, 10000);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'bpm', deck, 'uword', 0x0D);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'bpm_dot', deck, 'bit', 0x3B);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'vumeter', deck, 'ubyte', 0x13);
			defineHidPageAction(uniqueId, 'out-'+pageNameBase+'-screen', 'vumeter_enable', deck, 'bit', 0x98);
		}
		//------------------------------------	
		// pre-set some leds/lcd elements
		callHidPageAction(uniqueId, 'lcd_remain', deck, timeStatus[deck]?1:0);
		callHidPageAction(uniqueId, 'bpm_dot', deck, 1);// enable
		callHidPageAction(uniqueId, 'vumeter_enable', deck, 1);// enable
		callHidPageAction(uniqueId, 'jw_blue', deck, 1);
		callHidPageAction(uniqueId, 'jw_red', deck, 0);
	}
	//------------------------------------
	defineHidPage(uniqueId, 'mixer', true);// input page
	defineHidPageDetect(uniqueId, 'mixer', 'ubyte', 0, 1, 0x07);
	// actions
	defineHidPageAction(uniqueId, 'mixer', 'monitor', 0, 'bit', 0x24);
	defineHidPageAction(uniqueId, 'mixer', 'monitor', 1, 'bit', 0x25);
	defineHidPageAction(uniqueId, 'mixer', 'monitor', 2, 'bit', 0x26);
	defineHidPageAction(uniqueId, 'mixer', 'monitor', 3, 'bit', 0x27);
	defineHidPageAction(uniqueId, 'mixer', 'automix', -1, 'bit', 0x28);
	defineHidPageAction(uniqueId, 'mixer', 'cuepgmmix', -1, 'uword', 0x03, 1, 0, 1023);
	defineHidPageAction(uniqueId, 'mixer', 'monitorgain', -1, 'uword', 0x04, 1, 0, 1023);
	defineHidPageAction(uniqueId, 'mixer', 'crossfader', -1, 'uword', 0x05, 1, 0, 1023);
	defineHidPageAction(uniqueId, 'mixer', 'cfcurve', -1, 'ubyte', 0x04);// 0x80 thru; 0x40 cross; 0x20 flat; -- only these 3 possible settings (and in this order)
	//------------------------------------
	callSendAllNotifications(uniqueId, true);// send all notifications to the controller (so that we don't have to manually init all leds/lcd screens)
}
//----------------------------------------------------------------------------- 
// 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+']');
	// set all lcd/leds off
	var initHidData = new Array(33);
	// clear all deck output pages
	for(var deck=0; deck<4; deck++)
	{
		//-----------------------------------------
		// main (without shift)
		for(var i=0; i<33; i++)	initHidData[i] = 0;
		switch(deck)
		{
		case 0: // deck A
			initHidData[0+1] = 0x01;
			initHidData[2+1] = 0x80;
			break;
		case 1: // deck B
			initHidData[0+1] = 0x01;
			initHidData[16+1] = 0x80;
			break;
		case 2: // deck C
			initHidData[0+1] = 0x01;
			initHidData[2+1] = 0x40;
			break;
		case 3: // deck D
			initHidData[0+1] = 0x01;
			initHidData[16+1] = 0x40;
			break;
		}
		sendHid(uniqueId, initHidData, 33);
		//-----------------------------------------
		// main (WITH shift)
		for(var i=0; i<33; i++)	initHidData[i] = 0;
		switch(deck)
		{
		case 0: // deck A
			initHidData[0+1] = 0x01;
			initHidData[2+1] = 0x81;
			break;
		case 1: // deck B
			initHidData[0+1] = 0x01;
			initHidData[16+1] = 0x81;
			break;
		case 2: // deck C
			initHidData[0+1] = 0x01;
			initHidData[2+1] = 0x41;
			break;
		case 3: // deck D
			initHidData[0+1] = 0x01;
			initHidData[16+1] = 0x41;
			break;
		}
		sendHid(uniqueId, initHidData, 33);
		//-----------------------------------------
		// LCD screen
		for(var i=0; i<33; i++)	initHidData[i] = 0;
		switch(deck)
		{
		case 0: // deck A
			initHidData[0+1] = 0x02;
			initHidData[2+1] = 0x80;
			break;
		case 1: // deck B
			initHidData[0+1] = 0x02;
			initHidData[16+1] = 0x80;
			break;
		case 2: // deck C
			initHidData[0+1] = 0x02;
			initHidData[2+1] = 0x40;
			break;
		case 3: // deck D
			initHidData[0+1] = 0x02;
			initHidData[16+1] = 0x40;
			break;
		}
		sendHid(uniqueId, initHidData, 33);
		//-----------------------------------------
		showMainText(uniqueId, deck, true);
	}
}
//----------------------------------------------------------------------------- 
// 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 XDJ-AERO specific things
function actionReceived(uniqueId,name,deck,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 'keylock': call('deckKeyLock', 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 'loopinout': 
		if(value>0)
		{ 
			if(getValue('looping', deck)) 
			{
				call('deckLoopExit', deck, 0x7F);
				call('deckLoopExit', deck, 0x00);
			}else 
			{
				call('deckLoopInEnd', deck, 0x7F);
				call('deckLoopInEnd', deck, 0x00);
			}
		}
		break;
	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);
		}
		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(deck%2==0) call('sampler1', value*0x7F);else call('sampler5', value*0x7F);
			//if(value>0) call('samplerPlay', (deck%2==0)?1:5);
		}else
		{
			call('deckFxBeatwaw', deck, value*0x7F); 
		}
		break;
	case 'flanger':
		if(samplerStatus[deck])
		{
			if(deck%2==0) call('sampler2', value*0x7F);else call('sampler6', value*0x7F);
			//if(value>0) call('samplerPlay', (deck%2==0)?2:6);
		}else
		{
			call('deckFxFlanger', deck, value*0x7F); 
		}
		break;
	case 'echo':
		if(samplerStatus[deck])
		{
			if(deck%2==0) call('sampler3', value*0x7F);else call('sampler7', value*0x7F);
			//if(value>0) call('samplerPlay', (deck%2==0)?3:7);
		}else
		{
			call('deckFxEcho', deck, value*0x7F); 
		}
		break;
	case 'pan':
		if(samplerStatus[deck])
		{
			if(deck%2==0) call('sampler4', value*0x7F);else call('sampler8', value*0x7F);
			//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;
	
	// 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); break;
	
	// jog-wheels
	case 'jogwheel': call('moveDeckJogWheel', deck, value, 1200); break;
	case 'jogwheel-search': call('searchDeckJW', deck, value, 1200); 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 ['+uniqueId+'] name='+name + ' deck='+deck + ' p1(int)='+param1 + ' p2(float)='+param2 + ' p3(str)='+param3);
	// callHidPageAction(const char *actionName, int deck, float value);
	if(name=='play') 		callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='cue') 		callHidPageAction(uniqueId, name, deck, param1?1:0);	
	if(name=='keyLock') 	callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='fxBeatwaw') 	callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='fxFlanger') 	callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='fxEcho') 		callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='fxPan') 		callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='onbeat') 		callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='monitor') 	callHidPageAction(uniqueId, name, deck, param1?1:0);	
	if(name=='cuepos1') 	callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='cuepos2') 	callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='cuepos3') 	callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='cuepos4') 	callHidPageAction(uniqueId, name, deck, param1?1:0);	
	if(name=='loopIn')
	{
		callHidPageAction(uniqueId, 'looping', deck, param1?1:0);
		// 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')
	{
		callHidPageAction(uniqueId, 'looping', deck, param1?1:0);
	}	
	if((name=='artist')||(name=='title')) showMainText(uniqueId, deck, false);

	// global
	if(name=='autoDj') 		callHidPageAction(uniqueId, name, deck, param1?1:0);
	if(name=='pitchRange')
	{
		// 1 = 4%; 2 = 8%; 3 = 16%; 4 = 24%; 5 = 32%; 6 = 50%;
		callHidPageAction(uniqueId, 'lcd_tempo_6', -1, (param1==1)?1:0);
		callHidPageAction(uniqueId, 'lcd_tempo_10', -1, (param1==2)?1:0);
		callHidPageAction(uniqueId, 'lcd_tempo_16', -1, (param1==3)?1:0);
		callHidPageAction(uniqueId, 'lcd_tempo_wide', -1, (param1>3)?1:0);
	}
}
//----------------------------------------------------------------------------- 
function getVuValue(dbValue)
{
	var vuVal = 0;
	if(dbValue>-48) vuVal = 35;// green
	if(dbValue>-32) vuVal = 70;// green
	if(dbValue>-24) vuVal = 105;// green
	if(dbValue>-18) vuVal = 140;// green
	if(dbValue>-12) vuVal = 175;// orange
	if(dbValue>-8)  vuVal = 210;// orange	
	if(dbValue>-4)  vuVal = 245;// red
	return vuVal;
}
//----------------------------------------------------------------------------- 
// 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<4; 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
		var pitch = getValue('pitch', i);// pitch (100th of percents)
		var dbValue = getValue('vumeterDeck', i);// float dB
		//debug('deck '+i+'  timeElapsed='+timeElapsed+'s  timeRemain='+timeRemain+'s  BPM='+bpm+'  pitch='+pitch);
		showTimeValue(uniqueId, i, timeElapsed, timeElapsed + timeRemain);
		callHidPageAction(uniqueId, 'pitch', i, pitch);
		callHidPageAction(uniqueId, 'bpm', i, bpm*10);
		callHidPageAction(uniqueId, 'vumeter', i, getVuValue(dbValue));
		// loop in led
		if(loopingInBlinkLed[i]!=-1)
		{
			// actually set the led
			callHidPageAction(uniqueId, 'looping', i, (loopingInBlinkLed[i]<5)?1:0);
			loopingInBlinkLed[i]++;
			if(loopingInBlinkLed[i]>=10) loopingInBlinkLed[i] = 0;
		}
	}
}
//----------------------------------------------------------------------------- 
