This is a part of the Kinetiszer project.

Dependencies:   inc

Dependents:   kinetisizer

hardware.c

Committer:
Clemo
Date:
2014-10-28
Revision:
1:8ae4ab73ca6a
Parent:
0:cb80470434eb

File content as of revision 1:8ae4ab73ca6a:

#include "atmegatron.h"
#include "lcd.h"

#define LOCKOUT 3000        //time between ctrl stop moving and locking it out
#define HOLDLENGTH 2000     //time required to hold function button for hold functions (e.g. patch save)

//const uint16_t bg_select_store[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};              //values used to light single led of led array
//const uint16_t bg_fill_store[16] = {1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};            //values used to fill leds between 0-x of led array
const uint16_t envADR_store[16] = {0,8,23,48,88,151,245,381,574,845,1217,1722,2399,3297,4476,6013};               //times used for attack, decay and release of envelopes
const uchar envS_store[16] = {0,17,34,51,68,85,102,119,136,153,170,187,204,221,238,255};                          //levels used for sustain of envelope1 (unipolar for amplitude)
const signed char fenvS_store[16] = {0,8,17,25,34,42,51,59,68,76,85,93,102,110,119,127};                                 //levels used for sustain of envelope2 (bipolar for filter)
const uint16_t speed_store[16] = {32767,16383,8191,6143,5460,4095,3071,2730,2047,1535,1023,767,511,383,255,127}; //values used for speeds of LFO and arpeggiator
const uint16_t porta_store[16] = {0,5,14,27,46,77,123,195,306,477,740,1145,1768,2728,4207,6484};                  //values for portamento speeds

//lets and gets (not all letable or getable at pres)
byte hard_func = 255;         //current selected function. 255 to allow reset in Init_Hardware
byte hard_val[16];            //current select value for each function.
byte hard_ctrl[2][6];         //current value for each digital control [bank][control]
boolean hard_shift[16];       //current value of shift for each function (red/green mode)

//long lastval;  //last value of value rotary encoder
byte maxval = 15;  //maximum value that is selectable on the value dial (normally 15 apart from user wave mode)
boolean testbutdown = false;  //test button (value encoder button)
byte hard_curctrlbank = 0;        //current value of ctrl bank (red/green mode of controls)
byte hard_curctrl = 1;            //current control being read.  Only 1 control is read in each pass.  read order:  0,1,0,2,0,3,0,4 etc, so cutoff is smooth as poss
byte hard_nextctrl = 1;           //sets next control to be read after cutoff.
byte lastfuncbut = 0;     //last value of function button input
byte lastvalbut = 0;      //last value of value button input
byte lastbank = 0;        //last value of ctrl bank button input
byte lastctrl[6];                 //last value read of each ctrl
boolean movingctrl[6];            //is a ctrl moving (i.e. is user turning it)
unsigned long stopmovingtick[6];  //tick when ctrl will lock out (to stop jitter) assuming user has stopped moving it
unsigned long butstarttick = 0;   //tick when button was pressed
boolean finishedhold = false;     //used by function button for function that require 2 sec hold (e.g. patch save)
boolean MIDIchannelmode = false;


void Hardware_Adjust_Value(byte func, byte val);
void Hardware_Adjust_Shift(byte func, boolean val);
boolean IsFuncFill(byte func);
boolean IsShiftHold(byte func);
void Hardware_Let_CtrlBank(byte value);
void Hardware_Refresh_FuncBG(void);
void Hardware_Refresh_ValBG(void);
int Hardware_Ctrl_Update(byte shift, byte index, int value);


