Building another midi chord player. This time I'm using an SD card to provide a data file that can be read and written to into two arrays. Works great. Display, works great. All buttons work perfectly. Code seems to work perfectly. Except, when a midi chord is played, I might have to press the button 4 times to get it to sound. Sometimes twice. Sometimes 6 times. The display updates to the correct chord when played, so I know the "if" statement is being initiated by the correct button push. Thought it was a memory problem, so I downgraded to only 6 chord buttons and only 5 chord choices. No avail. I had functions for the midi chord on/off, but broke those out into each button push. So the code was efficient, but now its not. But that didn't make a difference.
So my question is does the SD card reader cause problems with any of the digital buttons? I'm using a Leonardo, which has its own dedicated SD pins. Here's the code followed by the data file:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Bounce2.h>
#include <MIDIUSB.h>
#include <SD.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
const int SD_CS_PIN = 10; // Chip select pin for SD card module
const char filename[] = "data.txt"; // File name for storing and reading the matrix
const int ButtonPins[] = {0, 1, 4, 5, 6, 7, 8, 9, 11, 12, 13, A0}; // Currently 8 chord buttons, 2 tranpose, 4 programming
int transpose = 0;
int transposebank = 0;
int b14 = 0;
int b15 = 0;
int b16 = 0;
int bp17 = 0;
int assigned;
int Menu_loop = 0;
int notetrack = 0;
int notetrack1 = 0;
int notetrack2 = 0;
int notetrack3 = 0;
int button_assignments[6][4];
const char* chord_names[6];
const int midi_note_nums[12] = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59};
const char* root_chord[12] = {"C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "};
const char* chord_type[5] = {"Maj ", "Min ", "7th ", "M7th", "m7th"};
const int chord_intervals[5][4] = { // Each row is a series of midi intervals
{0, 4, 7, 0}, // Major // intervals that are added to the selected root midi note to create chords
{0, 3, 7, 0}, // Minor
{0, 4, 7, 10}, // 7th
{0, 4, 7, 11}, // maj7th
{0, 3, 7, 10}, // min7th
};
void update_display() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Midi Chord Player ");
lcd.setCursor(0, 2);
lcd.print("Chord:");
lcd.setCursor(7, 2);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print("Octave: ");
lcd.setCursor(8,3);
lcd.print(" ");
lcd.setCursor(8,3);
lcd.print(transposebank);
}
const int nButtons = sizeof ButtonPins / sizeof ButtonPins[0];
Bounce buttons[nButtons];
void setup() {
for (int i = 0; i < nButtons; i++){
pinMode(ButtonPins[i], INPUT_PULLUP);
buttons[i].attach(ButtonPins[i]);
buttons[i].interval(40);
}
lcd.init();
lcd.backlight();
lcd.clear();
update_display();
Serial.begin(31250);
//
// Initialize the SD card
//
if (!SD.begin(SD_CS_PIN)) {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("SD card");
lcd.setCursor (0,1);
lcd.print("Initialization");
lcd.setCursor(0,2);
lcd.print("failed!");
while (1);
}
// Read the button_assignments and chord_names matrices from the SD card function
readMatrix();
}
void loop()
{
for (int i = 0; i < nButtons; i++){
buttons[i].update();
if (buttons[i].fell()) {
if (i==6) { // Check for Transpose Down Button Press - down to -1
if (transposebank== -1) { // If -1 octaves Down button pushed, stays on -1 octaves down (-12 to midi note)
transposebank= -1;
} else {
transposebank = transposebank-1;
transpose = transpose-12; // If Octave is 1, 2, or 3, drops notes -12
lcd.setCursor (8,3);
lcd.print(transposebank);
}
} else if (i==7) { // Check for Transpose Up Button Press
if (transposebank==3) // If 3 octaves up (+3), deducts -36 to midi note and rolls back over to 0 octaves up
{
transposebank = 3;
} else {
transposebank= transposebank+1;
transpose = transpose+12; // If UP pushed adds +12 to midi note for button push
lcd.setCursor (8,3);
lcd.print(transposebank);
}
} else if (i==0) // check for chord button press buttons 0-7
{
notetrack = ((button_assignments[i][0])+transpose);
midiEventPacket_t noteOn = {0x09, 0x90 | 0, notetrack, 60};
MidiUSB.sendMIDI(noteOn);
delay(10);
notetrack1 = ((button_assignments[i][1])+transpose);
midiEventPacket_t noteOn1 = {0x09, 0x90 | 0, notetrack1, 60};
MidiUSB.sendMIDI(noteOn1);
delay(10);
notetrack2 = ((button_assignments[i][2])+transpose);
midiEventPacket_t noteOn2 = {0x09, 0x90 | 0, notetrack2, 60};
MidiUSB.sendMIDI(noteOn2);
delay(10);
notetrack3 = ((button_assignments[i][3])+transpose);
midiEventPacket_t noteOn3 = {0x09, 0x90 | 0, notetrack3, 60};
MidiUSB.sendMIDI(noteOn3);
delay(10);
lcd.setCursor(7, 2);
lcd.print(String(chord_names[i]));
} else if (i==1)
{
notetrack = ((button_assignments[i][0])+transpose);
midiEventPacket_t noteOn = {0x09, 0x90 | 0, notetrack, 60};
MidiUSB.sendMIDI(noteOn);
delay(10);
notetrack1 = ((button_assignments[i][1])+transpose);
midiEventPacket_t noteOn1 = {0x09, 0x90 | 0, notetrack1, 60};
MidiUSB.sendMIDI(noteOn1);
delay(10);
notetrack2 = ((button_assignments[i][2])+transpose);
midiEventPacket_t noteOn2 = {0x09, 0x90 | 0, notetrack2, 60};
MidiUSB.sendMIDI(noteOn2);
delay(10);
notetrack3 = ((button_assignments[i][3])+transpose);
midiEventPacket_t noteOn3 = {0x09, 0x90 | 0, notetrack3, 60};
MidiUSB.sendMIDI(noteOn3);
delay(10);
lcd.setCursor(7, 2);
lcd.print(String(chord_names[i]));
} else if (i==2)
{
notetrack = ((button_assignments[i][0])+transpose);
midiEventPacket_t noteOn = {0x09, 0x90 | 0, notetrack, 60};
MidiUSB.sendMIDI(noteOn);
delay(10);
notetrack1 = ((button_assignments[i][1])+transpose);
midiEventPacket_t noteOn1 = {0x09, 0x90 | 0, notetrack1, 60};
MidiUSB.sendMIDI(noteOn1);
delay(10);
notetrack2 = ((button_assignments[i][2])+transpose);
midiEventPacket_t noteOn2 = {0x09, 0x90 | 0, notetrack2, 60};
MidiUSB.sendMIDI(noteOn2);
delay(10);
notetrack3 = ((button_assignments[i][3])+transpose);
midiEventPacket_t noteOn3 = {0x09, 0x90 | 0, notetrack3, 60};
MidiUSB.sendMIDI(noteOn3);
delay(10);
lcd.setCursor(7, 2);
lcd.print(String(chord_names[i]));
} else if (i==3) {
notetrack = ((button_assignments[i][0])+transpose);
midiEventPacket_t noteOn = {0x09, 0x90 | 0, notetrack, 60};
MidiUSB.sendMIDI(noteOn);
delay(10);
notetrack1 = ((button_assignments[i][1])+transpose);
midiEventPacket_t noteOn1 = {0x09, 0x90 | 0, notetrack1, 60};
MidiUSB.sendMIDI(noteOn1);
delay(10);
notetrack2 = ((button_assignments[i][2])+transpose);
midiEventPacket_t noteOn2 = {0x09, 0x90 | 0, notetrack2, 60};
MidiUSB.sendMIDI(noteOn2);
delay(10);
notetrack3 = ((button_assignments[i][3])+transpose);
midiEventPacket_t noteOn3 = {0x09, 0x90 | 0, notetrack3, 60};
MidiUSB.sendMIDI(noteOn3);
delay(10);
lcd.setCursor(7, 2);
lcd.print(String(chord_names[i]));
} else if (i==4) {
notetrack = ((button_assignments[i][0])+transpose);
midiEventPacket_t noteOn = {0x09, 0x90 | 0, notetrack, 60};
MidiUSB.sendMIDI(noteOn);
delay(10);
notetrack1 = ((button_assignments[i][1])+transpose);
midiEventPacket_t noteOn1 = {0x09, 0x90 | 0, notetrack1, 60};
MidiUSB.sendMIDI(noteOn1);
delay(10);
notetrack2 = ((button_assignments[i][2])+transpose);
midiEventPacket_t noteOn2 = {0x09, 0x90 | 0, notetrack2, 60};
MidiUSB.sendMIDI(noteOn2);
delay(10);
notetrack3 = ((button_assignments[i][3])+transpose);
midiEventPacket_t noteOn3 = {0x09, 0x90 | 0, notetrack3, 60};
MidiUSB.sendMIDI(noteOn3);
delay(10);
lcd.setCursor(7, 2);
lcd.print(String(chord_names[i]));
} else if (i==5) {
notetrack = ((button_assignments[i][0])+transpose);
midiEventPacket_t noteOn = {0x09, 0x90 | 0, notetrack, 60};
MidiUSB.sendMIDI(noteOn);
delay(10);
notetrack1 = ((button_assignments[i][1])+transpose);
midiEventPacket_t noteOn1 = {0x09, 0x90 | 0, notetrack1, 60};
MidiUSB.sendMIDI(noteOn1);
delay(10);
notetrack2 = ((button_assignments[i][2])+transpose);
midiEventPacket_t noteOn2 = {0x09, 0x90 | 0, notetrack2, 60};
MidiUSB.sendMIDI(noteOn2);
delay(10);
notetrack3 = ((button_assignments[i][3])+transpose);
midiEventPacket_t noteOn3 = {0x09, 0x90 | 0, notetrack3, 60};
MidiUSB.sendMIDI(noteOn3);
delay(10);
lcd.setCursor(7, 2);
lcd.print(String(chord_names[i]));
} else if (i==8){
Sub_Menu(); // Submenu program functionality
update_display();
}
} else if (buttons[i].rose()) { // Check if cord button rose
midiEventPacket_t noteOff = {0x08, 0x80 | 0, notetrack, 0}; // if note button rose, turn off note
MidiUSB.sendMIDI(noteOff);
delay(10);
midiEventPacket_t noteOff1 = {0x08, 0x80 | 0, notetrack1, 0}; // if note1 button rose, turn off note
MidiUSB.sendMIDI(noteOff1);
delay(10);
midiEventPacket_t noteOff2 = {0x08, 0x80 | 0, notetrack2, 0}; // if note2 button rose, turn off note
MidiUSB.sendMIDI(noteOff2);
delay(10);
midiEventPacket_t noteOff3 = {0x08, 0x80 | 0, notetrack3, 0}; // if note3 button rose, turn off note
MidiUSB.sendMIDI(noteOff3);
delay(10);
lcd.setCursor(7, 2);
lcd.print(" ");
}
}
}
/////// Clear memory before reading /////
void freeChordNamesMemory() {
for (int i = 0; i < 6; i++) {
if (chord_names[i] != NULL) {
delete[] chord_names[i]; // Free the memory
chord_names[i] = NULL; // Set the pointer to NULL
}
}
}
/////// Read Matrix From SD Card /////
void readMatrix() {
File file = SD.open("data.txt", FILE_READ);
if (file) {
// Read button_assignments array
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 4; j++) {
button_assignments[i][j] = file.parseInt();
}
}
// Consume the newline character at the end of button_assignments
file.readStringUntil('\n');
// Read chord_names array
for (int i = 0; i < 6; i++) {
String chordStr = file.readStringUntil('\n'); // Read the string until newline
chord_names[i] = new char[chordStr.length() + 1]; // Allocate memory
strcpy(chord_names[i], chordStr.c_str()); // Copy the string
}
file.close(); // Close the file
} else {
// Display an error message if the file cannot be opened
lcd.clear();
lcd.print("Error opening data.txt");
}
}
/////// Save button_assignments to SD Card ///////
void saveMatrix() {
// Delete the existing file
SD.remove(filename);
// Create a new file
File file = SD.open(filename, FILE_WRITE);
if (file) {
// Write button_assignments array
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 4; j++) {
file.print(button_assignments[i][j]);
file.print(' ');
}
file.println();
}
// Write chord_names array
for (int i = 0; i < 6; i++) {
file.println(String(chord_names[i]));
}
file.close(); // Close the file
} else {
lcd.clear();
lcd.print("Error writing data.txt");
}
}
/////// Program Buttons Function///////
void Sub_Menu() { //loop until save/exit //
Menu_loop = 0;
lcd.clear();
lcd.setCursor (0,0);
lcd.print ("Program: Select All");
lcd.setCursor(0,1);
lcd.print("Button:" + String(b14 + 1));
lcd.setCursor(0,2);
lcd.print("Assigned:" + String(chord_names[b14]));
lcd.setCursor(0,3);
lcd.print("Root:");
lcd.setCursor (8,3);
lcd.print("Type:");
while (Menu_loop < 1) {
for (int m = 8; m < 12; m++) {
buttons[m].update();
if (buttons[m].fell()) {
if (m==8) { // Pick Button to Program
b14 = b14+1;
assigned = b14;
if (b14 > 5) {
b14 = 0;
}
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print("Button:" + String(b14 +1));
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,2);
lcd.print ("Assigned:" + String(chord_names[b14]));
Menu_loop = 0;
b15=0;
b16=0;
} else if (m==9) { //Pick Root Chord
b15 = b15 + 1;
if (b15 > 11) {
b15 = 0;
}
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print("Button:" + String(b14));
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,2);
lcd.print ("Assigned:" + String(chord_names[assigned]));
lcd.setCursor(0,3);
lcd.print ("Root:" + String(root_chord[b15]));
Menu_loop = 0;
} else if (m==10) // Pick Type of Chord
{
b16 = b16 + 1;
if (b16 > 5) {
b16 = 0;
}
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print("Button:" + String(b14));
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,2);
lcd.print ("Assigned:" + String(chord_names[assigned]));
lcd.setCursor (8,3);
lcd.print ("Type:" + String(chord_type[b16]));
String combinedString = String(root_chord[b15]) + String(chord_type[b16]);
if (chord_names[b14] != NULL) {
delete[] chord_names[b14]; // Free the existing string if it exists
}
chord_names[b14] = new char[combinedString.length() + 1]; // Allocate new memory
strcpy(chord_names[b14], combinedString.c_str()); // Copy the new string
for (int j = 0; j<4; j++ ) {
button_assignments[b14][j] = chord_intervals[b16][j] + midi_note_nums[b15]; // Assign Chord Midi #'s to Array
}
Menu_loop = 0;
} else if (m==11) {
lcd.clear();
lcd.setCursor(4,1);
lcd.print ("Save & Exit ?");
lcd.setCursor(3,2);
lcd.print("(press again)");
if (bp17 == 0) {
bp17 = bp17 + 1;
Menu_loop = 0;
} else if (bp17 == 1) {
saveMatrix(); // Save New Button Assignments to SD Card
freeChordNamesMemory(); // Erase Memory
readMatrix(); // Reload Memory
b14 = 0;
b15= 0;
b16 = 0;
bp17 = 0;
Menu_loop = 1;
}
}
}
}
}
}
data file:
60 64 67 72
65 69 72 76
70 74 77 81
75 79 82 86
80 84 87 91
85 89 92 96
C Maj
D Min
E 7th
F maj7th
G min7th
A sus4