Mini Piano

Rig0

New member
Hey,
I'm trying to build a mini piano just for my kids. I have the Teensy 4.0 and it's audio adapter board. I could play 3 sounds and when I tried to play more it did not work, and I do not why, I am not a programmer I just have a basic knowledge about coding, if someone can help it would be fantastic.
Thanks!
 

Attachments

  • Code.txt
    5.5 KB · Views: 14
Its usual to post code with the code tags tool (see the </> icon) like this:

Code:
#include <Arduino.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>


// ---- AUDIO OBJECTS (2 voices per note) ----

// Note 1: C3
AudioPlaySdWav note1a;
AudioPlaySdWav note1b;

// Note 2: D3
AudioPlaySdWav note2a;
AudioPlaySdWav note2b;

// Note 3: E3
AudioPlaySdWav note3a;
AudioPlaySdWav note3b;

// Note 4: F3
AudioPlaySdWav note4a;
AudioPlaySdWav note4b;

// Note 5: G3
AudioPlaySdWav note5a;
AudioPlaySdWav note5b;

// Note 6: A3
AudioPlaySdWav note6a;
AudioPlaySdWav note6b;

// Note 7: B3
AudioPlaySdWav note7a;
AudioPlaySdWav note7b;

// Note 8: C4
AudioPlaySdWav note8a;
AudioPlaySdWav note8b;

// ---- 4 sub-mixers (AudioMixer4) for 16 total voices ----
// We'll group them in pairs: (C3,D3), (E3,F3), (G3,A3), (B3,C4).
AudioMixer4 mixerA; // for note1a, note1b, note2a, note2b
AudioMixer4 mixerB; // for note3a, note3b, note4a, note4b
AudioMixer4 mixerC; // for note5a, note5b, note6a, note6b
AudioMixer4 mixerD; // for note7a, note7b, note8a, note8b
AudioMixer4 mixerMaster;

// Output + SGTL5000 control
AudioOutputI2S      i2s1;
AudioControlSGTL5000 sgtl5000_1;

// ---- PATCH CORDS ----

// mixerA (notes 1 & 2)
AudioConnection pc1 (note1a, 0, mixerA, 0);
AudioConnection pc2 (note1b, 0, mixerA, 1);
AudioConnection pc3 (note2a, 0, mixerA, 2);
AudioConnection pc4 (note2b, 0, mixerA, 3);

// mixerB (notes 3 & 4)
AudioConnection pc5 (note3a, 0, mixerB, 0);
AudioConnection pc6 (note3b, 0, mixerB, 1);
AudioConnection pc7 (note4a, 0, mixerB, 2);
AudioConnection pc8 (note4b, 0, mixerB, 3);

// mixerC (notes 5 & 6)
AudioConnection pc9  (note5a, 0, mixerC, 0);
AudioConnection pc10 (note5b, 0, mixerC, 1);
AudioConnection pc11 (note6a, 0, mixerC, 2);
AudioConnection pc12 (note6b, 0, mixerC, 3);

// mixerD (notes 7 & 8)
AudioConnection pc13 (note7a, 0, mixerD, 0);
AudioConnection pc14 (note7b, 0, mixerD, 1);
AudioConnection pc15 (note8a, 0, mixerD, 2);
AudioConnection pc16 (note8b, 0, mixerD, 3);

// Combine A, B, C, D into mixerMaster
AudioConnection pc17 (mixerA, 0, mixerMaster, 0);
AudioConnection pc18 (mixerB, 0, mixerMaster, 1);
AudioConnection pc19 (mixerC, 0, mixerMaster, 2);
AudioConnection pc20 (mixerD, 0, mixerMaster, 3);

// mixerMaster -> I2S output
AudioConnection pc21 (mixerMaster, 0, i2s1, 0);
AudioConnection pc22 (mixerMaster, 0, i2s1, 1);

// ---- SENSOR PINS & FILENAMES ----
const int sensorPins[8] = {2, 3, 4, 5, 6, 7, 8, 9};

// Filenames in ascending order from C3 to C4
const char *noteFiles[8] = {
  "C3.WAV", // sensorPins[0] => pin2
  "D3.WAV", // sensorPins[1] => pin3
  "E3.WAV", // sensorPins[2] => pin4
  "F3.WAV", // sensorPins[3] => pin5
  "G3.WAV", // sensorPins[4] => pin6
  "A3.WAV", // sensorPins[5] => pin7
  "B3.WAV", // sensorPins[6] => pin8
  "C4.WAV"  // sensorPins[7] => pin9
};

// For edge detection
bool oldState[8] = {false, false, false, false, false, false, false, false};