// Here you can remap the rotary encoders in case you don't like
// the way they are set up.
//#define LEFT_HANDED
// 0 <-> 1
// 2 <-> 7
// 3 <-> 6
// 4 <-> 5
uint8_t encoder_to_ctrl[8] =
{
#if defined LEFT_HANDED
	0, // function encoder
	1, // value encoder
	2, // filter cut-off
	7, // filter resonance
	3, // filter envelope (red) / pitch envelope (green)
	4, // filter lfo (red) / pitch lfo (green)
	5, // amplitude lfo (red) / pulse width lfo (green)
	6  // distortion (red) / phase lfo (green)
#else
	0, // function encoder
	1, // value encoder
	7, // filter cut-off
	2, // filter resonance
	6, // filter envelope (red) / pitch envelope (green)
	5, // filter lfo (red) / pitch lfo (green)
	4, // amplitude lfo (red) / pulse width lfo (green)
	3  // distortion (red) / phase lfo (green)
#endif // LEFT_HANDED
};

#define ENCODER_FUNCTION  encoder_to_ctrl[0]
#define ENCODER_VALUE  encoder_to_ctrl[1]


//Hardware setup, only run once
void Init_Hardware(void)
{
	byte i;
	byte newctrl;
	//int ctrlread;

	memset(hard_shift,0,sizeof(hard_shift)); // Set everything to RED.

	Hardware_Let_Function(0);       //initialise function dial to 0

	//Hardware_LED_SetState(3,HIGH);  //initialise value encoder LED to red
	//Hardware_LED_SetState(2,LOW);
	Hardware_BicolorLED_SetState(2,2,1);

	Hardware_Refresh_ValBG();       //refresh value dial LED array

	Hardware_Let_CtrlBank(0);       //initialise ctrl bank to 0 (red mode)

	//READ FUNCTION ENCODER BUTTON to see if setting MIDI channel number
	newctrl = keyboard_get_pushbutton(ENCODER_FUNCTION,false);
	// If function button down, then MIDI channel mode.
	if (newctrl>0)
	{
		MIDIchannelmode = true;
		//initialise value encoder LED to yellow (woo)
		//Hardware_LED_SetState(3,HIGH);
		//Hardware_LED_SetState(2,HIGH);
		Hardware_BicolorLED_SetState(2,1,1);
		Hardware_Let_Value(FUNC_MEM,Memory_Channel_Read());
	}

	for (i=0; i<=5; i++)
	{
		//ctrlread = analogRead(i);     //initialise lastctrl array.  use analogread, coz speed not important
		//newctrl = ctrlread >> 2;      //all ctrls are stored as byte (0-255) not ints
		//lastctrl[i] = newctrl;
		lastctrl[i] = 128; // TODO: should get these values from EEPROM.
	}

	hard_curctrl = 0;
}


