// Arduino YM2149 MIDI Synth // by yukimizake // video: https://youtu.be/hUPs2pv5d_g // schematics: http://electronicfields.wordpress.com/?attachment_id=128 (Vcc/GND not shown) // New PCB and re-jig for 2022 by Obakegaku and Dan Suter. // Code re-factored in May 2022 as it would no longer conpile in the latest version of Arduino IDE // contact webmidi@dansfing.uk https://dansfing.uk for help. // 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 . #include #include //Port settings const int ad0 = 8; const int ad1 = 9; const int ad2 = 2; const int ad3 = 3; const int ad4 = 4; const int ad5 = 5; const int ad6 = 6; const int ad7 = 7; const int pinBC1 = 10; const int pinBDIR = 11; //BC2 to +5V const int pinYMReset = 12; //voicing byte noteA = 0; byte noteB = 0; byte noteC = 0; int periodA = 0; int periodB = 0; int periodC = 0; //envelope byte AmaxVolume = 0; byte BmaxVolume = 0; byte CmaxVolume = 0; //unison detune int detuneValue = 1; //arpeggio settings byte arpeggio[] = {0,7,12}; byte arpeggioLength = 3; byte arpeggioCounter = 0; boolean arpeggioFlipMe = false; byte defaultLevel = 10; //Fast pin switching macros #define CLR(x,y) (x&=(~(1< 0) { periodB = tp[noteB + arpeggio[arpeggioCounter]]; byte LSB = ( periodB & 0x00FF); byte MSB = ((periodB >> 8) & 0x000F); send_data(0x02, LSB); send_data(0x03, MSB); arpeggioCounter++; if (arpeggioCounter == arpeggioLength) arpeggioCounter = 0; } } } void setup(){ //init pins pinMode(ad0, OUTPUT); pinMode(ad1, OUTPUT); pinMode(ad2, OUTPUT); pinMode(ad3, OUTPUT); pinMode(ad4, OUTPUT); pinMode(ad5, OUTPUT); pinMode(ad6, OUTPUT); pinMode(ad7, OUTPUT); pinMode(pinBC1, OUTPUT); pinMode(pinBDIR, OUTPUT); pinMode(pinYMReset, OUTPUT); pinMode(ledPin, OUTPUT); __RGBLEDDDR__ |= ( 1 << __RLED__ | 1 << __GLED__ | 1 << __BLED__); //led pins as output resetYM(); AmaxVolume = defaultLevel; BmaxVolume = defaultLevel; CmaxVolume = defaultLevel; //serial init Serial.begin(31250); //timer1 : sample player cli(); TCCR1A = 0; //timer reset TCCR1B = 0; //timer reset OCR1A = 1450; //period for 11025 kHz at 16Mhz TCCR1B |= (1 << WGM12); //CTC mode TCCR1B |= (1 << CS10); // timer ticks = clock ticks TIMSK1 |= (1 << OCIE1A); // enable compare sei(); //say hello playNote(60, 127, 4); delay(40); playNote(64, 127, 4); delay(40); playNote(67, 127, 4); delay(40); playNote(72, 127, 4); delay(40); stopNote(72, 4); } void loop() { byte command = getSerialByte(); byte commandMSB = command & 0xF0; byte midiChannel = command & 0x0F; if (commandMSB == 0x80) //Note off { byte note = getSerialByte(); getSerialByte(); //discard 3rd byte stopNote(note, midiChannel); } else if (commandMSB == 0x90) //Note on { byte note = getSerialByte(); byte velo = getSerialByte(); if (velo != 0 && midiChannel == 0x09) playDigidrum(note, velo); else if (velo != 0) playNote(note, velo, midiChannel); else if (velo == 0) stopNote(note, midiChannel); } else if (commandMSB == 0xA0) // Key pressure { getSerialByte(); getSerialByte(); } else if (commandMSB == 0xB0) // Control change { byte controller = getSerialByte(); byte value = getSerialByte(); if (controller == 0x01) setDetune(value); if (controller == 0x07) setChannelVolume(value, midiChannel); } else if (commandMSB == 0xC0) // Program change { byte program = getSerialByte(); } else if (commandMSB == 0xD0) // Channel pressure { byte pressure = getSerialByte(); } else if (commandMSB == 0xE0) // Pitch bend { byte pitchBendLSB = getSerialByte(); byte pitchBendMSB = getSerialByte(); } } void setDetune(byte value) { detuneValue = (value >> 3) & 0x0F; //downscaling to 4 bits } void setChannelVolume(byte value, byte chan) { value = (value >> 3) & 0x0F; //downscaling to 4 bits if (chan == 0) { AmaxVolume = value; send_data(0x08, value); } else if (chan == 1) { BmaxVolume = value; send_data(0x09, value); } else if (chan == 2) { CmaxVolume = value; send_data(0x0A, value); } } void playNote(byte note, byte velo, byte chan) { if (note < 24) return; if (chan == 0) { SET(__RGBLEDPORT__,__RLED__); noteA = note; periodA = tp[note]; byte LSB = ( periodA & 0x00FF); byte MSB = ((periodA & 0x0F00) >> 8); cli(); send_data(0x00, LSB); send_data(0x01, MSB); send_data(0x08, AmaxVolume); //can be set to 0 by envelope mode note off sei(); } else if (chan == 1) { SET(__RGBLEDPORT__,__GLED__); noteB = note; periodB = tp[note]; byte LSB = ( periodB & 0x00FF); byte MSB = ((periodB >> 8) & 0x000F); cli(); arpeggioCounter = 0; //arpeggio reset send_data(0x02, LSB); send_data(0x03, MSB); sei(); } else if (chan == 2) { SET(__RGBLEDPORT__,__BLED__); noteC = note; periodC = tp[note]; byte LSB = ( periodC & 0x00FF); byte MSB = ((periodC >> 8) & 0x000F); cli(); send_data(0x04, LSB); send_data(0x05, MSB); sei(); } else if (chan == 3) { SET(__RGBLEDPORT__,__RLED__); SET(__RGBLEDPORT__,__GLED__); noteA = note; noteB = note; periodA = tp[note]; periodB = tp[note] + detuneValue; byte ALSB = ( periodA & 0x00FF); byte AMSB = ((periodA >> 8) & 0x000F); byte BLSB = ( periodB & 0x00FF); byte BMSB = ((periodB >> 8) & 0x000F); cli(); send_data(0x00, ALSB); send_data(0x01, AMSB); send_data(0x02, BLSB); send_data(0x03, BMSB); sei(); } else if (chan == 4) { SET(__RGBLEDPORT__,__RLED__); SET(__RGBLEDPORT__,__GLED__); SET(__RGBLEDPORT__,__BLED__); noteA = note; noteB = note; noteC = note; periodA = tp[note - 12]; periodB = tp[note] + detuneValue; periodC = tp[note] - detuneValue; byte ALSB = ( periodA & 0x00FF); byte AMSB = ((periodA >> 8) & 0x000F); byte BLSB = ( periodB & 0x00FF); byte BMSB = ((periodB >> 8) & 0x000F); byte CLSB = ( periodC & 0x00FF); byte CMSB = ((periodC >> 8) & 0x000F); cli(); send_data(0x00, ALSB); send_data(0x01, AMSB); send_data(0x02, BLSB); send_data(0x03, BMSB); send_data(0x04, CLSB); send_data(0x05, CMSB); sei(); } else if (chan == 5) { SET(__RGBLEDPORT__,__RLED__); noteA = note; periodA = envTp[note]; byte LSB = ( periodA & 0x00FF); byte MSB = ((periodA >> 8) & 0x000F); cli(); send_data(0x08, 0x10); send_data(0x0B, LSB); send_data(0x0C, MSB); send_data(0x0D, 0b00001110); sei(); } else if (chan == 6) { SET(__RGBLEDPORT__,__RLED__); noteA = note; periodA = envTp[note]; byte LSB = ( periodA & 0x00FF); byte MSB = ((periodA >> 8) & 0x000F); cli(); send_data(0x0B, LSB); send_data(0x0C, MSB); send_data(0x0D, 0b00001000); sei(); } else if (chan == 7) { SET(__RGBLEDPORT__,__RLED__); SET(__RGBLEDPORT__,__GLED__); noteA = note; noteB = note; periodA = envTp[note]; periodB = (tp[note - 12] + detuneValue) << 1; byte LSB = ( periodA & 0x00FF); byte MSB = ((periodA >> 8) & 0x000F); byte BLSB = ( periodB & 0x00FF); byte BMSB = ((periodB >> 8) & 0x000F); cli(); send_data(0x02, BLSB); send_data(0x03, BMSB); send_data(0x08, 0x10); //envelope mode on send_data(0x0B, LSB); send_data(0x0C, MSB); send_data(0x0D, 0b00001000); sei(); } } void stopNote(byte note, byte chan) { if (chan == 0 && note == noteA) { CLR(__RGBLEDPORT__,__RLED__); noteA = periodA = 0; cli(); send_data(0x00, 0); send_data(0x01, 0); sei(); } else if (chan == 1 && note == noteB) { CLR(__RGBLEDPORT__,__GLED__); noteB = periodB = 0; cli(); send_data(0x02, 0); send_data(0x03, 0); sei(); } else if (chan == 2 && note == noteC) { CLR(__RGBLEDPORT__,__BLED__); noteC = periodC = 0; cli(); send_data(0x04, 0); send_data(0x05, 0); sei(); } else if (chan == 3 && note == noteA) { CLR(__RGBLEDPORT__,__RLED__); CLR(__RGBLEDPORT__,__GLED__); noteA = periodA = 0; noteA = periodB = 0; cli(); send_data(0x00, 0); send_data(0x01, 0); send_data(0x02, 0); send_data(0x03, 0); sei(); } else if (chan == 4 && note == noteA) { CLR(__RGBLEDPORT__,__RLED__); CLR(__RGBLEDPORT__,__GLED__); CLR(__RGBLEDPORT__,__BLED__); noteA = periodA = 0; noteB = periodB = 0; noteC = periodC = 0; cli(); send_data(0x00, 0); send_data(0x01, 0); send_data(0x02, 0); send_data(0x03, 0); send_data(0x04, 0); send_data(0x05, 0); sei(); } else if (chan == 5 && note == noteA) { CLR(__RGBLEDPORT__,__RLED__); noteA = periodA = 0; cli(); send_data(0x0D, 0); send_data(0x08, AmaxVolume); sei(); } else if (chan == 6 && note == noteA) { CLR(__RGBLEDPORT__,__RLED__); noteA = periodA = 0; cli(); send_data(0x0D, 0); send_data(0x08, AmaxVolume); sei(); } else if (chan == 7 && note == noteA) { CLR(__RGBLEDPORT__,__RLED__); CLR(__RGBLEDPORT__,__GLED__); noteA = periodA = 0; noteB = periodB = 0; cli(); send_data(0x02, 0); send_data(0x03, 0); send_data(0x0D, 0); send_data(0x08, AmaxVolume); sei(); } } void playDigidrum(byte index, byte velo) { if (index == 48) { cli(); SET(__RGBLEDPORT__,__BLED__); sampleOffset = s0; sampleLength = s0Length; sampleCounter = 0; sei(); } else if (index == 49) { cli(); SET(__RGBLEDPORT__,__BLED__); sampleOffset = s1; sampleLength = s1Length; sampleCounter = 0; sei(); } else if (index == 50) { cli(); SET(__RGBLEDPORT__,__BLED__); sampleOffset = s2; sampleLength = s2Length; sampleCounter = 0; sei(); } else if (index == 51) { cli(); SET(__RGBLEDPORT__,__BLED__); sampleOffset = s3; sampleLength = s3Length; sampleCounter = 0; sei(); } else if (index == 52) { cli(); SET(__RGBLEDPORT__,__BLED__); sampleOffset = s4; sampleLength = s4Length; sampleCounter = 0; sei(); } } void resetYM() { digitalWrite(pinYMReset, LOW); digitalWrite(pinYMReset, HIGH); delay(1); send_data(0x07, 0b00111000); for (byte i=0; i <= defaultLevel; i++) { send_data(0x08, i); send_data(0x09, i); send_data(0x0A, i); delay(1); } } void send_data(unsigned char address, unsigned char data) { SET(__LEDPORT__,__LED__); boolean value[8]; //put address in a 8-bit array for (int i; i < 8; i++) { value[i] = ((0x01 & address) == 1); address = address >> 1; } //write address to pins outputToYM(value); //validate addess __BCPORT__ |= (1 << __BDIR__) | (1 << __BC1__); delayMicroseconds(1); __BCPORT__ &= ~((1 << __BDIR__) | (1 << __BC1__)); //put data in a 8-bit array for (int i; i < 8; i++) { value[i] = ((0x01 & data) == 1); data = data >> 1; } //write data to pins outputToYM(value); //validate data SET(__BCPORT__,__BDIR__); delayMicroseconds(1); CLR(__BCPORT__,__BDIR__); CLR(__LEDPORT__,__LED__); } void outputToYM(boolean value[]) { value[0] ? SET(PORTB, 0) : CLR(PORTB, 0); value[1] ? SET(PORTB, 1) : CLR(PORTB, 1); value[2] ? SET(PORTD, 2) : CLR(PORTD, 2); value[3] ? SET(PORTD, 3) : CLR(PORTD, 3); value[4] ? SET(PORTD, 4) : CLR(PORTD, 4); value[5] ? SET(PORTD, 5) : CLR(PORTD, 5); value[6] ? SET(PORTD, 6) : CLR(PORTD, 6); value[7] ? SET(PORTD, 7) : CLR(PORTD, 7); } byte getSerialByte() { while(Serial.available() < 1) __asm__("nop\n\t"); return Serial.read(); }