This is a part of the Kinetiszer project.
midi.c
- Committer:
- Clemo
- Date:
- 2014-10-28
- Revision:
- 1:8ae4ab73ca6a
- Parent:
- 0:cb80470434eb
File content as of revision 1:8ae4ab73ca6a:
/* 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__ } }