//-----------------------------------------------------------------------------
//------------ Copyright (c)2003-2016 Xylio. All Rights Reserved. -------------
//-----------------------------------------------------------------------------
// file implementing MIDI controller 'Behringer CMD Studio 2A'
//-----------------------------------------------------------------------------
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 "Behringer CMD Studio 2A";
}
//-----------------------------------------------------------------------------
// 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 getSupportedMidiDeviceVID(osId)
{
	return 0x1397;
}
//-----------------------------------------------------------------------------
function getSupportedMidiDevicePID(osId)
{
	return 0x00C7;
}
//-----------------------------------------------------------------------------
/*
function getSupportedMidiDeviceVID2(osId)
{
	return 0x1397;
}
//-----------------------------------------------------------------------------
function getSupportedMidiDevicePID2(osId)
{
	return 0x????;
}
*/
//-----------------------------------------------------------------------------
// 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 "00014E020013";
	return false;// it looks like this controller does not have a SysEx ID
	// F0... -- Behringer CMD Studio 2A complete sysex
}
//-----------------------------------------------------------------------------
// 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==0) return "Behringer CMD Studio 2A";
	return "Studio 2A";
}
//-----------------------------------------------------------------------------
function getSupportedMidiDeviceNameForOutput(osId)
{
	//if(osId==0) return "Behringer CMD Studio 2A";
	return "Studio 2A";
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// some variables necessary only for this particular script
var jogWheelsInScratchMode=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 Behringer CMD Studio 2A ['+uniqueId+']');// this outputs a string to DebugView app on Win
	// setting audio (the actual routing is done automatically for this controller so
	// we don't need to manually set "RoutingOutHeadphones", "RoutingOutDeckA", etc
	// ATTN: we first try to set ASIO mode, and only then regular CoreAudio, DirectSound audio/WDM/Wasapi
	if(setAudioDevice("Behringer CMD Studio 2A ASIO|||CMD Studio 2A ASIO|||Studio 2A ASIO|||Behringer CMD Studio 2A|||CMD Studio 2A|||Studio 2A|||Speakers (Behringer CMD Studio 2A)|||Speakers (CMD Studio 2A)|||Speakers (Studio 2A)"))
	{
		//debug('AudioDevice was set OK');
		setSetting('AudioDevice2', "NO AUDIO");
	}
	//sendMidi(uniqueId, status, channel, data1, data2);
	sendMidi(uniqueId,0x90,0,0x22,(jogWheelsInScratchMode ? 0x01 : 0x00));// ATTN: it's 0x01 for led ON, not 0x7F like it's usually
	//------------------------------------
	callSendAllNotifications(uniqueId, true);// send all notifications to the controller (so that we don't have to manually init all leds/lcd screens)
	// update all analog controls (eg. fader/knob positions)
	//sendMidi(uniqueId,0xB0,0,0x7F,0x7F);
}
//-----------------------------------------------------------------------------
// 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 ['+uniqueId+']');
	// set leds off
	sendMidi(uniqueId,0x90,0,0x22,0x00);// scratch mode
	//------------------------------------
	callSendAllNotifications(uniqueId, false);// send all notifications to the controller (so that we don't have to manually de-init all leds/lcd screens)
}
//-----------------------------------------------------------------------------
// this is your main target function to implement the controller -- this is called
// for each and every MIDI message that comes from the controller itself
// return true if the message was processed by the script and false to let it
// pass thru the software (eg. so it can be assigned by the user using the LEARN function)
function messageReceived(uniqueId,status,channel,data1,data2)
{
	var messageNotProcessed=false;
	switch(status)
	{
	//-----------------------------------------------
	// controllers: sliders, knobs, jog-wheels, encoders ('moving' things)
	case 0xB0:
		switch(data1)
		{
		// global
		case 0x20: call('setCrossfader', data2); break;
		case 0x21: call('setMonitorVolume', data2); break;

		// deck A
		// jog-wheel
		case 0x03: // with or without touch (hold)
			var val1 = data2-0x40;// -1 (to left), +1 (to right)
			// params: (deck, increment, total positions per complete revolution)
			if(jogWheelsInScratchMode) call('scratchDeckJW', 0, val1, 170);else call('moveDeckJogWheel', 0, val1, 170);
			break;
		// pitch (encoder)
		case 0x05:
			var val1 = data2-0x40;// -1 (to left), +1 (to right)
			call('moveDeckPitch', 0, val1);
			break;
		case 0x13: call('setDeckEqHigh', 0, data2); break;
		case 0x14: call('setDeckEqMid',  0, data2); break;
		case 0x15: call('setDeckEqLow',  0, data2); break;
		case 0x18: call('setDeckVolume', 0, data2); break;

		// deck B
		// jog-wheel
		case 0x33: // with or without touch (hold)
			var val1 = data2-0x40;// -1 (to left), +1 (to right)
			// params: (deck, increment, total positions per complete revolution)
			if(jogWheelsInScratchMode) call('scratchDeckJW', 1, val1, 170);else call('moveDeckJogWheel', 1, val1, 170);
			break;
		// pitch (encoder)
		case 0x35:
			var val1 = data2-0x40;// -1 (to left), +1 (to right)
			call('moveDeckPitch', 1, val1);
			break;
		case 0x43: call('setDeckEqHigh', 1, data2); break;
		case 0x44: call('setDeckEqMid',  1, data2); break;
		case 0x45: call('setDeckEqLow',  1, data2); break;
		case 0x48: call('setDeckVolume', 1, data2); break;

		default: messageNotProcessed = true;
		}
		break;
	//-----------------------------------------------
	// note on/off: buttons, switches ('pressing' things)
	case 0x80:
	case 0x90:
		switch(data1)
		{
		// global
		case 0x22:
			if(data2>0x3F)
			{
				jogWheelsInScratchMode = (jogWheelsInScratchMode + 1) % 2;
				sendMidi(uniqueId,0x90,0,0x22,(jogWheelsInScratchMode ? 0x01 : 0x00));
			}
			break;
		case 0x23: call('mixNext', data2); break;// Mode
		//case 0x23: call('setAutoDj', data2); break;// Mode
		case 0x24: if(data2>0x3F) call('lbMoveCursor', -1); break;// up
		case 0x25: if(data2>0x3F) call('lbBrowserSelect', data2); break;// folder
		case 0x26: if(data2>0x3F) call('lbToggleSelect', data2); break;// file
		case 0x27: if(data2>0x3F) call('lbMoveCursor', +1); break;// down

		// deck A
		case 0x01: call('deckCue', 0, data2); break;
		case 0x02: call('deckPlayPause', 0, data2); break;
		case 0x03: call('holdDeckJogWheel', 0, data2); break;
		case 0x04: call('deckSync', 0, data2); break;
		case 0x06: 
		{
			call('deckBendDown', 0, data2); 
			sendMidi(uniqueId,0x90,0,data1,(data2 ? 0x01 : 0x00));
			break;
		}
		case 0x07:
		{
			call('deckBendUp', 0, data2); 
			sendMidi(uniqueId,0x90,0,data1,(data2 ? 0x01 : 0x00));
			break;
		}
		// not used (the user can assign them using LEARN)
		//case 0x08: call('deckBendUp', 0, data2); break;// Assign A
		//case 0x09: call('deckBendUp', 0, data2); break;// Assign B
		case 0x0A: call('deckCuePos1', 0, data2); break;
		case 0x0B: call('deckCuePos2', 0, data2); break;
		case 0x0C: call('deckCuePos3', 0, data2); break;
		case 0x0D: call('deckCuePos4', 0, data2); break;
		case 0x0E: call('sampler1', data2); break;
		case 0x0F: call('sampler2', data2); break;
		case 0x11: call('sampler3', data2); break;
		case 0x12: call('sampler4', data2); break;
		case 0x16: call('deckMonitor', 0, data2); break;
		case 0x17: if(data2>0x3F) call('deckLBLoad', 0, data2); break;

		// deck B
		case 0x31: call('deckCue', 1, data2); break;
		case 0x32: call('deckPlayPause', 1, data2); break;
		case 0x33: call('holdDeckJogWheel', 1, data2); break;
		case 0x34: call('deckSync', 1, data2); break;
		case 0x36:
		{
			call('deckBendDown', 1, data2); 
			sendMidi(uniqueId,0x90,0,data1,(data2 ? 0x01 : 0x00));
			break;
		}
		case 0x37:
		{
			call('deckBendUp', 1, data2); 
			sendMidi(uniqueId,0x90,0,data1,(data2 ? 0x01 : 0x00));
			break;
		}
		// not used (the user can assign them using LEARN)
		//case 0x38: call('deckBendUp', 1, data2); break;// Assign A
		//case 0x39: call('deckBendUp', 1, data2); break;// Assign B
		case 0x3A: call('deckCuePos1', 1, data2); break;
		case 0x3B: call('deckCuePos2', 1, data2); break;
		case 0x3C: call('deckCuePos3', 1, data2); break;
		case 0x3D: call('deckCuePos4', 1, data2); break;
		case 0x3E: call('sampler5', data2); break;
		case 0x3F: call('sampler6', data2); break;
		case 0x41: call('sampler7', data2); break;
		case 0x42: call('sampler8', data2); break;
		case 0x46: call('deckMonitor', 1, data2); break;
		case 0x47: if(data2>0x3F) call('deckLBLoad', 1, 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>=2) return;
	var nameLen = name.length;

	// ATTN: it's 0x01 for led ON, not 0x7F like it's usually
	if(name=='cue') 		sendMidi(uniqueId,0x90,0, (deck==0)?0x01:0x31, (param1 ? 0x01 : 0x00));
	if(name=='play') 		sendMidi(uniqueId,0x90,0, (deck==0)?0x02:0x32, (param1 ? 0x01 : 0x00));
	if(name=='onbeat') 		sendMidi(uniqueId,0x90,0, (deck==0)?0x04:0x34, (param1 ? 0x01 : 0x00));// sync button
	if(name=='monitor') 	sendMidi(uniqueId,0x90,0, (deck==0)?0x16:0x46, (param1 ? 0x01 : 0x00));
	if(name=='autoDj') 		sendMidi(uniqueId,0x90,0, 0x23,(param1 ? 0x01 : 0x00));// automix (Mode button)
	// justLoaded: 1,2 (loading in progress), 4 (loading ended), 8 (progress), 16 (detection started), 32 (detection finished);
	if(name=='justLoaded')
	{
		if(param1!=0 && param1!=32)
		{
			// loading
			sendMidi(uniqueId,0x90,0,(deck==0)?0x17:0x47,0x01);// led on
		}else
		{
			// loading finished
			sendMidi(uniqueId,0x90,0,(deck==0)?0x17:0x47,0x00);// led off
		}
	}
	// cue points
	if(name=='cuepos1') 	sendMidi(uniqueId,0x90,0, (deck==0)?0x0A:0x3A, (param1 ? 0x01 : 0x00));
	if(name=='cuepos2') 	sendMidi(uniqueId,0x90,0, (deck==0)?0x0B:0x3B, (param1 ? 0x01 : 0x00));
	if(name=='cuepos3') 	sendMidi(uniqueId,0x90,0, (deck==0)?0x0C:0x3C, (param1 ? 0x01 : 0x00));
	if(name=='cuepos4') 	sendMidi(uniqueId,0x90,0, (deck==0)?0x0D:0x3D, (param1 ? 0x01 : 0x00));
	
	// sampler leds
	if(nameLen==13)
	{
		var name1 = name.substr(0, nameLen-1);
		if(name1=='samplerBlink')
		{
			// left side (deck A)
			if(name=='samplerBlink1') sendMidi(uniqueId,0x90,0,0x0E,(param1 ? 0x01 : 0x00));
			if(name=='samplerBlink2') sendMidi(uniqueId,0x90,0,0x0F,(param1 ? 0x01 : 0x00));
			if(name=='samplerBlink3') sendMidi(uniqueId,0x90,0,0x11,(param1 ? 0x01 : 0x00));
			if(name=='samplerBlink4') sendMidi(uniqueId,0x90,0,0x12,(param1 ? 0x01 : 0x00));
			// right side (deck B)
			if(name=='samplerBlink5') sendMidi(uniqueId,0x90,0,0x3E,(param1 ? 0x01 : 0x00));
			if(name=='samplerBlink6') sendMidi(uniqueId,0x90,0,0x3F,(param1 ? 0x01 : 0x00));
			if(name=='samplerBlink7') sendMidi(uniqueId,0x90,0,0x41,(param1 ? 0x01 : 0x00));
			if(name=='samplerBlink8') sendMidi(uniqueId,0x90,0,0x42,(param1 ? 0x01 : 0x00));
		}
	}
}
//-----------------------------------------------------------------------------
// 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+']');
}
//-----------------------------------------------------------------------------