// ---- SETUP ----
void setup() {
  Serial.begin(115200);
  while (!Serial) { /* wait for Serial Monitor */ }

  // Configure TTP223 pins
  for (int i = 0; i < 8; i++) {
    pinMode(sensorPins[i], INPUT);
  }

  // Allocate audio memory (16 voices => at least 32 recommended)
  AudioMemory(32);

  // Enable the SGTL5000 codec
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.8); // range 0.0 to 1.0

  // Mixer gains: each AudioMixer4 has 4 inputs
  // set them all to 1.0 (unity)
  for (int ch = 0; ch < 4; ch++) {
    mixerA.gain(ch, 1.0);
    mixerB.gain(ch, 1.0);
    mixerC.gain(ch, 1.0);
    mixerD.gain(ch, 1.0);
  }
  // Master mixer has 4 inputs (from sub-mixers)
  for (int out = 0; out < 4; out++) {
    mixerMaster.gain(out, 1.0);
  }

  // Initialize SD card
  // If your Audio Shield uses pin 10 for SD card CS, this is correct.
  // If using Teensy 4.0's built-in SD slot, try SD.begin(BUILTIN_SDCARD).
  if (!SD.begin(10)) {
    Serial.println("SD card init failed. Check wiring or card format.");
    while (1) { /* halt */ }
  }
  Serial.println("SD card initialized.");
}

// ---- HELPER: PLAY NOTE (2 VOICES) ----
void playNoteFile(AudioPlaySdWav &voiceA, AudioPlaySdWav &voiceB, const char* fname) {
  if (!voiceA.isPlaying()) {
    voiceA.play(fname);
  } else if (!voiceB.isPlaying()) {
    voiceB.play(fname);
  } else {
    // Both busy => forcibly re-trigger voiceA
    voiceA.stop();
    voiceA.play(fname);
  }
}

// ---- MAIN LOOP ----
void loop() {
  // Check each sensor, do edge detection
  for (int i = 0; i < 8; i++) {
    bool newState = (digitalRead(sensorPins[i]) == HIGH);

    // If it changed from LOW to HIGH, trigger note
    if (!oldState[i] && newState) {
      Serial.print("Sensor ");
      Serial.print(i);
      Serial.print(" => playing ");
      Serial.println(noteFiles[i]);

      // Trigger the correct pair of voices based on sensor index
      switch (i) {
        case 0: playNoteFile(note1a, note1b,  noteFiles[i]); break; // C3
        case 1: playNoteFile(note2a, note2b,  noteFiles[i]); break; // D3
        case 2: playNoteFile(note3a, note3b,  noteFiles[i]); break; // E3
        case 3: playNoteFile(note4a, note4b,  noteFiles[i]); break; // F3
        case 4: playNoteFile(note5a, note5b,  noteFiles[i]); break; // G3
        case 5: playNoteFile(note6a, note6b,  noteFiles[i]); break; // A3
        case 6: playNoteFile(note7a, note7b,  noteFiles[i]); break; // B3
        case 7: playNoteFile(note8a, note8b,  noteFiles[i]); break; // C4
      }
    }
    // Save state
    oldState[i] = newState;
  }
 
  // Brief delay
  delay(10);
}
 
Unfortunately the stock AudioPlaySdWav object struggles to achieve more than a couple of files playing simultaneously. I've put significant effort into an improved suite of playback and recording objects, with an associated discussion thread, which is quite long by now but contains a fair bit of guidance. I'm not sure how useful that will be for someone who is "not a programmer", though :)

You might want to try a synthesized approach, using AudioSynthWaveform and AudioEffectEnvelope to begin with, and expanding your horizons as you get to grips with the Audio library.
 
Unfortunately the stock AudioPlaySdWav object struggles to achieve more than a couple of files playing simultaneously. I've put significant effort into an improved suite of playback and recording objects, with an associated discussion thread, which is quite long by now but contains a fair bit of guidance. I'm not sure how useful that will be for someone who is "not a programmer", though :)

You might want to try a synthesized approach, using AudioSynthWaveform and AudioEffectEnvelope to begin with, and expanding your horizons as you get to grips with the Audio library.
Thank you for your response. I tried the synthesized approach but I did not work probably because I did it wrong. I can play 4 sounds but it does not respond for more sounds. Thank you again!
 
Its usual to post code with the code tags tool (see the </> icon) like this:

Code:
#include <Arduino.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>


// ---- AUDIO OBJECTS (2 voices per note) ----

// Note 1: C3
AudioPlaySdWav note1a;
AudioPlaySdWav note1b;

// Note 2: D3
AudioPlaySdWav note2a;
AudioPlaySdWav note2b;

// Note 3: E3
AudioPlaySdWav note3a;
AudioPlaySdWav note3b;

// Note 4: F3
AudioPlaySdWav note4a;
AudioPlaySdWav note4b;

