///////////////////////// Feb 2023 //////////////////////////////////////////////////////////////////////////////
// This is an example by dansfing.uk that uses the MIDI.h Library which frees up
// 20% of the program storage. And should make it easier to add some other nice features like a MIDI through socket.
// Arduino YM2149 MIDI Synth
// by yukimizake
// video: https://youtu.be/hUPs2pv5d_g
// schematics: http://electronicfields.wordpress.com/?attachment_id=128 (Vcc/GND not shown)
// 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
#include
MIDI_CREATE_DEFAULT_INSTANCE();
//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();
// Connect the handleNoteOn function to the library,
// so it is called upon reception of a NoteOn.
MIDI.setHandleNoteOn(handleNoteOn); // Put only the name of the function
// Do the same for NoteOffs
MIDI.setHandleNoteOff(handleNoteOff);
// Initiate MIDI communications, listen to all channels
MIDI.begin(MIDI_CHANNEL_OMNI);
//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 handleNoteOn(byte channel, byte pitch, byte velocity)
{
// Do whatever you want when a note is pressed.
playNote(pitch, 127, channel);
// Try to keep your callbacks short (no delays ect)
// otherwise it would slow down the loop() and have a bad impact
// on real-time performance.
}
void handleNoteOff(byte channel, byte pitch, byte velocity)
{
stopNote(pitch, channel);
// Do something when the note is released.
// Note that NoteOn messages with 0 velocity are interpreted as NoteOffs.
}
void loop() {
// Call MIDI.read the fastest you can for real-time performance.
MIDI.read();
}
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();
}