#define SPEAKER_PIN 8
const uint8_t buttonPins[] = { 12, 11, 10, 9, 7, 6, 5, 4 };
// Define all notes in chromatic scale using library notation
//const int chromaticScale[] = { NOTE_C1, NOTE_CS1, NOTE_D1, NOTE_DS1, NOTE_E1, NOTE_F1, NOTE_FS1, NOTE_G1, NOTE_GS1, NOTE_A1, NOTE_AS1, NOTE_B1 };
//const int numChromaticNotes = 12;
// Define scales as offsets from the chromatic scale
const int majorScaleOffsets[] = { 0, 2, 4, 5, 7, 9, 11 };
const int minorScaleOffsets[] = { 0, 2, 3, 5, 7, 8, 10 };
const int bluesScaleOffsets[] = { 0, 3, 5, 6, 7, 10, 12 }; // Adjust offsets as needed
const int pentatonicScaleOffsets[] = { 0, 3, 5, 7, 10 };
const int phrygianScaleOffsets[] = { 0, 1, 3, 5, 7, 8, 10 };
const int dorianScaleOffsets[] = { 0, 2, 3, 5, 7, 9, 11 };
// Define scale names
const char* scaleNames[] = { "Major", "Minor", "Blues", "Pentatonic", "Phrygian", "Dorian" };
typedef struct {
const char* name;
const int* offsets;
int numOffsets;
} Scale;
const Scale scales[] = {
{ "Major", majorScaleOffsets, sizeof(majorScaleOffsets) / sizeof(majorScaleOffsets[0]) },
{ "Minor", minorScaleOffsets, sizeof(minorScaleOffsets) / sizeof(minorScaleOffsets[0]) },
{ "Blues", bluesScaleOffsets, sizeof(bluesScaleOffsets) / sizeof(bluesScaleOffsets[0]) },
{ "Pentatonic", pentatonicScaleOffsets, sizeof(pentatonicScaleOffsets) / sizeof(pentatonicScaleOffsets[0]) },
{ "Phrygian", phrygianScaleOffsets, sizeof(phrygianScaleOffsets) / sizeof(phrygianScaleOffsets[0]) },
{ "Dorian", dorianScaleOffsets, sizeof(dorianScaleOffsets) / sizeof(dorianScaleOffsets[0]) }
};
typedef struct {
char name[4]; // Store note name as a string (e.g., "C4")
int frequency; // Frequency of the note in Hz
int octave; // Octave number (e.g., 4)
int midiValue; // MIDI value of the note (optional)
} Note;
const Note notes[] = {
// Populate notes from B0 to E8
for (int octave = 0; octave <= 8; ++octave) {
for (int note = 0; note < 12; ++note) {
int midiNote = 60 + note + (octave * 12);
double frequency = 440.0 * pow(2.0, ((midiNote - 69) / 12.0));
// Use string concatenation or a string library for formatting
notes[octave * 12 + note].name[0] = "CDEFGAB"[note % 7];
notes[octave * 12 + note].name[1] = (octave + 1) + '0';
notes[octave * 12 + note].name[2] = '\0'; // Null terminate the string
notes[octave * 12 + note].frequency = (int)frequency;
notes[octave * 12 + note].octave = octave + 1;
notes[octave * 12 + note].midiValue = midiNote;
}
}
};
//Name: B0, Frequency: 31, Hz, Octave: 0, MIDI Value: 60
//Name: C1, Frequency: 62, Hz, Octave: 1, MIDI Value: 61
//Name: C#1, Frequency: 65, Hz, Octave: 1, MIDI Value: 62
//...
//Name: E8, Frequency: 3280, Hz, Octave: 8, MIDI Value: 104
// Find a note by name
Note getNoteByName(const char* name) {
for (int i = 0; i < sizeof(notes) / sizeof(notes[0]); ++i) {
if (strcmp(notes[i].name, name) == 0) {
return notes[i];
}
}
// Return an empty note if not found
return Note();
}
// Find the closest note by frequency
Note getNoteByFrequency(int frequency) {
Note closestNote = { "", 0, 0, 0 };
int minDiff = INT_MAX;
for (int i = 0; i < sizeof(notes) / sizeof(notes[0]); ++i) {
int diff = abs(notes[i].frequency - frequency);
if (diff < minDiff) {
minDiff = diff;
closestNote = notes[i];
}
}
return closestNote;
}
// Shift the octave of a note
void shiftOctave(Note& note, int offset) {
note.octave += offset;
note.frequency *= pow(2, offset); // Update frequency based on octave change
}
// Get the scale offsets for a given scale name (implementation depends on your approach)
// You can create separate arrays for different scales or use a map/dictionary
int* getScaleOffsets(const char* scaleName) {
for (int i = 0; i < sizeof(scales) / sizeof(scales[0]); ++i) {
if (strcmp(scales[i].name, scaleName) == 0) {
return scales[i].offsets; // Return the matching scale's offset array
}
}
// Return nullptr if scale not found
return nullptr;
}
// Current key, scale, and octave variables
Note currentKey = getNoteByName("C1");
int currentScale = 0; // Index of the current scale in scaleNames and offset arrays
int currentOctave = 4; // Starting octave (C1 starts in octave 1)
bool shiftPressed = false; // Flag to track shift button state
void setup() {
Serial1.begin(115200);
Serial1.println("Pi Pico Serial Monitor example!");
Serial1.println("-------------------------------");
for (uint8_t i = 0; i < sizeof(buttonPins) / sizeof(buttonPins[0]); i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
pinMode(SPEAKER_PIN, OUTPUT);
}
void loop() {
int pitch = 0;
// Check if shift button is pressed
shiftPressed = digitalRead(buttonPins[7]) == LOW;
// Loop through remaining buttons
for (uint8_t i = 0; i < sizeof(buttonPins) / sizeof(buttonPins[0]); i++) {
if (digitalRead(buttonPins[i]) == LOW) {
// Handle different actions based on button and shift state
if (shiftPressed) {
switch (i) {
case 0: // Pin 12 (increment key)
//currentKey = (currentKey + 2) % numChromaticNotes; // Increment by 2 for sharps/flats
Serial1.println("Key increased");
break;
case 1: // Pin 11 (decrement key)
//currentKey = (currentKey - 2 + numChromaticNotes) % numChromaticNotes; // Decrement by 2 for sharps/flats
Serial1.println("Key decreased");
break;
case 2: // Pin 10 (change scale forward)
//currentScale = (currentScale + 1) % sizeof(scaleNames) / sizeof(scaleNames[0]);
Serial1.println("Scale increased");
break;
case 3: // Pin 9 (change scale backward)
//currentScale = (currentScale - 1 + sizeof(scaleNames) / sizeof(scaleNames[0])) % sizeof(scaleNames) / sizeof(scaleNames[0]);
Serial1.println("Scale decreased");
break;
case 4: // Pin 7 (increase octave)
currentOctave++;
Serial1.println("Octave increased");
break;
case 5: // Pin 6 (decrease octave)
currentOctave--;
Serial1.println("Octave decreased");
break;
}
} else {
// Play note in the current scale
Serial1.print("CurrentKey: ");
Serial1.println(currentKey);
//int noteIndex = i % numChromaticNotes;
Serial1.print("noteIndex: ");
Serial1.println(noteIndex);
//int scaleOffset = *(scaleNames[currentScale] + noteIndex);
Serial1.print("scaleOffset: ");
Serial1.println(scaleOffset);
//int pitch = currentKey + chromaticScale[noteIndex + scales[currentScale].offsets[noteIndex]] + (12 * (currentOctave - 1));
Serial1.print("The pitch is ");
Serial1.println(pitch);
Note* currentNote = findNoteByName(notes, currentKey, currentOctave); // Find matching Note object
int pitch = currentNote->frequency + chromaticScale[offset] + (12 * (currentOctave - 1));
}
}
}
if (pitch) {
tone(SPEAKER_PIN, pitch);
} else {
noTone(SPEAKER_PIN);
}
delay(10);
}