//*************POLL HARDWARE***********************
void Hardware_Poll(void)
{
	int newval;
	int newfunc;
	int temp;
	//byte r;
	byte newbut, newfuncbut, i, newvalbut;
	//int newctrl;
	//byte  diff;
	unsigned long butcurtick;
	//uint8_t low, high;
	uint8_t ctrl;

	// READ FUNCTION ENCODER
	newfunc = keyboard_get_rotary_encoder_value(ENCODER_FUNCTION);
	if (newfunc!=0)
	{
		// Grab display focus if needed.
		if (display_page_set(PAGE_FUNCTION)==0)
		{
			newfunc += hard_func;
			if (newfunc>15) newfunc = 0;
			else if (newfunc<0) newfunc = 15;
			Hardware_Let_Function(newfunc);
		}
		keyboard_set_rotary_encoder_value(ENCODER_FUNCTION,0);
	}

	/*newfunc = re3_value;
	if (newfunc>0)
	{
		if (hard_func==15)
		{
			// Increment function.
			Hardware_Let_Function(0);
		}
		else
		{
			Hardware_Let_Function(hard_func+1);
		}
		// Set encoder pulse number back to 0.
		re3_value = 0;
	}
	// Same as above but for anti-clockwise.
	else if (newfunc<0)
	{
		if (hard_func==0)
		{
			Hardware_Let_Function(15);
		}
		else
		{
			Hardware_Let_Function(hard_func-1);
		}
		// Set encoder pulse number back to 0.
		re3_value = 0;
	}*/

	// READ VALUE ENCODER
	newval = keyboard_get_rotary_encoder_value(ENCODER_VALUE);
	if (newval!=0)
	{
		// Grab display focus if needed.
		if (display_page_set(PAGE_FUNCTION)==0)
		{
			// Has encoder moved at all?
			if (newval!=0)
			{
				if (testbutdown==true)
				{
					// If value encoder button is down, change test note pitch.
					//if (newval>lastval)
					if (newval>0)
					{
						MIDI_TestButtonInc();
					}
					//if (newval<lastval)
					if (newval<0)
					{
						MIDI_TestButtonDec();
					}
					//lastval = newval;
				}
				else
				{
					// Allow roll-over of all functions (forget about "fill" functions).
					newval += hard_val[hard_func];
					if (newval>maxval) newval = 0;
					else if (newval<0) newval = maxval;
					Hardware_Let_Value(hard_func,newval);

					// Has encoder moved one click?
					/*if (newval>0)
					{
						// If not a fill function (fill functions are A,D,S,R & portamento) and value at max
						if (hard_val[hard_func]==maxval && IsFuncFill(hard_func)==false)
						{
							// Go back to 0.
							Hardware_Let_Value(hard_func,0);
						}
						// Otherwise increment value.
						else if (hard_val[hard_func]<maxval)
						{
							Hardware_Let_Value(hard_func,hard_val[hard_func]+1);
						}
					}
					else if (newval<0)
					{
						// Same as above but for anti-clockwise.
						if (hard_val[hard_func]==0 && IsFuncFill(hard_func)==false)
						{
							Hardware_Let_Value(hard_func,maxval);
						}
						else if (hard_val[hard_func]>0)
						{
							Hardware_Let_Value(hard_func,hard_val[hard_func]-1);
						}
					}*/
				}
			}
		}
		keyboard_set_rotary_encoder_value(ENCODER_VALUE,0);
	}

	//READ CTRL BANK BUTTON  (red/green mode)
	newbut = keyboard_get_pushbutton(BOARD_KEYBOARD_ISP,false);
	// Has a button just been pressed - do nothing on release
	if (lastbank!=newbut && newbut>0)
	{
		// Toggle ctrl bank.
		if (hard_curctrlbank==0)
		{
			Hardware_Let_CtrlBank(1);
		}
		else
		{
			Hardware_Let_CtrlBank(0);
		}
		// Set all ctrls to not moving, just in case last thing you did was move ctrl in other bank and not locked out.
		for (i=0; i<6; i++)
		{
			movingctrl[i] = false;
			stopmovingtick[i] = master_tick + LOCKOUT;
		}
		display_invalidate();
	}
	// Store last state of button.
	lastbank = newbut;

	//READ FUNCTION ENCODER BUTTON
	newfuncbut = keyboard_get_pushbutton(ENCODER_FUNCTION,false);
	// Are we on a function that requires function button to be held (e.g. save patch)?
	if (IsShiftHold(hard_func)==true)
	{
		//********SAVE AND LOAD IS DONE HERE**********
		//button pressed down, go green and wait
		if (lastfuncbut==0 && newfuncbut>0)
		{
			butstarttick = master_tick;
			Hard_Let_Shift(hard_func, true);
			finishedhold = false;
		}
		//is button still held
		else if (lastfuncbut>0 && newfuncbut>0)
		{
			butcurtick = master_tick - butstarttick;
			// Has button been held down for longer than hold length?
			if (butcurtick>=HOLDLENGTH && finishedhold==false)
			{
				switch (hard_func)
				{
					case FUNC_WAVE:
						// then do userwave write
						Memory_UserWave_Write(hard_val[FUNC_WAVE]);
						break;

					case FUNC_AENVA:
						// then write patch to sysex
						MIDI_SYSEX_write_patch();
						break;

					case FUNC_AENVR:
						// then start sysex mem dump
						Memory_SYSEX_write_mem();
						break;

					case FUNC_MEM:
						if (MIDIchannelmode==true)
						{
							MIDI_Set_Channel(hard_val[hard_func]);
						}
						else
						{
							lcd_cursor(1,0);
							lcd_puts("Saving...");
							SysTick_Delay(100);
							// then save patch
							Memory_Save(hard_val[hard_func]);
						}
						break;
				}
				// completed operation (to prevent refires)
				finishedhold = true;
				Hard_Let_Shift(hard_func, false);
				// make encoder flash
				Hardware_LED_StartFlash(0,7);
			}
		}
		// Has button been released...
		else if (newfuncbut==0 && Hard_Get_Shift(hard_func)==true)
		{
			butcurtick = master_tick - butstarttick;
			// and it's been held less time than hold length...
			if (butcurtick < HOLDLENGTH)
			{
				switch (hard_func)
				{
					case FUNC_WAVE:
						// ...then toggle user wave mode
						Memory_UserWave_Read(hard_val[FUNC_WAVE]);
						break;

					case FUNC_MEM:
						lcd_cursor(1,0);
						// then load patch
						if (Memory_Load(hard_val[hard_func])==true)
						{
							lcd_puts("Loading...");
						}
						else
						{
							lcd_puts("Empty...");
						}
						SysTick_Delay(100);
						for (i=0; i<6; i++)
						{
							// stop knob jitter from overriding loaded patch
							movingctrl[i] = false;
						}
						break;
				}
			}
			// Set shift back to false, LED red (red mode).
			Hard_Let_Shift(hard_func, false);
		}
	}
	else
	{
		// Button pressed, but not hold function.
		if (lastfuncbut!=newfuncbut && newfuncbut>0)
		{
			// ...then toggle shift  (red/green mode)
			Hard_Let_Shift(hard_func, !Hard_Get_Shift(hard_func));
		}
	}
	//store last button state
	lastfuncbut = newfuncbut;

	//READ VALUE ENCODER BUTTON
	newvalbut = keyboard_get_pushbutton(ENCODER_VALUE,false);
	//has button been pressed down
	if (lastvalbut==0 && newvalbut>0)
	{
		//midi test note on
		MIDI_TestButtonDown();
		testbutdown = true;
		//reset encoder pulse, because different ranges for midi test note mode.
		keyboard_set_rotary_encoder_value(ENCODER_VALUE,0);
		//knob goes green -> cpv red
		//Hardware_BicolorLED_SetState(1,2,1);
		//Hardware_LED_SetState(2,HIGH);
		//Hardware_LED_SetState(3,LOW);
	}
	//has button been released
	if (newvalbut==0 && lastvalbut>0)
	{
		//midi test note off
		MIDI_TestButtonUp();
		testbutdown = false;
		//reset encoder pulse again, to stop possible value dial jump
		keyboard_set_rotary_encoder_value(ENCODER_VALUE,0);
		//knob back to red -> cpv back to off
		//Hardware_BicolorLED_SetState(1,2,0);
	}
	//store last button state
	lastvalbut = newvalbut;

	// READ CTRL KNOBS
	ctrl = 0;
	// Skip Function & Value encoders.
	for (i=2; i<8; i++)
	{
		if (keyboard_get_pushbutton(encoder_to_ctrl[i],false)>0)
		{
			// Reset rotary encoder.
			Hardware_Ctrl_Update(hard_curctrlbank,ctrl,0);
			display_page_set(PAGE_CTRL);
			display_invalidate();
		}
		temp = keyboard_get_rotary_encoder_value(encoder_to_ctrl[i]);
		temp *= keyboard_get_rotary_encoder_accelerator(encoder_to_ctrl[i]);
		if (temp!=0)
		{
			temp += Hardware_Get_Ctrl(hard_curctrlbank,ctrl);
			// Grab display focus if needed.
			if (display_page_set(PAGE_CTRL)==0)
			{
				// No page change, we can update the control.
				Hardware_Ctrl_Update(hard_curctrlbank,ctrl,temp);
			}
			keyboard_set_rotary_encoder_value(encoder_to_ctrl[i],0);
		}
		// Next control.
		ctrl += 1;
	}
	//firstly has enough time passed to lock ctrl (to prevent jitter in DA readings effecting value)
	/*if (master_tick >= stopmovingtick[hard_curctrl])
	{
		movingctrl[hard_curctrl] = false;
	}
	//is reading from DA is ready
	if (bit_is_set(ADCSRA, ADSC)==false)
	{
		low  = ADCL;
		high = ADCH;
		//new reading of ctrl
		newctrl = ((high << 8) | low) >> 2;
		//calculate difference between new reading and old
		if (newctrl > lastctrl[hard_curctrl])
		{
			diff = newctrl-lastctrl[hard_curctrl];
		}
		else
		{
			diff = lastctrl[hard_curctrl]-newctrl;
		}
		//if difference > x then ctrl is being moved.  ctrl lockout sensitivity set here (default = 4)
		if (diff > 4)
		{
			movingctrl[hard_curctrl] = true;
			//ctrl is moving, so reset lockout time
			stopmovingtick[hard_curctrl] = master_tick + LOCKOUT;
		}
		if (movingctrl[hard_curctrl] == true)
		{
			Hardware_Let_Ctrl(hard_curctrlbank, hard_curctrl, newctrl);      //pass new value to ctrl parameter
			lastctrl[hard_curctrl] = newctrl;                                //store last ctrl state
		}

		//increment current ctrl to read.  order is: 0,1,0,2,0,3,0,4.. so filter cutoff ctrl is smooth
		if (hard_curctrl==0)
		{
			hard_curctrl = hard_nextctrl;
			hard_nextctrl++;
			if (hard_nextctrl>5)
			{
				hard_nextctrl = 1;
			}
		}
		else
		{
		 hard_curctrl = 0;
		}
		//start DA conversion of next ctrl going
		ADMUX = (1 << 6) | (hard_curctrl & 0x07);
		sbi(ADCSRA, ADSC);
	}*/

	//update LEDs that may be flashing
	//Hardware_LED_RefreshFlash();
}


