Mozzi Polyphonic experiment

campidelli
Posts: 2
Joined: Fri Aug 23, 2024 1:05 am

Mozzi Polyphonic experiment

Postby campidelli » Fri Aug 23, 2024 1:16 am

Hey everyone, this is my first post here, thanks!

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.
  1. #include "MozziConfigValues.h"
  2.  
  3. #define MOZZI_AUDIO_MODE   MOZZI_OUTPUT_I2S_DAC
  4. #define MOZZI_I2S_PIN_BCK  26
  5. #define MOZZI_I2S_PIN_WS   25
  6. #define MOZZI_I2S_PIN_DATA 22
  7. #define MOZZI_CONTROL_RATE 256
  8.  
  9. #include <Arduino.h>
  10. #include <Keypad.h>
  11. #include <Mozzi.h>
  12. #include <Oscil.h>
  13. #include <tables/cos8192_int8.h>
  14. #include <mozzi_midi.h>
  15. #include <ADSR.h>
  16.  
  17. // Envelope controllers
  18. #define ATTACK       100
  19. #define DECAY        200
  20. #define SUSTAIN      500 // Reduced sustain time
  21. #define RELEASE      500 // Reduced release time
  22. #define ATTACK_LEVEL 127 // Lower maximum amplitude level
  23. #define DECAY_LEVEL  127 // Lower decay level
  24.  
  25. // SETTINGS
  26. #define OCTAVE        4
  27. #define MAX_POLYPHONY LIST_MAX
  28.  
  29. // Keypad configuration
  30. const byte ROWS = 2;
  31. const byte COLS = 2;
  32. byte key_indexes[ROWS][COLS] = {
  33.   {1, 2},
  34.   {3, 4}
  35. };
  36. byte rowPins[ROWS] = {14, 12};
  37. byte colPins[COLS] = {32, 33};
  38.  
  39. Keypad keypad = Keypad(makeKeymap(key_indexes), rowPins, colPins, ROWS, COLS);
  40.  
  41. // Voices
  42. struct Voice {
  43.     Oscil<COS8192_NUM_CELLS, AUDIO_RATE> osc;
  44.     ADSR<MOZZI_CONTROL_RATE, AUDIO_RATE> env;
  45.     byte note;
  46. };
  47.  
  48. Voice voices[MAX_POLYPHONY];
  49.  
  50. void noteOff(byte note) {
  51.     for (int i = 0; i < MAX_POLYPHONY; i++) {
  52.         if (voices[i].note == note) {
  53.             voices[i].env.noteOff();
  54.             voices[i].note = 0;
  55.             Serial.print("Note Off: ");
  56.             Serial.println(note);
  57.             return;
  58.         }
  59.     }
  60. }
  61.  
  62. void noteOn(byte note) {
  63.     int noteIndex = 0;
  64.     for (; noteIndex < MAX_POLYPHONY; noteIndex++) {
  65.         if (!voices[noteIndex].env.playing()) {
  66.             // This voice is not playing, let's use it then
  67.             break;
  68.         }
  69.         if (voices[noteIndex].note == note) {
  70.             // This note is already playing, ignore
  71.             return;
  72.         }
  73.         if (noteIndex + 1 == MAX_POLYPHONY) {
  74.             // This is the last voice and it is occupied,
  75.             // let's steal the oldest voice (index 0) and reuse it
  76.             noteIndex = 0;
  77.             break;
  78.         }
  79.     }
  80.     voices[noteIndex].note = note;
  81.     voices[noteIndex].osc.setFreq(mtof(note));
  82.     voices[noteIndex].env.noteOn();
  83.     Serial.print("Note On: ");
  84.     Serial.println(note);
  85. }
  86.  
  87. void play() {
  88.     if (keypad.getKeys()) {
  89.         for (int i = 0; i < LIST_MAX; i++) {
  90.             if (keypad.key[i].stateChanged) {
  91.                 byte note = (OCTAVE * 12) + 11 + keypad.key[i].kchar;
  92.                 KeyState state = keypad.key[i].kstate;
  93.                 if (state == PRESSED) {
  94.                     noteOn(note);
  95.                 } else if (state == RELEASED) {
  96.                     noteOff(note);
  97.                 }
  98.             }
  99.         }
  100.     }
  101. }
  102.  
  103. void setup() {
  104.     Serial.begin(115200);
  105.  
  106.     for (int i = 0; i < MAX_POLYPHONY; i++) {
  107.         voices[i].osc.setTable(COS8192_DATA);
  108.         voices[i].env.setADLevels(ATTACK_LEVEL, DECAY_LEVEL);
  109.         voices[i].env.setTimes(ATTACK, DECAY, SUSTAIN, RELEASE);
  110.     }
  111.  
  112.     startMozzi(MOZZI_CONTROL_RATE);
  113. }
  114.  
  115. void updateControl() {
  116.     play();
  117.  
  118.     for (int i = 0; i < MAX_POLYPHONY; i++) {
  119.         voices[i].env.update();
  120.     }
  121. }
  122.  
  123. AudioOutput updateAudio() {
  124.     long currentSample = 0;
  125.     for (unsigned int i = 0; i < MAX_POLYPHONY; i++) {
  126.         if (voices[i].env.playing()) {
  127.             currentSample += voices[i].osc.next() * voices[i].env.next();
  128.         }
  129.     }
  130.     return MonoOutput::fromAlmostNBit(20, currentSample);
  131. }
  132.  
  133. void loop() {
  134.     audioHook();
  135. }
Cheers!

ESP_Sprite
Posts: 9730
Joined: Thu Nov 26, 2015 4:08 am

Re: Mozzi Polyphonic experiment

Postby ESP_Sprite » Fri Aug 23, 2024 1:39 am

Potentially adding the waveforms for the two notes makes the total go out of range for the output... normally that would wrap around, giving an instantly recognizable 'bad sound', but your libraries may clip it instead, making the beat frequency between the two notes appear instead.

Who is online

Users browsing this forum: No registered users and 57 guests