Problem: Today, many children are taking music lessons. They learn to read notes and play them on different instruments. But is it enough to learn notes and scores by heart in order to cultivate an ear for music? Or to recognise notes both auditory and visually to compose them into pleasant melodies that make sense?
Solution: Capacitive μPiano helps beginners to do exactly that. You can repeat your favourite melody as many times you want, until you finally perceive how to reproduce it. Each capacitive sensor represents a different note. The melody’s reading becomes an auditory experience for the user, who previously used visual scores. This process helps new practitioners to train an ear for music from the early beginning.
Demo:
https://www.youtube.com/watch?v=L1W8SxbK3Og
Code:
# include <CapacitiveSensor.h> #include <pitches.h> // Capacitive Sensors CapacitiveSensor cs1 = CapacitiveSensor(6, 9); bool cs1Touched = true; long cs1Val; CapacitiveSensor cs2 = CapacitiveSensor(6, 7); bool cs2Touched = true; long cs2Val; CapacitiveSensor cs3 = CapacitiveSensor(6, 5); bool cs3Touched = true; long cs3Val; CapacitiveSensor cs4 = CapacitiveSensor(6, 8); bool cs4Touched = true; long cs4Val; // Speaker + Music #define speakerPin 4 int melody1[] = {NOTE_E4, NOTE_G3, NOTE_G3, NOTE_C4, NOTE_G3, 0, NOTE_B3, NOTE_C4}; int noteDurations[] = {4, 8, 8, 4, 4, 4, 4, 4}; // Switch #define switchPin 2 typedef struct switchTracker { int lastReading; // last raw value read long lastChangeTime; // last time the raw value changed byte pin; // the pin this is tracking changes on byte switchState; // debounced state of the switch } switchTrack; void initSwitchTrack(struct switchTracker &sw, int swPin) { pinMode(swPin, INPUT); sw.lastReading = digitalRead(swPin); sw.lastChangeTime = millis(); sw.pin = swPin; sw.switchState = sw.lastReading; } switchTrack switchInput; bool practiseTime = false; void setup() { Serial.begin(9600); pinMode(speakerPin, OUTPUT); initSwitchTrack(switchInput, switchPin); attachInterrupt(digitalPinToInterrupt(switchPin), changeMode, RISING); } void loop() { Serial.println(practiseTime); if (practiseTime) { practise(); } else { playMelody(); delay(3000); } } void changeMode() { practiseTime = !practiseTime; } void practise() { capacitiveSensor1(); capacitiveSensor2(); capacitiveSensor3(); capacitiveSensor4(); } void capacitiveSensor1() { cs1Val = cs1.capacitiveSensor(80); // resolution if (cs1Touched) { if (cs1Val > 1000) { Serial.println(cs1Val); cs1Touched = false; tone(speakerPin, NOTE_E4); } } if (!cs1Touched) { if (cs1Val < 100) { Serial.println(cs1Val); noTone(speakerPin); cs1Touched = true; } } } void capacitiveSensor2() { cs2Val = cs2.capacitiveSensor(80); // resolution if (cs2Touched) { if (cs2Val > 1000) { Serial.println(cs2Val); cs2Touched = false; tone(speakerPin, NOTE_G3); } } if (!cs2Touched) { if (cs2Val < 100) { Serial.println(cs2Val); noTone(speakerPin); cs2Touched = true; } } } void capacitiveSensor3() { cs3Val = cs3.capacitiveSensor(80); // resolution if (cs3Touched) { if (cs3Val > 1000) { Serial.println(cs3Val); cs3Touched = false; tone(speakerPin, NOTE_C4); } } if (!cs3Touched) { if (cs3Val < 100) { Serial.println(cs3Val); cs3Touched = true; noTone(speakerPin); } } } void capacitiveSensor4() { cs4Val = cs4.capacitiveSensor(80); // resolution if (cs4Touched) { if (cs4Val > 1000) { Serial.println(cs4Val); cs4Touched = false; tone(speakerPin, NOTE_B3); } } if (!cs4Touched) { if (cs4Val < 100) { Serial.println(cs4Val); noTone(speakerPin); cs4Touched = true; } } } void playMelody() { for (int thisNote = 0; thisNote < 8; thisNote++) { int noteDuration = 1000 / noteDurations[thisNote]; tone(speakerPin, melody1[thisNote], noteDuration); // to distinguish the notes, set a minimum time between them. // the note's duration + 30% seems to work well: int pauseBetweenNotes = noteDuration * 1.30; delay(pauseBetweenNotes); // stop the tone playing: noTone(1); } } boolean switchChange(struct switchTracker & sw) { const long debounceTime = 100; // default to no change until we find out otherwise boolean result = false; int reading = digitalRead(sw.pin); if (reading != sw.lastReading) sw.lastChangeTime = millis(); sw.lastReading = reading; // if time since the last change is longer than the required dwell if ((millis() - sw.lastChangeTime) > debounceTime) { result = (reading != sw.switchState); // in any case the value has been stable and so the reported state // should now match the current raw reading sw.switchState = reading; } return result; }