int Hardware_Ctrl_Update(byte shift, byte index, int value)
{
	if (value<0)
	{
		//value = 255;
		value = 0;
	}
	else if (value>255)
	{
		//value = 0;
		value = 255;
	}
	Hardware_Let_Ctrl(shift,index,value);
	display_invalidate();
	return value;
}


void Hardware_Let_Ctrl(byte shift, byte index, byte value)
{
  switch (index)
  {
    case CTRL_FILT:                      //Filter Cutoff frequency (same in red and green mode)
      hard_ctrl[0][index] = value;
      hard_ctrl[1][index] = value;
      Filt_Let_Fc(value);
      break;

    case CTRL_Q:                        //Filter resonance (same in red and green mode)
      hard_ctrl[0][index] = value;
      hard_ctrl[1][index] = value;
      Filt_Let_Q(value);
      break;

    case CTRL_ENV:
      hard_ctrl[shift][index] = value;
      if (shift==0)
      {
        Filt_Let_FenvAmt(value);        //Filter envelope amount
      }
      else
      {
        Pitch_Let_FenvAmt(value);       //Pitch envelope amount
      }
      break;

    case CTRL_LFO:
      hard_ctrl[shift][index] = value;
      if (shift==0)
      {
        Filt_Let_LFOAmt(value);         //Filter LFO amount
      }
      else
      {
        Pitch_Let_LFOAmt(value);        //Pitch LFO amount
      }
      break;

    case CTRL_AMP:
      hard_ctrl[shift][index] = value;
      if (shift==0)
      {
        Amplitude_Let_LFOAmt(value);    //Amplitude LFO amount
      }
      else
      {
        PWM_Let_LFOAmt(value>>3);       //Pulse width modulation amount
      }
      break;

    case CTRL_FX:
      hard_ctrl[shift][index] = value;
      if (shift==0)
      {
        Distortion_Let_Amt(value>>5);  //Distortion (takes values 0-7)
      }
      else
      {
        Flange_Let_LFOAmt(value>>3);   //Phaser (takes values 0-31)
      }
      break;
  }
}


