I am working on a little experiment to create a standalone instrument (a melodica, and eventually an accordion) using an ESP32 board, a PCM5102 DAC and a PAM8403 amplifier.
I managed to make it work, as you can see in the video below:
https://www.youtube.com/watch?v=rba7DDyHNXI
The problem is, as you can notice at the end of the video when I play more than one note at the same time, the sound is weird, it is like it is oscillating. I don't know if it is because of my lack of knowledge of synthesizers, ESP32 concurrency, or both
Anyway, this is my sketch, I hope you can give me a tip about this.
- #include "MozziConfigValues.h"
- #define MOZZI_AUDIO_MODE MOZZI_OUTPUT_I2S_DAC
- #define MOZZI_I2S_PIN_BCK 26
- #define MOZZI_I2S_PIN_WS 25
- #define MOZZI_I2S_PIN_DATA 22
- #define MOZZI_CONTROL_RATE 256
- #include <Arduino.h>
- #include <Keypad.h>
- #include <Mozzi.h>
- #include <Oscil.h>
- #include <tables/cos8192_int8.h>
- #include <mozzi_midi.h>
- #include <ADSR.h>
- // Envelope controllers
- #define ATTACK 100
- #define DECAY 200
- #define SUSTAIN 500 // Reduced sustain time
- #define RELEASE 500 // Reduced release time
- #define ATTACK_LEVEL 127 // Lower maximum amplitude level
- #define DECAY_LEVEL 127 // Lower decay level
- // SETTINGS
- #define OCTAVE 4
- #define MAX_POLYPHONY LIST_MAX
- // Keypad configuration
- const byte ROWS = 2;
- const byte COLS = 2;
- byte key_indexes[ROWS][COLS] = {
- {1, 2},
- {3, 4}
- };
- byte rowPins[ROWS] = {14, 12};
- byte colPins[COLS] = {32, 33};
- Keypad keypad = Keypad(makeKeymap(key_indexes), rowPins, colPins, ROWS, COLS);
- // Voices
- struct Voice {
- Oscil<COS8192_NUM_CELLS, AUDIO_RATE> osc;
- ADSR<MOZZI_CONTROL_RATE, AUDIO_RATE> env;
- byte note;
- };
- Voice voices[MAX_POLYPHONY];
- void noteOff(byte note) {
- for (int i = 0; i < MAX_POLYPHONY; i++) {
- if (voices[i].note == note) {
- voices[i].env.noteOff();
- voices[i].note = 0;
- Serial.print("Note Off: ");
- Serial.println(note);
- return;
- }
- }
- }
- void noteOn(byte note) {
- int noteIndex = 0;
- for (; noteIndex < MAX_POLYPHONY; noteIndex++) {
- if (!voices[noteIndex].env.playing()) {
- // This voice is not playing, let's use it then
- break;
- }
- if (voices[noteIndex].note == note) {
- // This note is already playing, ignore
- return;
- }
- if (noteIndex + 1 == MAX_POLYPHONY) {
- // This is the last voice and it is occupied,
- // let's steal the oldest voice (index 0) and reuse it
- noteIndex = 0;
- break;
- }
- }
- voices[noteIndex].note = note;
- voices[noteIndex].osc.setFreq(mtof(note));
- voices[noteIndex].env.noteOn();
- Serial.print("Note On: ");
- Serial.println(note);
- }
- void play() {
- if (keypad.getKeys()) {
- for (int i = 0; i < LIST_MAX; i++) {
- if (keypad.key[i].stateChanged) {
- byte note = (OCTAVE * 12) + 11 + keypad.key[i].kchar;
- KeyState state = keypad.key[i].kstate;
- if (state == PRESSED) {
- noteOn(note);
- } else if (state == RELEASED) {
- noteOff(note);
- }
- }
- }
- }
- }
- void setup() {
- Serial.begin(115200);
- for (int i = 0; i < MAX_POLYPHONY; i++) {
- voices[i].osc.setTable(COS8192_DATA);
- voices[i].env.setADLevels(ATTACK_LEVEL, DECAY_LEVEL);
- voices[i].env.setTimes(ATTACK, DECAY, SUSTAIN, RELEASE);
- }
- startMozzi(MOZZI_CONTROL_RATE);
- }
- void updateControl() {
- play();
- for (int i = 0; i < MAX_POLYPHONY; i++) {
- voices[i].env.update();
- }
- }
- AudioOutput updateAudio() {
- long currentSample = 0;
- for (unsigned int i = 0; i < MAX_POLYPHONY; i++) {
- if (voices[i].env.playing()) {
- currentSample += voices[i].osc.next() * voices[i].env.next();
- }
- }
- return MonoOutput::fromAlmostNBit(20, currentSample);
- }
- void loop() {
- audioHook();
- }