#include <Adafruit_SSD1306.h>
#include <MIDI.h>
// #include <Encoder.h>
// #include <Utilities.h>
// OLED display dimensions
#define OLED_WIDTH 128
#define OLED_HEIGHT 32
// OLED display address (depends on your display)
#define OLED_ADDRESS 0x3C
// OLED display object
Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire, OLED_ADDRESS);
// Create a default MIDI instance
MIDI_CREATE_DEFAULT_INSTANCE();
// MIDI channel
uint8_t channel;
// Button pins
const uint8_t buttonPins[] = { 12, 11, 10, 9, 8, 7, 6, 5 };
// LED pins
const uint8_t signalPins[] = {A0, A1, A2, A3};
// Encoder library
// Encoder rotaryEncoder(3, 2);
const int ENCODER_SW = 13;
const int encoderPinA = 2; // pin for encoder A
const int encoderPinB = 3; // pin for encoder B
int a = 0; // initial value for variable a
int b = 0; // initial value for variable b
int c = 0; // initial value for variable c
int d = 0;
int e = 0;
// Declare variables to store the previous values of a, b, and c
// (used to check if the values have changed)
int prevA = 0;
int prevB = 0;
int prevC = 0;
int prevD = 0;
int prevE = 0;
// Declare a variable to store the current active value (either a, b, c or d)
int activeValue = 0; // 0 = a, 1 = b, 2 = c, 3 = d, 4 = e
// Encoder encoder(encoderPinA, encoderPinB); // create an Encoder object
// Buzzer pin
#define SPEAKER_PIN 4
// Frequency output at the buzzer
uint16_t frequ = 440;
// Is something being played?
bool playing = false;
// Encoder switch and movement
int lastClk = HIGH;
int lastencSW = HIGH;
// Counter and message
int zaehler = 0;
String message = " ";
// Mode number and parameter to adjust with encoder
int modus = 0;
String modusName[] = {"Speed", "Scale", "Base", "Channel"};
// Encoder movement
volatile int rotary = 0;
// Beats per minute, note spacing and frequency
// int bpm = 120;
uint16_t bpm = 120;
float period = 500;
float frequency;
unsigned long startMillis = millis(); //
unsigned long currentMillis; // what time is it?
// Scale index and base tone
int indexSkala = 1;
int baseTone = 60;
// Name of tone played and scale
String midiNote = "";
String skalenname = "";
struct NoteInfo { // Variable, in der sowohl Name als auch Frequenz gespeichert sind.
String name;
int frequency;
};
// Scales
int skala[] = {1, 3, 5, 6, 8, 10, 12, 13, 13, 15, 17, 18, 20, 22, 24, 25};
int ion_skala[] = {1, 3, 5, 6, 8, 10, 12, 13, 13, 15, 17, 18, 20, 22, 24, 25};
int dor_skala[] = {1, 3, 4, 6, 8, 10, 11, 13, 13, 15, 16, 18, 20, 22, 23, 25};
int phy_skala[] = {1, 2, 4, 6, 8, 9, 11, 13, 13, 14, 16, 18, 20, 21, 23, 25};
int lyd_skala[] = {1, 3, 5, 7, 8, 10, 12, 13, 13, 15, 17, 19, 20, 22, 24, 25};
int mix_skala[] = {1, 3, 5, 6, 8, 10, 11, 13, 13, 15, 17, 18, 20, 22, 23, 25};
int aol_skala[] = {1, 3, 4, 6, 8, 10, 11, 13, 13, 15, 16, 18, 20, 22, 23, 25};
int loc_skala[] = {1, 3, 5, 6, 8, 10, 11, 13, 13, 15, 16, 18, 20, 22, 23, 25};
int aeo_skala[] = {1, 3, 5, 6, 8, 9, 11, 13, 13, 14, 16, 18, 20, 21, 23, 25};
bool increment = true; // used in loop
uint8_t x = 0;
uint8_t i = 0;
void setup() {
// Set up the OLED display
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS);
// display.display();
// display.clearDisplay();
oledOut("Starting up");
delay(500);
oledOut("");
// without encoder library:
// Set the pin modes for the encoder and button
pinMode(encoderPinA, INPUT_PULLUP);
pinMode(encoderPinB, INPUT_PULLUP);
pinMode(ENCODER_SW, INPUT_PULLUP);
// Set up the MIDI interface
// MIDI.begin(31250);
MIDI.begin(MIDI_CHANNEL_OMNI);
// Set up the buttons
for (uint8_t i = 0; i < sizeof(buttonPins); i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
// Set up the LEDs
for (uint8_t i = 0; i < sizeof(signalPins); i++) {
pinMode(signalPins[i], OUTPUT);
}
// Set up the buzzer
pinMode(SPEAKER_PIN, OUTPUT);
// Set up the encoder switch
pinMode(ENCODER_SW, INPUT_PULLUP);
int midiNummer = 60; // start with Note C4
/*
while (digitalRead(ENCODER_SW) == HIGH) { // until you press Encoder button
// Call the getNoteName function and assign the returned values to variables
NoteInfo info = getNoteName(midiNummer);
tone(SPEAKER_PIN, info.frequency);
MIDI.sendNoteOn(midiNummer, 100, 1);
oledOut(info.name);
oledOut2(String(info.frequency));
delay(300);
MIDI.sendNoteOn(midiNummer, 0, 1);
// Increment the MIDI number and reset if needed
midiNummer ++;
if (midiNummer >= 100) {
midiNummer = 11;
}
noTone(SPEAKER_PIN);
}
*/
oledOut("Now main program");
delay(100);
// oledOut(String(a) + " "+ String(b) + " " + String(c) + " " + String(d) + " " + String(e));
}
void loop() {
// Read the state of the button
int buttonState = digitalRead(ENCODER_SW);
// If the button is pressed, cycle to the next active value
if (buttonState == LOW) {
activeValue++;
if (activeValue > 4) {
activeValue = 0;
}
digitalWrite(signalPins[activeValue], HIGH);
digitalWrite(signalPins[activeValue - 1], LOW);
oledOut(modusName[activeValue]);
oledOut2(String(a) + " " + String(b) + " " + String(c) + " " + String(d) + " " + String(e));
// delay(200); // Debounce the button press
}
a = 0; // a, b, c, d, e are zero, unless changed with the encoder
b = 0;
c = 0;
d = 0;
e = 0;
// Read the state of the encoder pins
int encoderStateA = digitalRead(encoderPinA);
int encoderStateB = digitalRead(encoderPinB);
// Check if the encoder has been turned
if (encoderStateA != encoderStateB) {
delay(10);
// The encoder has been turned in the clockwise direction
if (encoderStateA == LOW) {
// Increment the active value
if (activeValue == 0) {
bpm = bpm++;
a++;
} else if (activeValue == 1) {
b++;
indexSkala = indexSkala++;
} else if (activeValue == 2) {
c++;
baseTone = baseTone++;
// digitalWrite(signalPins[1], HIGH);
} else if (activeValue == 3) {
channel = channel++;
d++;
// digitalWrite(signalPins[2], HIGH);
} else if (activeValue == 4) {
e++;
// digitalWrite(signalPins[3], HIGH);
}
} else {
// The encoder has been turned in the counterclockwise direction
// Decrement the active value
if (activeValue == 0) {
a--;
bpm = bpm--;
digitalWrite(signalPins[0], HIGH);
} else if (activeValue == 1) {
b--;
indexSkala = indexSkala--;
digitalWrite(signalPins[1], HIGH);
} else if (activeValue == 2) {
c--;
baseTone = baseTone--;
digitalWrite(signalPins[2], HIGH);
} else if (activeValue == 3) {
d--;
channel = channel--;
digitalWrite(signalPins[3], HIGH);
} else if (activeValue == 4) {
e++;
}
}
}
//oledOut2(String(a) + " " + String(b) + " " + String(c) + " " + String(d) + " " + String(e));
// set the limits
if (bpm < 20) {
bpm = 20;
}
if (bpm > 2600) {
bpm = 260;
}
period = 60000 / bpm;
// indexSkala = indexSkala + b;
if (indexSkala < 0) {
indexSkala = 7;
}
if (indexSkala > 7) {
indexSkala = 0;
}
// Nun zur Auswahl der Skala:
switch (indexSkala) { // Which scale to play? shows skalenname
case 1:
skalenname = "Ionisch (Dur)";
memcpy(skala, ion_skala, sizeof(ion_skala));
break;
case 2:
skalenname = "Dorisch";
memcpy(skala, dor_skala, sizeof(dor_skala));
break;
case 3:
skalenname = "Phyrgisch";
memcpy(skala, phy_skala, sizeof(phy_skala));
break;
case 4:
skalenname = "Lydisch";
memcpy(skala, lyd_skala, sizeof(lyd_skala));
break;
case 5:
skalenname = "Mixolydisch";
memcpy(skala, mix_skala, sizeof(mix_skala));
break;
case 6:
skalenname = "Aeolisch";
memcpy(skala, aeo_skala, sizeof(aeo_skala));
break;
case 7:
skalenname = "Lokrisch";
memcpy(skala, loc_skala, sizeof(loc_skala));
break;
}
// baseTone = baseTone + c;
if (baseTone < 11) {
baseTone = 11;
}
if (baseTone > 85) {
baseTone = 85;
}
//channel = channel + d;
if (channel < 1) {
channel = 16;
}
if (channel > 16) {
channel = 1;
}
// oledOut2(skalenname);
// Check if the values of a, b, and c have changed
if (a != prevA || b != prevB || c != prevC || d != prevD || e != prevE ) {
//*
// Print the current values of a, b, and c on the serial port
// oledOut2(String(a) + " " + String(b) + " " + String(c) + " " + String(d) + " " + String(e));
// Update the previous values of a, b, and c
prevA = a;
prevB = b;
prevC = c;
prevD = d;
prevE = e;
}
// Und hier jetzt die Ausgabe - abhängig von den millis...
// currentMillis = millis(); //get the current time
if (millis() - startMillis >= period) //test whether the period has elapsed
{
// oledOut("I am here");
Serial.print("bpm = ");
Serial.print(bpm);
Serial.print(period);
Serial.print(" Skalenindex = ");
Serial.print(indexSkala);
Serial.print(" Basis = ");
Serial.print(baseTone);
Serial.print( " Channel = ");
Serial.println(channel);
// Serial.println("I am here just after if millis in loop");
// delay(200);
//****
// oledOut(String(skala[i]));
// delay(200);
// int midiNummer = baseTone + skala[i] - 1; //-1 + skala[i];
// oledOut("I am here");
// delay(200);
// oledOut(String(midiNummer));
// */
// Midinummer_Name(); // Name aus Nummer generieren
//
startMillis = millis(); // currentMillis; //IMPORTANT to save the start time
// MIDI.sendNoteOn(60, 127, 1); // Send a MIDI note on message
// midi.sendNoteOff(60, 0, 1);;
oledOut(String(bpm) + " " + skalenname + " " + String(baseTone) + " " + String(channel));
// Jetzt zur nächsten Note der Skala schalten
if (x == 0) {
// Inkrementieren Sie den Zählerwert
x++;
} else if (increment && x < 16) {
// Inkrementieren Sie den Zählerwert
x++;
} else if (!increment && x > 1) {
// Dekrementieren Sie den Zählerwert
x--;
} else {
// Wechseln Sie die Zählerrichtung
increment = !increment;
}
i = x - 1;
/****/
// oledOut("inside loop");
// oledOut2(String(bpm)+" "+ skalenname);
}
/*
if (currentMillis - startMillis >= period / 100) {
//digitalWrite(pinBeat, HIGH);
// digitalWrite(pinBeat, HIGH);
digitalWrite(signalPins[2], LOW);
}
else {
digitalWrite(signalPins[2], HIGH);
}*/
}
/*
void loop(){
static int currentVariable = 0; // 0=a, 1=b, 2=c
static int lastButtonState = HIGH; // last state of the button
// Check for button press
int buttonState = digitalRead(ENCODER_SW);
if (buttonState == LOW && lastButtonState == HIGH) {
// Button has just been pressed
currentVariable = (currentVariable + 1) % 3; // cycle through variables a, b, c
}
lastButtonState = buttonState;
// Read the encoder position
long newPosition = encoder.read();
// Check if the encoder has moved
if (newPosition != encoder.read()) {
// Encoder has moved, update the appropriate variable
if (currentVariable == 0) {
digitalWrite(signalPins[1], HIGH);
a += newPosition - encoder.read();
}
else if (currentVariable == 1) {
digitalWrite(signalPins[2], HIGH);
b += newPosition - encoder.read();
}
else if (currentVariable == 2) {
digitalWrite(signalPins[3], HIGH);
c += newPosition - encoder.read();
}
encoder.write(newPosition); // save the new position
}
oledOut(String(a) + " "+ String(b) + " " + String(c));
digitalWrite(signalPins[1], LOW);
digitalWrite(signalPins[2], LOW);
digitalWrite(signalPins[3], HIGH);
// You can use the variables a, b, and c in your code here
}
*/
/*
void loop() {
// Read the encoder movement
rotary = rotaryEncoder.read();
// Read the encoder switch
int encSW = digitalRead(ENCODER_SW);
// Check for encoder movement
if (rotary != 0) {
// Update the display
updateDisplay();
}
// Check for button press
for (uint8_t i = 0; i < sizeof(buttonPins); i++) {
if (digitalRead(buttonPins[i]) == LOW) {
// Update the display
updateDisplay();
// Play the note
playNote(i);
}
}
// Check for encoder switch press
if (encSW == LOW && lastencSW == HIGH) {
// Update the display
updateDisplay();
// Change the mode
modus++;
if (modus > 3) {
modus = 0;
}
}
lastencSW = encSW;
}
*/
/*
void updateDisplay() {
// Clear the display
display.clearDisplay();
// Set the cursor position
display.setCursor(0, 0);
// Print the message
display.print(message);
// Set the cursor position
display.setCursor(0, 16);
// Print the mode name
display.print(Modusname);
// Update the display
display.display();
}
'/
void playNote(int button) {
// Calculate the MIDI number
int midiNummer = basetone + skala[index * 8 + button];
// Check if a note is being played
if (!playing) {
// Play the note
MIDI.sendNoteOn(midiNummer, 100, channel);
// Set the playing flag
playing = true;
// Turn on the LED
digitalWrite(signalPins[button], HIGH);
// Set the note name
NoteInfo midiNote = getNoteName(midiNummer);
// midiNote = getNoteName(midiNummer);
} else {
// Stop playing the note
MIDI.sendNoteOff(midiNummer, 0, channel);
// Clear the playing flag
playing = false;
// Turn off the LED
digitalWrite(signalPins[button], LOW);
// Clear the note name
midiNote = "";
}
// Update the message
message = "Note: " + midiNote + " Scale: " + skalenname;
}
*/
// here the function that calculates the Name and frequency of a midiNummer
NoteInfo getNoteName(int midiNummer) {
// Calculate the octave
int octave = (midiNummer / 12) - 1;
// Calculate the note number
int note = midiNummer % 12;
// Get the note name
String noteName;
int faktor;
switch (note) {
case 0:
noteName = "C";
faktor = 523.25;
break;
case 1:
noteName = "C#";
faktor = 554.37;
break;
case 2:
noteName = "D";
faktor = 587.33;
break;
case 3:
noteName = "D#";
faktor = 622.25;
break;
case 4:
noteName = "E";
faktor = 659.25;
break;
case 5:
noteName = "F";
faktor = 698.46;
break;
case 6:
noteName = "F#";
faktor = 739.99;
break;
case 7:
noteName = "G";
faktor = 783.99;
break;
case 8:
noteName = "G#";
faktor = 830.61;
break;
case 9:
noteName = "A";
faktor = 880;
break;
case 10:
noteName = "A#";
faktor = 932.33;
break;
case 11:
noteName = "B";
faktor = 987.77;
break;
}
// Calculate the frequency using the provided formula
frequency = faktor * pow(2, octave - 5);
// Alternatively: Calculate 2 raised to a power using bit shifting
// frequency = faktor * (1 << (octave - 5));
// Return the note and frequency
return {noteName + String(octave), frequency};
}
/*
String getNoteName(int midiNummer) {
// Calculate the octave
int octave = (midiNummer / 12) - 1;
// Calculate the note number
int note = midiNummer % 12;
// Get the note name
String noteName;
float faktor;
switch (note) {
case 0:
noteName = "C";
faktor = 523.25;
break;
case 1:
noteName = "C#";
faktor = 554.37;
break;
case 2:
noteName = "D";
faktor = 587.33;
break;
case 3:
noteName = "D#";
faktor = 622.25;
break;
case 4:
noteName = "E";
faktor = 659.25;
break;
case 5:
noteName = "F";
faktor = 698.46;
break;
case 6:
noteName = "F#";
faktor = 739.99;
break;
case 7:
noteName = "G";
faktor = 783.99;
break;
case 8:
noteName = "G#";
faktor = 830.61;
break;
case 9:
noteName = "A";
faktor = 880;
break;
case 10:
noteName = "A#";
faktor = 932.33;
break;
case 11:
noteName = "B";
faktor = 987.77;
break;
}
// Return the note and corresponding frequency
frequency = faktor * (octave - 2);
noteName = noteName + String(octave);
return {noteName, frequency};
}
*/
// void output(const char *message) { // besser mit (String message)
void oledOut(String message) {
//Clear OLED display
display.clearDisplay();
// Display current message on OLED
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println(message);
display.display();
}
void oledOut2(String message) {
// Clear OLED display
//display.clearDisplay();
// Display current message on OLED
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 9);
display.println(message);
display.display();
}
/*
void keyboard() {
int frequ = 0;
int midiNummer;
for (uint8_t i = 0; i < 8; i++) {
if (digitalRead(buttonPins[i]) == LOW) { // if a button is pressed
if (i == 0) {
midiNummer = 60; // select Note to play and Frequency
frequ = 262;
}
if (i == 1) {
midiNummer = 62;
frequ = 294;
}
if (i == 2) {
midiNummer = 64;
frequ = 330;
}
if (i == 3) {
midiNummer = 65;
frequ = 349;
}
if (i == 4) {
midiNummer = 67;
frequ = 392;
}
if (i == 5) {
midiNummer = 69;
frequ = 440;
}
if (i == 6) {
midiNummer = 71;
frequ = 494;
}
if (i == 7) {
midiNummer = 72;
frequ = 523;
}
}
}
if (frequ) { // if Frequency =|= zero
if (!playing) { // and if we are not already playing
tone(SPEAKER_PIN, frequ); // send Frequency to Speaker pin
MIDI.sendNoteOn(midiNummer, 127, channel); // and send MIDI Signal
oledOut(String("Frequency: ") + frequ); // Show what happens on OLED
playing = true; // yes, we play
}
} else { // if Frequency is zero
if (playing) { // and if we are playing
noTone(SPEAKER_PIN); // stop Speaker
MIDI.sendNoteOn(midiNummer, 0, channel); // Stop MIDI Signal
oledOut("Push Encoder Button"); // Show what happens
oledOut2("or the keys"); // and what can be done
playing = false; // and remember nothing is plaid
}
}
}
*/