byte Hardware_Get_Ctrl(byte shift, byte index)
{
	return hard_ctrl[shift][index];
}


byte Hardware_Get_Ctrl_Shift(void)
{
	return hard_curctrlbank;
}


//**************FUNCTION KNOB*******************
//set current function
void Hardware_Let_Function(byte newfunc)
{
	if (newfunc!=hard_func)
	{
		//if new value is different to current
		hard_func = newfunc;
		display_invalidate();
		//Hardware_Refresh_FuncBG();                                  //refresh function dial LED array
		//Hardware_Refresh_ValBG();                                   //refresh value dial LED array (to show val of new func)
//		Hardware_Refresh_ShiftLED(hard_func);                       //refresh shift (red/green) mode (to show shift mode of new func)
		if (hard_shift[FUNC_AENVS]==true && hard_func==FUNC_WAVE)
		{
			//set max value of value dial (normally 15)
			maxval = 5;
		}
		else
		{
			maxval = 15;
		}
	}
}


byte Hardware_Get_Function()
{
	//get current function
	return hard_func;
}


void Hardware_Refresh_FuncBG(void)
{
	//user_interface_print_function(hard_func,hard_shift[hard_func]);
	display_invalidate();
}


//*************VALUE KNOB***********************
//set new value of function
void Hardware_Let_Value(byte func, byte newval)
{
	if (newval!=hard_val[func])
	{                      //is new value different to current value
		hard_val[func] = newval;
		Hardware_Adjust_Value(func, hard_val[func]);    //the meat of how the new value effects the sound
	    if (func==hard_func)
	    {
	    	Hardware_Refresh_ValBG();                     //refresh the value dial LED array
	    }
	}
}