// Note 5: G3
AudioPlaySdWav note5a;
AudioPlaySdWav note5b;

// Note 6: A3
AudioPlaySdWav note6a;
AudioPlaySdWav note6b;

// Note 7: B3
AudioPlaySdWav note7a;
AudioPlaySdWav note7b;

// Note 8: C4
AudioPlaySdWav note8a;
AudioPlaySdWav note8b;

// ---- 4 sub-mixers (AudioMixer4) for 16 total voices ----
// We'll group them in pairs: (C3,D3), (E3,F3), (G3,A3), (B3,C4).
AudioMixer4 mixerA; // for note1a, note1b, note2a, note2b
AudioMixer4 mixerB; // for note3a, note3b, note4a, note4b
AudioMixer4 mixerC; // for note5a, note5b, note6a, note6b
AudioMixer4 mixerD; // for note7a, note7b, note8a, note8b
AudioMixer4 mixerMaster;

// Output + SGTL5000 control
AudioOutputI2S      i2s1;
AudioControlSGTL5000 sgtl5000_1;

// ---- PATCH CORDS ----

// mixerA (notes 1 & 2)
AudioConnection pc1 (note1a, 0, mixerA, 0);
AudioConnection pc2 (note1b, 0, mixerA, 1);
AudioConnection pc3 (note2a, 0, mixerA, 2);
AudioConnection pc4 (note2b, 0, mixerA, 3);

// mixerB (notes 3 & 4)
AudioConnection pc5 (note3a, 0, mixerB, 0);
AudioConnection pc6 (note3b, 0, mixerB, 1);
AudioConnection pc7 (note4a, 0, mixerB, 2);
AudioConnection pc8 (note4b, 0, mixerB, 3);

// mixerC (notes 5 & 6)
AudioConnection pc9  (note5a, 0, mixerC, 0);
AudioConnection pc10 (note5b, 0, mixerC, 1);
AudioConnection pc11 (note6a, 0, mixerC, 2);
AudioConnection pc12 (note6b, 0, mixerC, 3);

// mixerD (notes 7 & 8)
AudioConnection pc13 (note7a, 0, mixerD, 0);
AudioConnection pc14 (note7b, 0, mixerD, 1);
AudioConnection pc15 (note8a, 0, mixerD, 2);
AudioConnection pc16 (note8b, 0, mixerD, 3);

// Combine A, B, C, D into mixerMaster
AudioConnection pc17 (mixerA, 0, mixerMaster, 0);
AudioConnection pc18 (mixerB, 0, mixerMaster, 1);
AudioConnection pc19 (mixerC, 0, mixerMaster, 2);
AudioConnection pc20 (mixerD, 0, mixerMaster, 3);

// mixerMaster -> I2S output
AudioConnection pc21 (mixerMaster, 0, i2s1, 0);
AudioConnection pc22 (mixerMaster, 0, i2s1, 1);

// ---- SENSOR PINS & FILENAMES ----
const int sensorPins[8] = {2, 3, 4, 5, 6, 7, 8, 9};

// Filenames in ascending order from C3 to C4
const char *noteFiles[8] = {
  "C3.WAV", // sensorPins[0] => pin2
  "D3.WAV", // sensorPins[1] => pin3
  "E3.WAV", // sensorPins[2] => pin4
  "F3.WAV", // sensorPins[3] => pin5
  "G3.WAV", // sensorPins[4] => pin6
  "A3.WAV", // sensorPins[5] => pin7
  "B3.WAV", // sensorPins[6] => pin8
  "C4.WAV"  // sensorPins[7] => pin9
};

// For edge detection
bool oldState[8] = {false, false, false, false, false, false, false, false};

// ---- SETUP ----
void setup() {
  Serial.begin(115200);
  while (!Serial) { /* wait for Serial Monitor */ }

  // Configure TTP223 pins
  for (int i = 0; i < 8; i++) {
    pinMode(sensorPins[i], INPUT);
  }

  // Allocate audio memory (16 voices => at least 32 recommended)
  AudioMemory(32);

  // Enable the SGTL5000 codec
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.8); // range 0.0 to 1.0

  // Mixer gains: each AudioMixer4 has 4 inputs
  // set them all to 1.0 (unity)
  for (int ch = 0; ch < 4; ch++) {
    mixerA.gain(ch, 1.0);
    mixerB.gain(ch, 1.0);
    mixerC.gain(ch, 1.0);
    mixerD.gain(ch, 1.0);
  }
  // Master mixer has 4 inputs (from sub-mixers)
  for (int out = 0; out < 4; out++) {
    mixerMaster.gain(out, 1.0);
  }

  // Initialize SD card
  // If your Audio Shield uses pin 10 for SD card CS, this is correct.
  // If using Teensy 4.0's built-in SD slot, try SD.begin(BUILTIN_SDCARD).
  if (!SD.begin(10)) {
    Serial.println("SD card init failed. Check wiring or card format.");
    while (1) { /* halt */ }
  }
  Serial.println("SD card initialized.");
}

