MCV876 Controller Update: Source Code
Übersetzen
Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden. Im Studio ein neues, leeres Projekt mit Standard-Einstellungen, einem sinnvollen Namen und an einem Ort, wo man es wiederfindet erstellen, Quelltext in das (automatisch erzeugte, leere) C-File kopieren, auf Build->Build klicken, fertig. Das resultierende HEX File kann man mit einem üblichen Programmieradapter in der Chip flashen, für uns zweckmäßiger ist allerdings, das in ein Sys-Ex File zu Konvertieren [->Link auf Konverter folgt...], und das an den Controller zu schicken. Dann programmiert der sich selber um. Dazu muss er allerdings einmalig mit dem Bootlader [hier Link auf Bootlader imaginieren.. ] programmiert worden sein.
Zu den Atmel Controllern findet man eine Menge Info mit Tante Google, http://www.mikrocontroller.net kommt häufig vor, und hat ein ergiebiges Forum und gute Artikel. Auf der Atmel Webseite gibt es auch einige Info, das ATMega 88 Datenblatt ist spätestens dann nötig, wenn es um die Hardware geht.
Quelltext
/*
MCV876 with ATMega 88
Midi to 4 Channel Control Voltage and six Gates Interface
Preliminary
Version 0.20 2011-06-22
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <avr/io.h> // yes, we want to talk to the port pins by name
#include <avr/eeprom.h> // ... oh.. and some eeprom access would be nice
#include <avr/interrupt.h> // ... and w/o the interupts it wouldn't do anything...
/* ===================ZERO LINE=================================
WARNING: everthing below the zero line counts from zero!
e.g. whats Gate 6 in manual is GATE5 here, CVs are CV0 to 3
*/
/*
where should the LED output be
*/
#define LEDLED 6 // LED at Reset-Pin, standard for series, but no debugging possible.
#define LEDGATE0 0 // Led at Gate 0, no LED (Test I: no LED )
#define LEDGATE4 4 // LED at Gate 5, Test II: no Gate 5 Function (Midi Start/Stop)
#define LEDOUT LEDGATE4 // select your evil...
#define BUTTON (!(PIND & (1<<4))) // True if Button pressed
#define NUM_CV 4
#define PARAMETEROFFSET 0x23 // First paramter has adress 0x23
#define NUM_PARAMTERS 17 // and there are 17 parameters
struct CONFIG {
unsigned char midiChannel;
unsigned char noteOffset;
unsigned char cvMidiCC[NUM_CV];
unsigned char gateMidiCC[NUM_CV];
unsigned char gateTreshhold;
unsigned char clockDivider;
unsigned char clockTime;
unsigned char ifMode0; // bit 0 ... bit 3 = CV0 .. CV3, 0=Pitch, 1=CC (if pitch, CC assignement is ignored)
// bit 5: voices: 0=2; 1=4
// bit 7:6= 00 Mono; 01:Multi-Channel; 10: Poly (not supported);
unsigned char ifMode1; // Gate Trigger Mode Bits (0=Re-Trigger, 1=TriggerControl)
unsigned char ifMode2; // bit0: Velo0=>CV0; bit1:Velo0=>CV1; bit2:Velo-Poly2=>CV2; bit3:Velo1=>CV3
// bit4: PB=>CV0; bit5:PB=>CV1; bit6:PB=>CV2; bit7:PB=>CV3
unsigned char ifMode3; // bit0..bit3 = CV0... CV3; 0=10V, 1=5V Mode
// internal evaluated configuration
unsigned char numMidiChannels;
} shadow_config, // her goes a copy, when config should be written, because that might take a while
config // current configuration, mostly direct evaluated troughout the program
= { // default values (Mono-Mode, Midi-Ch 1, CV1=Pitch, CV2=Velocity CV3=ModWheel CV4=Pitch Bend
0, // Midi Channel
24, // Note Offset (2 Oktaves)
{ 255,255,1,255, }, // CV - assigned Midi Controllers (used if not pitch output) 255=none
{ 255,18,19,20, }, // Gate - asigned Controllers (if not pitch) 255=none
63, // Treshold
6, // Divider 6 (1/16 Note)
1, // clock time (unused)
0x0e, // mode 1 Low nibble CC-Bits, high: interface mode
0x82, // mode 2 High Nibble: PB-bits, Low: Velo-Bits
0, // mode 3 (5V-Bits)
};
#define EE_MAGIC 17
unsigned char ee_magic EEMEM;
unsigned char config_eeprom[NUM_PARAMTERS] EEMEM; // Config in EEPROM, saves it during power down.
#define MODE_MONO 0x00 // monophon, all assigned to last note on a single channel
#define MODE_MULTI2 0x40 // two channel, 1/2 for midiChannel, 2/3 for midiChannel+1
#define MODE_MULTI4 0x60 // for channel midiChannel to midiChannel+3
#define MODE_POLY2 0x80 // not supported
#define MODE_POLY4 0xA0 // not supported
// to have the clock synced to "beat 1" of a bar ...
volatile unsigned int spp; // song position pointer (from midi message)
unsigned char ticksPerBar=96; // How many ticks per bar (is not tx-ed: guess 96 for 4/4 ... )
volatile unsigned char barTick; // where are we inside the bar (counted, because calculation from spp takes some cpu-cycles )
volatile unsigned char eepromWriteFlag; // set to one, if main-loop should write eeprom
volatile unsigned char button; // debounced button status
volatile unsigned char learn; // (channel/base note/cc) "do learn" status
volatile unsigned char blinker; // some count for blinkenlights
volatile unsigned char clocker; // timer counter for half a midi-clock (which is needed for divider 1)
volatile unsigned char gate5off; // timer counter for when clock gate output should go low again.
volatile unsigned char watchTicker; // midi active sense counter
void setConfig(void);
void setLed(unsigned char on);
void resetAll(void);
/*
main()
Main does initialization, than runs a loop, which does nearly nothing:
Programming the EEPROM (because its slow)
Blinking
Button evaluation
All the important things are done by the interupts. (Thanks to the
20MHZ RISC power everything can be done in less than 320µs=one Midi-Character)
*/
int main(void)
{
unsigned char blink=6;
unsigned char i,x;
unsigned char *cp;
static unsigned char lastButton;
/*
HARDWARE INIT
*/
// Input/Output Ports initialization
// DDRx = Define I/O direction for each pins
// PORTx = If pin is Output then `0' = lo, `1' = hi
// If pin is input then `0' = no pullup, `1' = pullup enabled
// `0' = Input `1' = Output
// Port B initialization
DDRB = 0x3f; // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)
PORTB = 0x04; // WR1 High, Rest Low
// Port C initialization
DDRC = 0x7F; // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)
PORTC = 0x00; // all Low
// Port D initialization
DDRD = 0xee; // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3
PORTD = 0x1F; // Pullup on RX & Button, all WR High, Gates Low
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 20Mhz /64
// Mode: CTC
// OC0 output: Disconnected
OCR1A= 625; // 2 ms
TCCR1A =0;
TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64
TIMSK1 =2; // OCF1A Interupt
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: OFF
// USART Mode: Asynchronous
UCSR0A = 0x00;
/* Set frame format: 8data, 1stop bit */
UCSR0C = 6;
// USART Baud rate: 31250; 20E6/16/31250 - 1
UBRR0H = 0;
UBRR0L = 39;
/*Enable receiver , Enable Rx-int */
UCSR0B = ((1<<RXEN0)|(1<<RXCIE0) ) ;
/*
HARDWARE READY
*/
// Read Configuration
if( eeprom_read_byte(ee_magic) == EE_MAGIC && !BUTTON) // if eeprom valid AND Button not pressed
{
cp=(unsigned char* )&config;
for(i=0;i<NUM_PARAMTERS;++i)
{
x=eeprom_read_byte(config_eeprom+i); // read config from eeprom
*cp=x;
cp++;
}
}
setConfig();
// enable interrupts
sei();
while(1)
{
if(eepromWriteFlag)
{ // save configuration
cp=(unsigned char* )&shadow_config;
for(i=0;i<NUM_PARAMTERS;++i)
{
x=*cp;
eeprom_write_byte(config_eeprom+i,x);
cp++;
}
eeprom_write_byte(ee_magic,EE_MAGIC);
setLed(1);
blink=6;
blinker=0;
eepromWriteFlag=0;
}
if(blink && blinker>2) // blink after power up and after storing in nv-memory
{
blink--;
setLed(blink&1);
blinker=0;
}
if(button != lastButton)
{
if(button==1 )
{
resetAll();
if(learn==1)
{
learn=0;
setLed(0);
}
}
if(button==6)
{
if(learn ==0)
{
learn=1;
setLed(1);
}
}
lastButton=button;
}
}
return 0;
}
/* ***********************************************************************************
Hardware Interface
*/
/* Pin assignment
Atmel Fkt [Orig]
1 Reset LED [MCLR]
2 RX Midi-Rx[WR1]
3 PD1 WR2
4 PD2 WR3
5 PD3 WR4
6 PD4 Button
7 VCC +5 [LED] (Either from Atmel-1(Series) or Atmel-15(w/Debug))
8 GND GND
9 OSC OSC
10 OSC OSC
11 PD5 G1
12 PD6 G2
13 PD7 G3
14 PB0 G4
15 PB1 G5 (Optional + LED)
16 PB2 G6
17 PB3 WR1 [---]
18 PB4 D0 [RX]
19 PB5 D1 [GND]
20 AVCC +5
21 ARef +5 [D0]
22 GND GND [D1]
23 PC0 D2
24 PC1 D3
25 PC2 D4
26 PC3 D5
27 PC4 D6
28 PC5 D7
*/
// put "val" to the right port bits for the DA-Converter
// ... better program than solder *that*...
void adByteOut(unsigned char val)
{
unsigned char b;
b=val;
b>>=2;
PORTC=b;
b=val&3;
b<<=4;
val=PORTB;
val &=0xCF;
val|=b;
PORTB=val;
}
// set DA-Output voltage of one CV Channel
void set_cv(unsigned char val, unsigned char channel)
{
adByteOut(val); // set D0..D7 converter data bits
switch (channel) // pulse with write pin
{
case 0: //PB3
PORTB &= ~(1<<3);
PORTB |= (1<<3);
break;
case 1: // PD1
PORTD &= ~(1<<1);
PORTD |= (1<<1);
break;
case 2: // PD2
PORTD &= ~(1<<2);
PORTD |= (1<<2);
break;
case 3: // PD3
PORTD &= ~(1<<3);
PORTD |= (1<<3);
break;
}
}
// set on of the gate outputs active
void gate_on(unsigned char channel)
{
switch (channel)
{
case 0: // PD5
PORTD |= (1<< 5);
break;
case 1: // PD6
PORTD |= (1<< 6);
break;
case 2: // PD7
PORTD |= (1<< 7);
break;
case 3: // PB0
PORTB |= (1<< 0);
break;
case 4: // PB1
#if LEDOUT != LEDGATE4
PORTB |= (1<< 1);
#endif
break;
case 5: // PB2
PORTB |= (1<< 2);
break;
}
}
// clear one of the gate outputs
void gate_off(unsigned char channel)
{
switch (channel)
{
case 0: // PD5
PORTD &=~(1<< 5);
break;
case 1: // PD6
PORTD &=~(1<< 6);
break;
case 2: // PD7
PORTD &=~(1<< 7);
break;
case 3: // PB0
PORTB &=~(1<< 0);
break;
case 4: // PB1
#if LEDOUT != LEDGATE4
PORTB &=~(1<< 1);
#endif
break;
case 5: // PB2
PORTB &=~(1<< 2);
break;
}
}
/* ***********************************************************************************
LED
*/
void setLed(unsigned char on)
{
#if LEDOUT == LEDLED
if (on) // PC6
PORTC |= (1<< 6);
else
PORTC &=~(1<< 6);
#endif
#if LEDOUT == LEDGATE4
if (on) // PB1
PORTB |= (1<< 1);
else
PORTB &=~(1<< 1);
#endif
}
/* ***********************************************************************************
Data Handling
*/
typedef struct {
unsigned char channel; // (relative) Midi channel (0..3) Mode dependant
unsigned char cvOut; // for which CV-Ouput (0..3) fixed
unsigned char ccVal; // current CC Value
unsigned char note; // current Note
unsigned char velo; // current Velocity
unsigned char on; // if Note is on
signed char pb; // current Pitch Bend Value
unsigned char gateDipper;
} cvoutput;
cvoutput cv_out[NUM_CV]; // current state for each cv out
unsigned char gates; // which gates are on => used for LED setting
// set cv output (and assigned gate, if pitch output)
// called after anything that changes the cv_out structure
void updateCv(cvoutput *cvo)
{
unsigned char bm;
unsigned char gated=0;
int val=0;
bm=1<<cvo->cvOut; // bit mask for configuration flags
if(config.ifMode0 & bm) // 1=CC-Mode
{
val=cvo->ccVal; // <<1;
/*
if( (config.ifMode3 & bm) && cvo->on==0) // 5V-Flag, abused as Gated-Mode-CV Flag
gated=1;
*/
}
else // Note
{
val=cvo->note;
val -= config.noteOffset;
if(config.ifMode3 & bm) // 5V
val <<= 2;
else
val <<= 1;
}
if(config.ifMode2 & bm) // Velocity
val+=cvo->velo;
if(config.ifMode2 & (bm<<4)) // PB
val+=cvo->pb;
if(val<0 || gated) // clip values to converter range
val=0;
if(val>255)
val=255;
set_cv((unsigned char)val, cvo->cvOut);
if((config.ifMode0 & bm)==0)
{
if(cvo->on)
{
if(cvo->gateDipper==0) // no retrigger required
gate_on(cvo->cvOut);
else // dip gate for a few milliseconds
gate_off(cvo->cvOut);
gates |=bm;
}
else
{
gate_off(cvo->cvOut);
gates &=~bm;
}
}
}
// calculate/initialize control variables after
// mode-configuration change
void setConfig()
{
unsigned char i;
cvoutput *cvo;
cvo=cv_out;
for(i=0;i<NUM_CV;++i)
{
cvo->cvOut=i;
cvo->pb=0x80;
cvo++;
}
cvo=cv_out;
switch(config.ifMode0 & 0xf0 )
{
default:
case MODE_MONO:
for(i=0;i<NUM_CV;++i)
{
cvo->channel=0;
cvo++;
}
config.numMidiChannels=1;
break;
case MODE_MULTI2:
for(i=0;i<NUM_CV/2;++i)
{
cvo->channel=0;
cvo++;
}
for(;i<NUM_CV;++i)
{
cvo->channel=1;
cvo++;
}
config.numMidiChannels=2;
break;
case MODE_MULTI4:
for(i=0;i<NUM_CV;++i)
{
cvo->channel=i;
cvo++;
}
config.numMidiChannels=NUM_CV;
break;
}
}
// assign new note state to cv outputs
void setNote(unsigned char note, unsigned char velo, unsigned char onOff, unsigned char channel)
{
unsigned char i;
cvoutput *cvo;
cvo=cv_out;
for(i=0;i<NUM_CV;i++)
{
if(cvo->channel==channel)
{
cvo->note=note;
cvo->velo=velo;
if(onOff==2 && cvo->on && (config.ifMode1 & (1<<i)))
cvo->gateDipper=4;
cvo->on=onOff;
updateCv(cvo);
}
cvo++;
}
setLed(gates);
}
// process new continous controller value
void setCC(unsigned char cc, unsigned char value, unsigned char channel)
{
unsigned char i;
cvoutput *cvo;
cvo=cv_out;
for(i=0;i<NUM_CV;++i)
{
if(channel== cvo->channel )
{
if(config.ifMode0 &(1<<i))
{
if(config.cvMidiCC[i]==cc)
{
cvo->ccVal=value;
updateCv(cvo);
}
if(config.gateMidiCC[i]==cc )
{
if(value>config.gateTreshhold)
gate_on(channel);
else
gate_off(channel);
}
}
}
cvo++;
}
}
// process new pitch bend value
void setPb(signed int value, unsigned char channel)
{
unsigned char i;
cvoutput *cvo;
cvo=cv_out;
for(i=0;i<NUM_CV;++i)
{
if(channel==cvo->channel && (config.ifMode2 & (16<<i)) )
{
cvo->pb =(signed char) (value>>6);
updateCv(cvo);
}
cvo++;
}
}
// prepare for (asynchronous) configuration write to eeprom
void setEeWrite(void)
{
shadow_config=config;
learn=0;
eepromWriteFlag=1;
}
void (*bootloader)(void) = (void*)0x1800; // Bootloader sits at adress 0x1800 ...
// process sys ex command
void setSyx(unsigned char adr, unsigned char val)
{
unsigned char *cp;
cp=(unsigned char *) &config;
if(adr==0x14 && val ==0x7b)
{ // write eeprom
if(eepromWriteFlag==0)
{
setEeWrite();
}
return;
}
// 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7
if(adr==0x16 && val ==0x42) // Software update
{
cli(); // interupts off
resetAll(); // outputs off
GPIOR1 = 0xAB; // tell bootloader it's an application boot request
// (hardware reset sets GPIOR1 to zero, 0xab == magic value for bootloader )
bootloader(); // bye bye!
return; // <-- never!
}
adr-=PARAMETEROFFSET;
if(adr < NUM_PARAMTERS) // if valid parameter
{
*(cp+adr)=val; // set in
if(adr==0x30-PARAMETEROFFSET) // if Mode Byte 0,
setConfig(); // .. cv assignement must be recalculated
}
}
/* ***********************************************************************************
Note Puffer Handling
*/
#define MAX_NOTES 0x10 // one for each finger of a real programmer
#define MAX_CHANNELS 4
unsigned char notes[MAX_CHANNELS][MAX_NOTES];
unsigned char velocities[MAX_CHANNELS][MAX_NOTES];
unsigned char playedNote[MAX_CHANNELS]; // number of currently pressed notes
// update note puffer for a midi note-on message
void note_on(unsigned char note ,unsigned char velocity, unsigned char channel)
{
unsigned char n;
n=playedNote[channel];
if(n<MAX_NOTES)
{
playedNote[channel]++;
}
else
{
for (n=MAX_NOTES-1;n>0;--n)
{
notes[channel][n-1]=notes[channel][n];
velocities[channel][n-1]=velocities[channel][n];
}
n=MAX_NOTES-1;
}
notes[channel][n]=note;
velocities[channel][n]=velocity;
setNote(note, velocity, 2, channel);
}
// update note puffer for a new midi note-off message
void note_off(unsigned char note ,unsigned char velocity, unsigned char channel)
{
unsigned char i;
signed char n;
n=playedNote[channel];
--n;
for (i=0;i<=n;i++)
{
if(notes[channel][i]==note)
{
if(i==n) // last pressed (== the on which playes) note released
{
if(n==0) // it was the only pressed note
{
setNote(note, velocities[channel][n], 0, channel);
playedNote[channel]--;
}
else
{
--n;
setNote(notes[channel][n], velocities[channel][n], 1, channel);
playedNote[channel]--;
}
}
else // non playing note released
{ // => just remove it from list
int j;
for(j=i+1;j<=n;++j)
{
notes[channel][j-1]=notes[channel][j];
velocities[channel][j-1]=velocities[channel][j];
}
playedNote[channel]--;
}
break;
}
}
}
/* ***********************************************************************************
All Notes off, empty Note Buffer, All Controllers Reset
*/
void resetAll(void)
{
unsigned char i;
cvoutput *cvo;
cvo=cv_out;
cli(); // this might be called asynchronous to midi stream
for(i=0;i<MAX_CHANNELS;++i)
playedNote[i]=0;
for(i=0;i<NUM_CV;++i)
{
cvo->cvOut=i;
cvo->ccVal=0;
cvo->on=0;
cvo->pb=0x80;
updateCv(cvo);
cvo++;
}
sei();
setLed(0);
}
/* ***********************************************************************************
2ms Timer Interupt
*/
ISR(TIMER1_COMPA_vect)
{
unsigned char i;
static unsigned char buttonCount;
static signed char blinkCount;
// LED blinker
if(--blinkCount<=0)
{
blinkCount=40;
blinker++;
if(button)
{
if(button<10)
button++;
}
}
// Button debouncer
if(BUTTON)
{ // down
if(buttonCount < 10)
{
buttonCount++;
if(buttonCount==10)
{
button=1;
blinkCount=40;
}
}
}
else
{ // up
if(buttonCount > 0)
{
buttonCount--;
if(buttonCount==0)
button=0;
}
}
// Gate 5 timing (clock output)
clocker++;
if(gate5off)
{
--gate5off;
if(gate5off==0)
gate_off(5);
}
// activ sense
if(watchTicker)
{
watchTicker--;
if(watchTicker==0)
resetAll();
}
// retrigger timing
for(i=0;i<NUM_CV;i++)
{
if(cv_out[i].gateDipper)
{
--cv_out[i].gateDipper;
if(cv_out[i].gateDipper==0)
gate_on(i);
}
}
}
/* ***********************************************************************************
Eval Midi Data
*/
#define NOTE_ON 0x90 // Midi note on message
#define NOTE_OFF 0x80 // Midi note off message
#define PITCH 0xE0 // pitch bender
#define AFTERTOUCH 0xD0 // after touch command (unused)
#define CC 0xB0 // Continuous Controller
#define CHAN_PRES 0xD0 // Channel Pressure (unused)
#define SONG_POSITION_POINTER 0xF2 // used for clock synchronization (first beat)
#define SYSEX 0xF0 // System exclusive beginn
#define SYSEND 0xF7 // System exclusive end
struct {
unsigned char runningStatus; // Midi runnig status (*)
unsigned char msgByteCnt; // number of current midi-message byte received
unsigned char byte1; // first data byte
unsigned char syxAdr; // Sys-Ex Adress Byte
unsigned char startStop; // current Midi Start/Stop Status
unsigned char startFlag; // Start on next clock
unsigned char clockTick; // counter for clock division
unsigned char channel; // channel of current midi-command (relative, e.g. 0..3 )
unsigned char syncStartFlag; // start on next 1
}midi;
// (*) running status is also used as 'current status' - it remembers allways, what next data byte is for. Or its zero, than data is ignored.
#define FRAMING_ERROR (1<<FE0)
#define PARITY_ERROR (1<<UPE0)
#define DATA_OVERRUN (1<<DOR0)
// USART Receiver interrupt service routine,
// complete midi processing is done here, as the CPU is fast enough to
// do everything before next midi byte can come in.
ISR(USART_RX_vect)
{
unsigned char status,byte;
status = UCSR0A;
byte = UDR0;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )
{
if(watchTicker) // if there had been an active sense
watchTicker=160; // every byte is ok, to see that cable is still plugged in
if (byte > 0x7F) // if command byte (MSB set)
{
if(byte>= 0xF8) // if real-time msg
{
switch (byte)
{
case 0xF8: // Clock
{
if(midi.startFlag) // synchronized start
{
midi.startStop=1;
midi.startFlag=0;
midi.syncStartFlag=1;
midi.clockTick=0;
gate_on(4);
}
else if(midi.startStop) // running
{
++spp;
++barTick;
if(barTick >= ticksPerBar)
barTick=0;
}
++midi.clockTick;
if(midi.clockTick >= config.clockDivider) // clock output
{
midi.clockTick=0;
gate_on(5);
gate5off=clocker>>1; // pulse-width == half of a midi clock
}
if(midi.syncStartFlag && barTick==0 && midi.startStop ) // synchronize clock out to bars
{
midi.clockTick=0;
gate_on(5);
gate5off=clocker>>1;
midi.syncStartFlag=0;
}
clocker=0; // restart midi clock pulse width counter.
break;
}
case 0xF9: // Tick
break;
case 0xFA: // Start
spp=0;
barTick=0;
midi.startFlag=1;
break;
case 0xFB: // Continue
midi.startFlag=1;
break;
case 0xFC: // Stop
midi.startStop=0;
gate_off(4);
break;
case 0xFD: // unassigned
break;
case 0xFE: // Active Sense
watchTicker=160;
break;
case 0xFF: // Reset
break;
}
return ;
}
if(byte >= 0xF0) // if System Common Category message
{
if(byte == SYSEX)
{
midi.runningStatus=byte;
midi.msgByteCnt = 0;
return;
}
else if(byte==SYSEND)
{
if(midi.runningStatus==SYSEX && midi.msgByteCnt==6)
setSyx(midi.syxAdr, midi.byte1);
midi.msgByteCnt = 0;
}
else if(byte ==SONG_POSITION_POINTER)
{
midi.runningStatus=byte;
midi.msgByteCnt = 0;
return ;
}
midi.runningStatus = 0;
return ;
}
midi.runningStatus = 0; // just new status messages left, so running status is lost
// only channel messages left here ...
if(learn)
{
if(config.midiChannel != (byte&0xf)) // if message channel not current channel
{
config.midiChannel=byte&0xf; // assign channel
setEeWrite(); // and store it
}
}
midi.channel=(byte & 0xf)-config.midiChannel ;
if ( midi.channel<config.numMidiChannels) // if MIDI channel matches,
{
midi.runningStatus = byte & 0xF0 ; // set running status (==Midi command w/o channel)
midi.msgByteCnt = 0;
return ;
}
return ;
}
else
{
switch (midi.runningStatus) // last command byte (or zero, if data is not for us)
{
case SONG_POSITION_POINTER:
if(midi.msgByteCnt) // 2nd Byte
{
spp= (((int)byte << 7 ) | midi.byte1) * 6;
barTick = spp % (unsigned int) ticksPerBar;
midi.runningStatus=0;
}
else
{
midi.byte1 = byte;
midi.msgByteCnt=1;
}
break;
case (NOTE_ON): // if we've had a note on byte
if (midi.msgByteCnt) // if counter = 1
{ // then we've just received a velocity byte
midi.msgByteCnt = 0; // msg complete - clear byte count
if (byte == 0) // is it a note off (velo = 0) ?
note_off(midi.byte1,byte,midi.channel);
else // NOTE ON
{
if(learn)
{
config.noteOffset=midi.byte1;
setEeWrite();
}
note_on(midi.byte1, byte,midi.channel); // ... and do what the note on has told us
}
}
else // first data byte
{
midi.byte1 = byte; // save it
midi.msgByteCnt = 1; // set counter to get 2nd data byte
}
break;
case (NOTE_OFF): // if we've had a note off byte
if (midi.msgByteCnt) // is second data byte ?
{
midi.msgByteCnt = 0; // clear byte count
note_off(midi.byte1,byte,midi.channel); // switch note off
}
else
{
midi.byte1 = byte;
midi.msgByteCnt = 1; // set counter to get 2nd byte
}
break;
case (PITCH): // PITCH BEND command
if (midi.msgByteCnt) // second data byte (MSB)
{
int v;
v=byte;
v<<=6;
v|=midi.byte1;
v-=0x2000;
setPb(v,midi.channel);
midi.msgByteCnt = 0; // clear byte count
}
else
{
midi.byte1 = byte;
midi.msgByteCnt = 1; // set counter to get next byte
return;
}
break;
case (CC): // Continuous Controller Message
if (midi.msgByteCnt) // if we've had first byte
{
midi.msgByteCnt = 0; // clear byte count
if(learn)
{
config.cvMidiCC[3]=midi.byte1;
setEeWrite();
}
setCC(midi.byte1, byte,midi.channel);
}
else
{
midi.byte1 = byte;
midi.msgByteCnt = 1; // set counter ready to get 3rd byte
return;
}
break;
// case (CHAN_PRES): // if channel pressure command
// channel_pressure (byte); // set Aftertouch
case SYSEX:
midi.msgByteCnt++;
switch(midi.msgByteCnt)
{
case 1: // Manufactorer
if(byte==0x70)
;
else
midi.msgByteCnt=0;
break;
case 2: // Unit - ID
if(byte==0x7D)
;
else
midi.msgByteCnt=0;
break;
case 3: // Midi-Adress (ignored!)
if(byte<0x10)
;
else
midi.msgByteCnt=0;
break;
case 4: // adress byte
midi.syxAdr=byte;
break;
case 5: // high data nibble
if(byte<0x10)
{
midi.byte1=(byte<<4);
}
else
midi.msgByteCnt=0;
break;
case 6: // low data nibble
if(byte<0x10)
{
midi.byte1 |= byte;
}
else
midi.msgByteCnt=0;
break;
default: // not our framing
midi.msgByteCnt=0;
break;
}
if(midi.msgByteCnt==0) // something did not fit
midi.runningStatus=0;
break;
case 0: // bnwtko (bytes no one wants to know off )
return ;
default:
break;
}
}
}
}