#include <MIDI.h>

// Updated GPIO pins to 74HC595
const int dataPin = 19;    // DS (Pin 14 of Shift Register 1)
const int clockPin = 18;   // SH_CP (Clock Pin for all Shift Registers)
const int latchPin = 5;    // ST_CP (Latch Pin for all Shift Registers)


const int numShiftRegisters = 11; // Total number of 74HC595 shift registers


// Array to hold the state of each shift register
byte shiftRegisterStates[numShiftRegisters] = {0};


void setup() {
 // Initialize Serial Communication
 Serial.begin(115200);
 Serial.println("Hello, ESP32-S2!");


 // Initialize Shift Register Control Pins
 pinMode(dataPin, OUTPUT);
 pinMode(clockPin, OUTPUT);
 pinMode(latchPin, OUTPUT);


 // Initialize control pins to LOW
 digitalWrite(dataPin, LOW);
 digitalWrite(clockPin, LOW);
 digitalWrite(latchPin, LOW);
}


void loop() {
  playMozartMelody();  // Play the simulated Mozart melody
  delay(5000);         // Pause for 5 seconds before repeating
}

// Function to simulate a simple Mozart melody using MIDI Note On/Off events
void playMozartMelody() {
  int melody[] = {60, 60, 67, 67, 69, 69, 67, // C4 C4 G4 G4 A4 A4 G4
                  65, 65, 64, 64, 62, 62, 60}; // F4 F4 E4 E4 D4 D4 C4
  int noteDurations[] = {500, 500, 500, 500, 500, 500, 1000, // Durations for first phrase
                         500, 500, 500, 500, 500, 500, 2000}; // Durations for second phrase
  
  int length = sizeof(melody) / sizeof(melody[0]);
  
  for (int i = 0; i < length; i++) {
    // Simulate Note On event
    handleNoteOn(1, melody[i], 127);  // Channel 1, velocity 127
    delay(noteDurations[i]);          // Hold the note for its duration
    
    // Simulate Note Off event
    handleNoteOff(1, melody[i], 0);   // Channel 1, velocity 0
    delay(100);                       // Short delay between notes
  }
}

// Function to map MIDI note to LED index
int midiNoteToLED(int note) {
  if (note < 21 || note > 108) return -1; // Out of range
  return note - 21; // Maps MIDI note to LED index (0-87)
}

// Handle MIDI Note On Event
void handleNoteOn(byte channel, byte note, byte velocity) {
  int ledNumber = midiNoteToLED(note);
  if (ledNumber == -1) return; // Invalid note

  // Calculate which shift register and which bit
  int srIndex = ledNumber / 8;  // 0 to 10
  int bitIndex = ledNumber % 8; // 0 to 7

  // Set the corresponding bit to turn on the LED
  shiftRegisterStates[srIndex] |= (1 << bitIndex);

  // Update the shift registers
  shiftOutMultiple(shiftRegisterStates, numShiftRegisters);
}

// Handle MIDI Note Off Event
void handleNoteOff(byte channel, byte note, byte velocity) {
  int ledNumber = midiNoteToLED(note);
  if (ledNumber == -1) return; // Invalid note

  // Calculate which shift register and which bit
  int srIndex = ledNumber / 8;  // 0 to 10
  int bitIndex = ledNumber % 8; // 0 to 7

  // Clear the corresponding bit to turn off the LED
  shiftRegisterStates[srIndex] &= ~(1 << bitIndex);

  // Update the shift registers
  shiftOutMultiple(shiftRegisterStates, numShiftRegisters);
}

// Function to shift out data to multiple 74HC595s
void shiftOutMultiple(byte data[], int numRegisters) {
 digitalWrite(latchPin, LOW); // Begin data transmission


 // Shift out all bytes from last to first shift register
 for (int i = numRegisters - 1; i >= 0; i--) {
   shiftOutData(data[i]);
 }


 digitalWrite(latchPin, HIGH); // Update outputs
}


// Function to shift out a single byte to 74HC595
void shiftOutData(byte data) {
 // Shift out the data byte, MSB first
 for (int i = 7; i >= 0; i--) {
   // Write bit by bit
   digitalWrite(clockPin, LOW);
   digitalWrite(dataPin, (data & (1 << i)) ? HIGH : LOW);
   digitalWrite(clockPin, HIGH);
 }
}
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595