void Hardware_Adjust_Value(byte func, byte val)
{               //the meat of how a new value effects the sound
  switch (func) {
      case FUNC_WAVE:
        if (hard_shift[FUNC_AENVS]==false){                    //if not user wave mode
          Wave_Let_Table(val);                                 //select waveform
        }
        break;
      case FUNC_FILT:
        Filt_Let_Type(val);                                    //set filter type
        break;
      case FUNC_FENVA:
        Fenv_Let_A(envADR_store[val]);       //set filter/pitch envelope attack time  (stored in PROGMEM)
        break;
      case FUNC_FENVDR:
        Fenv_Let_DR(envADR_store[val]);      //set filter/pitch envelope decay time
        break;
      case FUNC_FENVS:
        Fenv_Let_S(fenvS_store[val]);   //set filter/pitch envelope sustain level
        break;
      case FUNC_AENVA:
        Aenv_Let_A(envADR_store[val]);       //set amplitude envelope attack time
        break;
      case FUNC_AENVD:
        Aenv_Let_D(envADR_store[val]);       //set amplitude envelope decay time
        break;
      case FUNC_AENVS:
        Aenv_Let_S(envS_store[val]);         //set amplitude envelope sustain level
        break;
      case FUNC_AENVR:
        Aenv_Let_R(envADR_store[val]);       //set amplitude envelope release time
        break;
      case FUNC_LFOTYPE:
        LFO_Let_Type(val);                                     //select lfo waveform
        break;
      case FUNC_LFOSPEED:
        LFO_Let_Speed(speed_store[val]);//select lfo speed  (stored in PROGMEM)
        break;
#ifdef __HAS_ARPEGGIATOR__
      case FUNC_ARPTYPE:                                       //select arpeggiator pattern
        Arp_Let_Type(val);
        if (val==0)
        {
        	// Make sure that both LEDs are on when the arpeggiator is switched off.
			if (Hardware_Get_Ctrl_Shift()==GREEN)
			{
				Hardware_BicolorLED_SetState(1,1,2);
				Hardware_BicolorLED_SetState(2,1,2);
			}
			else
			{
				Hardware_BicolorLED_SetState(1,2,1);
				Hardware_BicolorLED_SetState(2,2,1);
			}
        }
        break;
      case FUNC_ARPSPEED:
        Arp_Let_Speed(speed_store[val]);            //set arpeggiator speed when free running
        MIDI_Let_ClockArpSpeed(speed_store[val]);   //set arpeggiator speed when MIDI clock synced
        break;
#endif // __HAS_ARPEGGIATOR__
      case FUNC_PORTA:
        Pitch_Let_Porta(porta_store[val]);          //set portamento time
        break;
      case FUNC_BITCRUSH:
        BitCrush_Let_Type(val);                                //set wave crusher setting
        break;
      case FUNC_MEM:                                           //this is a hold function, so do nothing (patch save)
        break;
  }
}


