This is a part of the Kinetiszer project.

Dependencies:   inc

Dependents:   kinetisizer

Revision:
0:cb80470434eb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/midi.c	Tue Oct 28 12:19:42 2014 +0000
@@ -0,0 +1,573 @@
+/*
+Copyright 2013 Paul Soulsby www.soulsbysynths.com
+    This file is part of Atmegatron.
+
+    Atmegatron is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Atmegatron is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Atmegatron.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "atmegatron.h"
+
+
+//frequency of all midi notes  (rounded to nearest integer)
+const uint16_t MIDI_freqs[128] =
+{
+	 8, 9, 9,10,10,11,12,12,13,14,15,15,16,17,18,19,
+	21,22,23,24,26,28,29,31,33,35,37,39,41,44,46,49,
+	52,55,58,62,65,69,73,78,82,87,92,98,104,110,117,123,
+	131,139,147,156,165,175,185,196,208,220,233,247,262,277,294,311,
+	330,349,370,392,415,440,466,494,523,554,587,622,659,698,740,784,
+	831,880,932,988,1047,1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976,
+	2093,2217,2349,2489,2637,2794,2960,3136,3322,3520,3729,3951,4186,4435,4699,4978,
+	5274,5588,5920,6272,6645,7040,7459,7902,8372,8870,9397,9956,10548,11175,11840,12544
+};
+
+
+//local vars
+
+boolean arp_triggernext=false;                //trigger arpeggiator note on next midi clock
+boolean gettingSYSEX=false;                   //in the middle of getting sysex message
+boolean memDumping=false;                     //in the middle of dumping memory to librarian software
+byte curMemDump=0;                            //current stage of memory dump
+byte SYSEXbytenum=0;                          //byte number of sysex message
+byte SYSEXmesstype=0;                         //type of sysex message
+
+//lets and gets
+byte MIDI_curnote = 57;                       //current note pressed
+byte MIDI_curbutnote = 60;                    //current note of test button
+boolean MIDI_keydown[128];                    //store which keys are down for arpeggiator
+signed char MIDI_totnoteson =0;                      //total notes held down 
+char MIDI_lasttotnoteson = 0;                 //last total notes held down
+boolean MIDI_clockpresent=false;              //is midi clock present?
+boolean MIDI_SYSEXread=false;                 //is sysex read enabled?
+unsigned long MIDI_clocktick=0;               
+unsigned long MIDI_clockticksperarpstep=12;
+unsigned long MIDI_clocknextarp=0;
+char MIDI_pbend_level=0; 
+byte MIDI_channel=0;
+
+//Lets and Gets
+byte MIDI_Get_curNote(void)
+{                            //get current midi note
+  return MIDI_curnote;
+}
+
+
+unsigned int MIDI_Get_Freq(byte notenum)
+{          //get current midi frequency (potentially different to actual current frequency in pitch tab)
+  return MIDI_freqs[notenum];
+}
+
+
+boolean MIDI_Get_KeyDown(byte notenum)
+{            //find out if note is down
+  return MIDI_keydown[notenum];
+}
+
+
+void MIDI_Let_ClockArpSpeed(unsigned int newspeed)
+{                            //set clock arp speed.  not actual speed.  see Arp_Let_Speed for how this works
+  MIDI_clockticksperarpstep = ((unsigned long)newspeed+1) * 24 >> 13;
+}
+
+
+void MIDI_Let_SYSEXRead(boolean newval)
+{          //set sysex read mode
+  MIDI_SYSEXread = newval;
+}
+
+
+//boolean MIDI_Get_SYSEXRead(void)
+//{
+//  return MIDI_SYSEXread;
+//}
+
+
+boolean MIDI_Get_ClockPresent(void)
+{                   //return if midi clock is preset
+  return MIDI_clockpresent;
+}
+
+
+void MIDI_Set_Channel(byte newchannel)
+{
+  if(newchannel!=MIDI_channel){
+    MIDI_channel = newchannel;
+    Memory_Channel_Write(newchannel);
+  }
+}
+
+//MIDI meat
+//initialise serial port
+void MIDI_Init(void)
+{                                  //enable midi.  just uses standard arduino serial ports.
+  MIDI_channel = Memory_Channel_Read();
+  Serial.begin(31250);
+}
+
+
+//********MIDI note stuff***********
+//press key
+void MIDI_NoteOn(byte notenum)
+{
+  MIDI_curnote = notenum;                                    //set current note = new note
+  MIDI_lasttotnoteson = MIDI_totnoteson;                     //set last total notes on
+  MIDI_totnoteson++;                                         //increment total notes on
+  MIDI_keydown[notenum] = true;                              //set midi key down = true
+#ifdef __HAS_ARPEGGIATOR__
+  if (Arp_Get_Type()==0)
+#endif // __HAS_ARPEGGIATOR__
+  {                                    //if arpeggiator off then
+    MIDI_TriggerNote(notenum);                               //trigger note
+  }
+#ifdef __HAS_ARPEGGIATOR__
+  else if (MIDI_lasttotnoteson==0 && MIDI_totnoteson==1) {   //if arpeggiator on and this is the first note pressed down
+    Arp_Reset();                                             //reset arpeggiator and lfo, so they sync together       
+    LFO_Reset();          
+  }
+#endif // __HAS_ARPEGGIATOR__
+}
+
+
+//release key
+void MIDI_NoteOff(byte notenum)
+{
+  MIDI_keydown[notenum] = false;                             //set midi key down = false
+  MIDI_lasttotnoteson = MIDI_totnoteson;                     //set last total notes on
+  MIDI_totnoteson--;                                         //decrement total notes on
+  if (MIDI_totnoteson<=0){                                   //if total notes on = 0 (ie all keys released)  (use <= for safety)
+    AEnv_Release();                                          //release amplitude envelope
+    FEnv_Release();                                          //release filter envelope
+    if (MIDI_totnoteson<0){                                  //safety - if tot notes on < 0
+      MIDI_totnoteson = 0;                                   //reset to 0 (<0 not possible)
+    }
+  }
+}
+
+
+//*********TRIGGER A NOTE**************** (used by arpeggiator too)
+void MIDI_TriggerNote(byte notenum)
+{
+  Pitch_Let_NextFreq(MIDI_freqs[notenum]);                //set next frequency (pitch)
+  Pitch_ResetPorta();                                                       //set portamento start tick
+  AEnv_Trigger();                                                           //trigger amplitude envelope
+  FEnv_Trigger();                                                           //trigger filter envelope
+}
+
+
+//reset all notes
+void MIDI_Reset(void)
+{
+	uint8_t i;
+	for (i=0; i<=127; i++)
+	{
+		MIDI_NoteOff(i);
+	}
+}
+
+
+//press the test button (value knob)
+void MIDI_TestButtonDown(void)
+{
+  Pitch_Let_NextFreq(MIDI_freqs[MIDI_curbutnote]);       //set next frequency
+  Pitch_ResetPorta();                                                      //set portamento start tick
+  AEnv_Trigger();                                                          //trigger amplitude envelope
+  FEnv_Trigger();                                                          //trigger filter envelope
+}
+
+
+//release the test button (value knob)
+void MIDI_TestButtonUp(void)
+{
+  if (MIDI_totnoteson<=0){                                                 //prevent stuck notes
+    AEnv_Release();                                                        //release amplitude envelope
+    FEnv_Release();                                                        //release filter envelope
+  }  
+}
+
+
+//increment test button note (clockwise turn)
+void MIDI_TestButtonInc(void)
+{
+  if (MIDI_curbutnote<126){                                                //if cur test button note < max midi note
+    MIDI_curbutnote++;                                                     //increment
+    Pitch_Let_NextFreq(MIDI_freqs[MIDI_curbutnote]);     //set next frequency (pitch)
+  }
+}
+
+
+//decrement test button note (anti clockwise turn)
+void MIDI_TestButtonDec(void)
+{
+  if (MIDI_curbutnote>1){                                                  //if cur test button note > min midi note
+    MIDI_curbutnote--;                                                     //decrement
+    Pitch_Let_NextFreq(MIDI_freqs[MIDI_curbutnote]);     //set next frequency (pitch)
+  }
+}
+
+
+//*******MIDI clock stuff********
+//MIDI clock start received
+void MIDI_ClockStart(void)
+{
+  MIDI_clockpresent = true;       //set clock present 
+  MIDI_clocktick = 0;             //reset clock tick counter
+  MIDI_clocknextarp = 0;          //reset next arpeggiator note tick
+#ifdef __HAS_ARPEGGIATOR__
+  if (Arp_Get_Type()!=0) {        //if arpeggiator on
+    Arp_Reset();                  //reset it (so synced with clock)
+  }
+#endif // __HAS_ARPEGGIATOR__
+  LFO_Reset();                    //reset lfo (so synced with clock)
+}
+
+
+//No implementation of clock continue at present.  just does clock start
+void MIDI_ClockContinue(void)
+{
+   MIDI_ClockStart();
+}
+
+
+//MIDI clock stop received
+void MIDI_ClockStop(void)
+{
+  MIDI_clockpresent = false;       //set clock not present
+}
+
+
+//MIDI clock tick received
+void MIDI_ClockTick(void)
+{
+  if (MIDI_clockpresent==true) {                                  //check clock has acutally been started (not rogue data)
+#ifdef __HAS_ARPEGGIATOR__
+    if (MIDI_clocktick>=MIDI_clocknextarp && Arp_Get_Type()!=0){  //if midi clock counter > midi clock value for next arppeggiator trigger (and arpeggitor on) :
+      arp_triggernext = true;                                     //trigger arpeggiator at end of current midi receive (bottom of MIDI_poll)
+    }
+#endif // __HAS_ARPEGGIATOR__
+    if (MIDI_clocktick%24 == 0){                                  //every 24 clock ticks (every beat):
+      Hardware_LED_StartFlash(4,1);                               //flash the midi led
+    }
+    MIDI_clocktick++;                                             //increment midi clock counter
+  }
+  
+}
+
+
+//get midi clock counter
+unsigned long MIDI_Get_ClockTick(void)
+{
+  return MIDI_clocktick;
+}
+
+
+//get pitch bend  (only using msb of pitch bend data)
+char MIDI_Get_PitchBend_Level(void)
+{
+  return MIDI_pbend_level;
+}
+
+
+//***********SYSEX stuff***********
+//byte of sysex data received
+void MIDI_SYSEX_read(byte databyte)
+{
+  //int addr;
+  int samp;
+
+  switch (SYSEXbytenum){                                          //SYSEXbytenum - counter used to log position in sysex message
+    case 1:                                                       //Manufactuer ID 125 (one day I'll get my own!)
+      if (databyte!=125){
+        gettingSYSEX = false;
+        Hardware_LED_SetState(4,LOW);
+      }
+      break;
+    case 2:                                                       //Product ID   (0 for Atmegatron)
+      if (databyte!=0){
+        gettingSYSEX = false;
+        Hardware_LED_SetState(4,LOW);
+      }
+      break;
+    case 3:                                                      //Message type ID (0 = patch, 1 = waveform, 2 = mem dump)
+      SYSEXmesstype = databyte;
+      break;
+    default:
+      if (SYSEXmesstype==SYSEX_PATCH && MIDI_SYSEXread==true){   //********Patch********  
+        if (SYSEXbytenum>=4 && SYSEXbytenum<=19){                //set relevent value (decided by SYSEXbytenum (position in message))
+          Hardware_Let_Value(SYSEXbytenum-4,databyte);
+        }
+        if (SYSEXbytenum>=20 && SYSEXbytenum<=25){
+          Hardware_Let_Ctrl(0,SYSEXbytenum-20,databyte<<1);      //values that are byte (0-255) are *2, because sysex max value is 127
+        }      
+        if (SYSEXbytenum>=26 && SYSEXbytenum<=29){
+          Hardware_Let_Ctrl(1,SYSEXbytenum-24,databyte<<1);
+        }
+        if (SYSEXbytenum==30){
+          Hard_Let_Shift(FUNC_WAVE,databyte);
+        }
+        if (SYSEXbytenum==31){
+          Hard_Let_Shift(FUNC_FILT,databyte);
+        }
+        if (SYSEXbytenum==32){
+          Hard_Let_Shift(FUNC_FENVA,databyte);
+        }
+        if (SYSEXbytenum==33){
+          Hard_Let_Shift(FUNC_LFOTYPE,databyte);
+        }
+        if (SYSEXbytenum==34){
+#ifdef __HAS_ARPEGGIATOR__
+          Hard_Let_Shift(FUNC_ARPTYPE,databyte);
+#endif // __HAS_ARPEGGIATOR__
+        }
+        if (SYSEXbytenum==35){
+          Hard_Let_Shift(FUNC_PORTA,databyte);
+        }
+        if (SYSEXbytenum==36){                                   //this is the last byte
+          Hard_Let_Shift(FUNC_BITCRUSH, databyte);
+          Hardware_LED_StartFlash(0,5);                          //Flash function knob led.  this means all bytes received (different to end of sysex message (F7))
+          if(memDumping==true){                                  //****if this is part of a mem dump receive:****
+            Serial.flush();                                      //flush serial buffer - need all the mem clear for next message
+            Memory_Save(curMemDump);                             //save current received patch to flash mem
+            curMemDump++;                                        //increment mem dump counter (patch num to request)
+            Serial.write(SYSEXBEGIN);                            //0 begin sysex message
+            Serial.write(125);                                   //1 manufacturer ID
+            Serial.write(0);                                     //2 product ID
+            if (curMemDump<16){                                  //first 16 dumps are patches, if more patches to recieve:  request next patch
+              Serial.write(SYSEX_CALLPATCH);                     //3 message type
+            }
+            else{                                                //or request first user wave
+              Serial.write(SYSEX_CALLWAVE);                      //3 message type
+              curMemDump = 0;                                    //reset counter for user waves
+            }
+            Serial.write(curMemDump);                            //4 mem dump number (so librarian knows which patch/wave)
+            Serial.write(SYSEXEND);                              //end message
+          }
+        }
+      }    
+      
+      if (SYSEXmesstype==SYSEX_WAVE && MIDI_SYSEXread==true){    //***********User Wave**************
+        if (SYSEXbytenum>=4 && SYSEXbytenum<=35){                //bytes of sample data
+          samp = (int)databyte;                                  //convert to integer
+          samp *= 2;                                             //*2 (librarian outputs (-63 - 63), we need (-127 - 128, actually use -126 - 126)
+          samp -= 126;                                           //apply offset (byte transmitted as positive integers)        
+          Wave_Let_UserWave(SYSEXbytenum-4,(signed char)samp);          //convert to char and set current user wave
+        }
+        if (SYSEXbytenum==35){                                   //last byte of data
+          Hardware_LED_StartFlash(0,5);                          //Flash function knob led.  this means all bytes received (different to end of sysex message (F7))
+          if(memDumping==true){                                  //****if this is part of a mem dump receive:****
+            Serial.flush();                                      //flush serial buffer - need all the mem clear for next message
+            Memory_UserWave_Write(curMemDump);                   //save current received user wave to flash mem
+            curMemDump++;                                        //increment mem dump counter (user wave to request)
+            if (curMemDump<6){                                   //if more user waves still to receive:  request next user wave
+              Serial.write(SYSEXBEGIN);                          //0 begin message
+              Serial.write(125);                                 //1 manufacturer ID
+              Serial.write(0);                                   //2 product ID
+              Serial.write(SYSEX_CALLWAVE);                      //3 message type
+              Serial.write(curMemDump);                          //4 mem dump number (so librarian knows which patch/wave)
+              Serial.write(SYSEXEND);                            //end message
+            }
+            else{                                                //finished final user wave
+              memDumping = false;                                //stop dumping
+            }
+          }
+        }
+      }
+      
+      if (SYSEXmesstype==SYSEX_MEM && MIDI_SYSEXread==true){     //*************Memory Dump**************
+        curMemDump = 0;                                          //counter used to track which patch/wave is being dumped
+        memDumping = true;                                       //memory dump started
+        Serial.write(SYSEXBEGIN);                                //request first patch from librarian
+        Serial.write(125);                                       //1 manufacturer ID
+        Serial.write(0);                                         //2 product ID
+        Serial.write(SYSEX_CALLPATCH);                           //3 message type
+        Serial.write(curMemDump);                                //4 mem dump number (patch 0)
+        Serial.write(SYSEXEND);                                  //end message        
+      }
+  }
+}
+
+
+//Write patch to librarian
+void MIDI_SYSEX_write_patch(void)
+{
+  byte data;
+  byte i;
+  //boolean b;
+  
+  Serial.write(SYSEXBEGIN);
+  Serial.write(125);                          //  1  manufacturer ID
+  Serial.write(0);                            //  2  product ID
+  Serial.write(SYSEX_PATCH);                  //  3  message type
+  for (i=0;i<16;i++){                         //  4-19  function vals
+    Serial.write(Hardware_Get_Value(i));
+  }
+  for (i=0;i<6;i++){                          //  20-25  ctrl vals bank=0  
+    data =  Hardware_Get_Ctrl(0, i) >> 1;
+    Serial.write(data); 
+  }
+  for (i=2;i<6;i++){                          //  26-29  ctrl vals bank=1
+    data =  Hardware_Get_Ctrl(1, i) >> 1;
+    Serial.write(data); 
+  }
+  Serial.write(Hard_Get_Shift(FUNC_WAVE));    //30-36 shift mode for functions
+  Serial.write(Hard_Get_Shift(FUNC_FILT));
+  Serial.write(Hard_Get_Shift(FUNC_FENVA));
+  Serial.write(Hard_Get_Shift(FUNC_LFOTYPE));
+#ifdef __HAS_ARPEGGIATOR__
+  Serial.write(Hard_Get_Shift(FUNC_ARPTYPE));
+#else
+  Serial.write(0);
+#endif // __HAS_ARPEGGIATOR__
+  Serial.write(Hard_Get_Shift(FUNC_PORTA));
+  Serial.write(Hard_Get_Shift(FUNC_BITCRUSH));
+  Serial.write(SYSEXEND);                     //37 end message
+
+}
+
+
+//**************MIDI POLL ********************* 
+void MIDI_Poll(void)
+{
+  byte incomingByte=0;   
+  static byte notebyte=0;        //these are all static because there's a small chance that the subroutine is exited before the 2 bytes of MIDI needed for note_on are received.
+  static byte velocitybyte=0;
+  static byte statusbuffer=0;
+  static boolean firstbyte;
+  if (Serial.available() > 0) {                    //if there is something in the serial input buffer
+    do {
+      incomingByte = Serial.read();                // read the incoming byte:
+      if (incomingByte>247) {                      // ****this is where MIDI clock stuff is done***
+        switch (incomingByte){
+          case 248:                                //MIDI clock tick
+            MIDI_ClockTick();
+            break;
+          case 250:                                //MIDI clock start
+            MIDI_ClockStart();
+            break;
+          case 251:                                //MIDI clock continue (not implemented)
+            MIDI_ClockContinue();
+            break;
+          case 252:                                //MIDI clock stop
+            MIDI_ClockStop();
+            break;
+        }
+      }
+      else if (incomingByte>239) {                 //*****this is where SYSEX stuff is done*****
+        statusbuffer = 0;
+        if (incomingByte==SYSEXBEGIN){             //start of sysex message
+          gettingSYSEX = true;
+          SYSEXbytenum = 0;                        //reset message byte counter
+          Hardware_LED_SetState(4,HIGH);           //turn midi light on while receiving
+        }
+        if (incomingByte==SYSEXEND){               //end of sysex message
+          gettingSYSEX = false; 
+          Hardware_LED_SetState(4,LOW);            //turn midi light off (this doesn't guarantee that message was successfully read though!  Func knob flashes for that)
+        }
+      }
+      else if (incomingByte>127) {                 //*****this is where all other message types are done ******
+        statusbuffer = incomingByte;               //status buffer stores what kind of buffer it is
+        firstbyte = true;                          //it must be the first byte of pair
+        notebyte = 0;                              //so reset the note byte
+        velocitybyte = 0;                          //and velocity byte, ready incase it's a note
+        gettingSYSEX = false;                      //safety - note a sysex message
+      }
+      else if (gettingSYSEX==true){                //****SYSEX data*******  if getting sysex, it HAS to be sysex data if  < 128
+        SYSEXbytenum++;                            //inc message byte counter
+        MIDI_SYSEX_read(incomingByte);             //process the incoming byte
+      }
+      else if (statusbuffer!=0) {                  //******all other message data*******
+        if (firstbyte == true) {                   //if it's first byte of pair
+          notebyte = incomingByte;                 //set note = incoming byte
+          firstbyte = false;                       //and set = false, so ready for second byte
+        }
+        else {                                     // so must be second byte of pair then
+          velocitybyte = incomingByte;                                                            //set velocitybyte as incoming byte (even if not note data)
+          if (statusbuffer == (NOTE_ON | MIDI_channel) && velocitybyte != 0) {                                     //is it a note on?
+            Hardware_LED_StartFlash(4,1);                                                         //flash midi led
+            MIDI_NoteOn(notebyte);                                                                //trigger note
+          }
+          else if (statusbuffer == (NOTE_OFF | MIDI_channel) || (statusbuffer == (NOTE_ON | MIDI_channel) && velocitybyte == 0)) {  //is it a note off?  (there's 2 ways to trigger note off:  note_off or Note_on with velocity =0)
+            Hardware_LED_StartFlash(4,1);                                                         //flash midi led
+            MIDI_NoteOff(notebyte);                                                               //release note
+          }
+          else if (statusbuffer == (PITCH_WHEEL | MIDI_channel)){                                                  //is it pitch wheel?
+            MIDI_pbend_level = velocitybyte - 64;                                                 //set pbend (may aswell do the offset here).  only msb of pitch bend used
+          }
+          else if (statusbuffer == (CONTROLLER | MIDI_channel)){                                                   //is it a controller message?  I have attempted to use controller numbers appoximating the spec of official controller list.  Used "MISC" controllers where necessary
+            switch (notebyte) {
+              case CC_PITCHLFO:                                                                   //pitch lfo (mod wheel).  This is just an override of the pitch lfo knob
+                Hardware_Let_Ctrl(1,CTRL_LFO,velocitybyte >> 1);                                  //default uses >>1 for typical mod wheel vibrato.  use  << 1 for full range (default full range = 1 octave)
+                break;
+              case CC_PORTAMENTO:                                                                 //portamento
+                Hardware_Let_Value(FUNC_PORTA,velocitybyte >> 3);                                 //scale to values 0-15
+                break;
+              case CC_FILTERENV:                                                                  //filter envelope
+                Hardware_Let_Ctrl(0,CTRL_ENV,velocitybyte << 1);                                  //scale to values 0-255
+                break;
+              case CC_DISTORTION:                                                                 //distortion
+                Hardware_Let_Ctrl(0,CTRL_FX,velocitybyte << 1);                                   //scale to values 0-255
+                break;
+              case CC_FILTCUTOFF:                                                                 //filter cutoff frequency
+                Hardware_Let_Ctrl(0,CTRL_FILT,velocitybyte << 1);                                 //scale to values 0-255
+                break;
+              case CC_AMPENVR:                                                                    //amplitude envelope release
+                Hardware_Let_Value(FUNC_AENVR,velocitybyte >> 3);                                 //scale to values 0-15
+                break;
+              case CC_AMPENVA:                                                                    //amplitude envelope attack
+                Hardware_Let_Value(FUNC_AENVA,velocitybyte >> 3);                                 //scale to values 0-15
+                break;
+              case CC_FILTRES:                                                                    //filter resonance
+                Hardware_Let_Ctrl(0,CTRL_Q,velocitybyte << 1);                                    //scale to values 0-255
+                break;
+              case CC_AMPENVD:                                                                    //amplitude envelope decay
+                Hardware_Let_Value(FUNC_AENVD,velocitybyte >> 3);                                 //scale to values 0-15
+                break;
+              case CC_LFOCLOCKDIV:                                                                //lfo rate (speed).    ******DUBSTEP PRODUCERS THIS IS CONTROLLER 79!!!********
+                Hardware_Let_Value(FUNC_LFOSPEED,velocitybyte >> 3);                              //scale to values 0-15 
+                break;
+              case CC_PWM:                                                                        //pulse width modulation
+                Hardware_Let_Ctrl(1,CTRL_AMP,velocitybyte << 1);                                  //scale to values (0-255)
+                break;
+              case CC_AMPLFO:                                                                     //amplitude lfo
+                Hardware_Let_Ctrl(0,CTRL_AMP,velocitybyte << 1);                                  //scale to values (0-255)
+                break;
+              case CC_FILTLFO:                                                                    //filter lfo
+                Hardware_Let_Ctrl(0,CTRL_LFO,velocitybyte << 1);
+                break;
+              case CC_PITCHENV:                                                                   //pitch envelope
+                Hardware_Let_Ctrl(1,CTRL_ENV,velocitybyte << 1);
+                break;
+              case CC_FLANGE:                                                                     //phaser
+                Hardware_Let_Ctrl(1,CTRL_FX,velocitybyte << 1);
+                break;
+            }
+          }                                                                       
+          notebyte = 0;                                                                           //now reset them ready for next note
+          velocitybyte = 0; 
+          firstbyte = true;         
+        }
+      }
+    } while (Serial.available() > 0);                                                             //loop until serial buffer is empty
+
+#ifdef __HAS_ARPEGGIATOR__
+    if (arp_triggernext==true && MIDI_totnoteson>0){                                              //midi clock tick was received and it's time to fire an arpeggiator note!
+      do{                                                                                         //nasty  do loop.  this is important, incase midi clock ticks are faster than Atmegatron polling loop and multiple notes need to be triggered now
+        Arp_TriggerStep();                                                                        //trigger arpeggiator
+        MIDI_clocknextarp += MIDI_clockticksperarpstep;                                           //MIDI_clockticksperarpstep  MUST never be 0
+      } while (MIDI_clocknextarp<MIDI_clocktick);                                                 //loop until all notes triggered that need to be are
+      arp_triggernext = false;                                                                    //triggered all notes, so set false
+    }
+#endif // __HAS_ARPEGGIATOR__
+  } 
+}
+