// ---- HELPER: PLAY NOTE (2 VOICES) ----
void playNoteFile(AudioPlaySdWav &voiceA, AudioPlaySdWav &voiceB, const char* fname) {
  if (!voiceA.isPlaying()) {
    voiceA.play(fname);
  } else if (!voiceB.isPlaying()) {
    voiceB.play(fname);
  } else {
    // Both busy => forcibly re-trigger voiceA
    voiceA.stop();
    voiceA.play(fname);
  }
}

// ---- MAIN LOOP ----
void loop() {
  // Check each sensor, do edge detection
  for (int i = 0; i < 8; i++) {
    bool newState = (digitalRead(sensorPins[i]) == HIGH);

    // If it changed from LOW to HIGH, trigger note
    if (!oldState[i] && newState) {
      Serial.print("Sensor ");
      Serial.print(i);
      Serial.print(" => playing ");
      Serial.println(noteFiles[i]);

      // Trigger the correct pair of voices based on sensor index
      switch (i) {
        case 0: playNoteFile(note1a, note1b,  noteFiles[i]); break; // C3
        case 1: playNoteFile(note2a, note2b,  noteFiles[i]); break; // D3
        case 2: playNoteFile(note3a, note3b,  noteFiles[i]); break; // E3
        case 3: playNoteFile(note4a, note4b,  noteFiles[i]); break; // F3
        case 4: playNoteFile(note5a, note5b,  noteFiles[i]); break; // G3
        case 5: playNoteFile(note6a, note6b,  noteFiles[i]); break; // A3
        case 6: playNoteFile(note7a, note7b,  noteFiles[i]); break; // B3
        case 7: playNoteFile(note8a, note8b,  noteFiles[i]); break; // C4
      }
    }
    // Save state
    oldState[i] = newState;
  }
 
  // Brief delay
  delay(10);
}
Thank you!
 
Hi,
I would encourage you to go with the "synth approach" as well. Could you tell us more ?
- do you want to build a monophonic or polyphonic synth ?
- what kind of keyboard do you use ?
- do you want to add a lot of knobs and switches ?
- do you need MIDI ?

A few years ago, I build a simple polyphonic synth for kids workshop. I can help you if you want to build something close to it :

Emmanuel
 
Hi,
I would encourage you to go with the "synth approach" as well. Could you tell us more ?
- do you want to build a monophonic or polyphonic synth ?
- what kind of keyboard do you use ?
- do you want to add a lot of knobs and switches ?
- do you need MIDI ?

A few years ago, I build a simple polyphonic synth for kids workshop. I can help you if you want to build something close to it :

Emmanuel
Hi Emmanuel!
Thank you for your answer. I am trying to build something similar to your device; for kids but with real sounds, like piano, guitar, just in the range of an octave; I think max 8 sounds. I am using 8 capacitive touch sensors, I am not sure about monophonic or polyphonic; I want that kids can press the sensors and play the sound, if they press 3,4,5 at the same time, I want to hear ll of them. I do not want to add nobs just to get something closer to a real instrument.
Congratulations for Synth; it is great!!👌
 
I had good results playing samples with playMem. You have to prepare your samples with wav2sketch. I suggest 16 bits, 22kHz.
The problem is that the memory is going to be full very quickly. This is perfect for a drum kit, with short sounds. But for a big collection of longer samples, it won't work.
If I were you, I would do a synthesizer rather than a samples player, it is so easier. Then I would use wavetable or string (karplusStrong) objects that can produce real instrument sounds (close). With a simple oscillator + envelope + nice reverb you can also produce very nice sounds easlily.

Emmanuel
 
If you
want that kids can press the sensors and play the sound, if they press 3,4,5 at the same time, I want to hear ll of them.
then you want polyphonic.

As @emmanuel63 says, if you're playing real samples then you run short of memory very quickly, which is why streaming from SD starts to look like a good idea. It's definitely feasible, as I said in post #3, but as someone who is "not a programmer I just have a basic knowledge about coding" you really should consider starting out with synthesised sound, IMHO. You don't need knobs if you don't want them, just have the waveforms, envelopes, filters etc. settings decided in your code.

Once you're familiar with the audio system, you can then look into sampled sounds, decide on an approach, and try it out. There are many options: streaming from SD card, loading a sample set from SD card into into PSRAM, using fewer than 8 samples and adjusting playback speed using the TeensyVariablePlayback library, and so on. Each has its merits and drawbacks.

You'll need two sets of hardware - one for your kids, and one for you to develop the next iteration!
 
Back
Top