void Hardware_Refresh_ValBG(void)
{
	//user_interface_print_value(0,13,hard_val[hard_func],true);
	display_invalidate();
}


byte Hardware_Get_Value(byte func)
{
	return hard_val[func];
}


void Hard_Let_Shift(byte func, boolean newshift)
{
	if (newshift!=hard_shift[func])
	{
		switch (func)
		{
			// Tie filter/pitch envelope shifts together (envelope invert mode).
			case FUNC_FENVA:
			case FUNC_FENVDR:
			case FUNC_FENVS:
				hard_shift[FUNC_FENVA] = newshift;
				hard_shift[FUNC_FENVDR] = newshift;
				hard_shift[FUNC_FENVS] = newshift;
				break;

			// Tie LFO shifts together (LFO invert mode).
			case FUNC_LFOTYPE:
			case FUNC_LFOSPEED:
				hard_shift[FUNC_LFOTYPE] = newshift;
				hard_shift[FUNC_LFOSPEED] = newshift;
				break;

#ifdef __HAS_ARPEGGIATOR__
			// Tie arpeggiator shifts together (arpeggiator ping pong mode).
			case FUNC_ARPTYPE:
			case FUNC_ARPSPEED:
				hard_shift[FUNC_ARPTYPE] = newshift;
				hard_shift[FUNC_ARPSPEED] = newshift;
				break;
#endif // __HAS_ARPEGGIATOR__

			default:
				hard_shift[func] = newshift;
		}
		// Meat of how shift affects sound.
		Hardware_Adjust_Shift(func, newshift);
		if (func==hard_func)
		{
		//	Hardware_Refresh_ShiftLED(func);          //refresh function knob LED colour
			Hardware_Refresh_FuncBG();
		}
		// Update display.
		display_invalidate();
	}
}


boolean Hard_Get_Shift(byte func)
{
	return hard_shift[func];
}


// Meat of how shift affects sound.
void Hardware_Adjust_Shift(byte func, boolean val)
{
  //byte i;
  switch (func)
  {
      case FUNC_WAVE:
        if (hard_shift[FUNC_AENVS]==false)
        {
          if (val==true)
          {
            Wave_Let_Bank(1);                          //set waveform bank
          }
          else
          {
            Wave_Let_Bank(0);
          }
        }
        break;

      case FUNC_FILT:
        Filt_Let_GainAdj(val);                          //set filter normalise mode
        break;

      case FUNC_FENVA:
      case FUNC_FENVDR:
      case FUNC_FENVS:
        Fenv_Let_Invert(!Fenv_Get_Invert());            //set filter/pitch envelope invert mode
        break;

      case FUNC_AENVA:                                  //hold function, so do nothing here  (SEE Hardware_Poll)
        break;

      case FUNC_AENVD:
        MIDI_Let_SYSEXRead(val);                        //set sysex read mode
        break;

      case FUNC_AENVS:
        Wave_Let_UserMode(val);                         //set user wave mode
        if (val==true && hard_val[FUNC_WAVE]>5)
        {
          Hardware_Let_Value(FUNC_WAVE,5);
        }
        break;

      case FUNC_AENVR:                                  //hold function, so do nothing here  (SEE Hardware_Poll)
        break;

      case FUNC_LFOTYPE:
      case FUNC_LFOSPEED:
        LFO_Let_Invert(val);                            //set LFO invert mode
        break;

#ifdef __HAS_ARPEGGIATOR__
      case FUNC_ARPTYPE:
      case FUNC_ARPSPEED:
        Arp_Let_PingPong(val);                          //set arpeggiator ping pong mode
        break;
#endif // __HAS_ARPEGGIATOR__

      case FUNC_PORTA:
        Pitch_Let_PropPorta(val);                       //set proportional portamento mode
        break;

      case FUNC_BITCRUSH:
        BitCrush_Let_PreFilt(val);                      //set wave crusher pre-filter mode

        // CPV: no break???

      case FUNC_MEM:                                    //hold function, so do nothing here  (SEE Hardware_Poll)
        break;
  }
}


