Capacitive μPiano: Cultivating an ear for music

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;
}

 

 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.