//*************CTRL BANK BUTTON***********************
//set ctrl bank (red/green mode)
void Hardware_Let_CtrlBank(byte value)
{
	hard_curctrlbank = value;
	//Hardware_Refresh_CtrlBankLED();              //refresh LED color
	Hardware_BicolorLED_SetState(1,hard_curctrlbank!=0?1:0,hard_curctrlbank==0?1:0);
	Hardware_BicolorLED_SetState(2,hard_curctrlbank!=0?1:0,hard_curctrlbank==0?1:0);
	/*if (hard_curctrlbank==0)
	{
		Hardware_BicolorLED_SetState(false,true);
		//Hardware_LED_SetState(2,false);
		//Hardware_LED_SetState(3,true);
	}
	else
	{
		Hardware_BicolorLED_SetState(true,false);
		//Hardware_LED_SetState(2,true);
		//Hardware_LED_SetState(3,false);
	}*/
}


void Hardware_LED_SetState(byte LEDnum, byte LEDstate)
{
	if (LEDnum==BOARD_LED2_GREEN)
	{
//		Board_LED_Set(BOARD_LED2_GREEN,LEDstate==HIGH?BOARD_LED_ON:BOARD_LED_OFF);
	}
	else if (LEDnum==BOARD_LED2_RED)
	{
//		Board_LED_Set(BOARD_LED2_RED,LEDstate==HIGH?BOARD_LED_ON:BOARD_LED_OFF);
	}
	else if (LEDnum==BOARD_LED3)
	{
		// MIDI LED.
		Board_LED_Set(BOARD_LED3,LEDstate);
	}
}


void Hardware_BicolorLED_SetState(uint8_t led, uint8_t green, uint8_t red)
{
	if (led==1)
	{
		if (green<2) Board_LED_Set(BOARD_LED1_GREEN,green==1?BOARD_LED_ON:BOARD_LED_OFF);
		if (red<2) Board_LED_Set(BOARD_LED1_RED,red==1?BOARD_LED_ON:BOARD_LED_OFF);
	}
	else if (led==2)
	{
		if (green<2) Board_LED_Set(BOARD_LED2_GREEN,green==1?BOARD_LED_ON:BOARD_LED_OFF);
		if (red<2) Board_LED_Set(BOARD_LED2_RED,red==1?BOARD_LED_ON:BOARD_LED_OFF);
	}
}


void Hardware_LED_StartFlash(byte LEDnum, byte FlashTimes)
{
	//SysTick_LED_Flash(LEDnum==0?BOARD_LED1_GREEN:BOARD_LED1_RED,10);
	if (LEDnum==4)
	{
		SysTick_LED_Flash(BOARD_LED3,10);
	}
}


/*boolean IsFuncFill(byte func)
{
  //returns true for functions where value dial fills up
  switch (func) {
      case FUNC_WAVE:
      case FUNC_FILT:
      case FUNC_LFOTYPE:
      case FUNC_LFOSPEED:
      case FUNC_ARPTYPE:
      case FUNC_ARPSPEED:
      case FUNC_BITCRUSH:
      case FUNC_MEM:
        return false;
        break;
      default:
        return true;
  }
}*/


boolean IsShiftHold(byte func)
{
  //returns true for functions where encoder button should be held down
  switch (func) {
      case FUNC_WAVE:
        return hard_shift[FUNC_AENVS];
        //break;
      case FUNC_AENVA:
      case FUNC_AENVR:
      case FUNC_MEM:
        return true;
        //break;
      default:
        return false;
  }
}