pschatzmann/arduino-audio-tools

Can't achieve polyphony using multiple instruments and a voicer

campidelli opened this issue · 5 comments

Problem Description

Hello Mr. Schatzmann, sorry to bother you, but I am trying to create a simple standalone instrument that is triggered by a keypad.

You can check my code https://github.com/campidelli/arduino-accordion if you want to see the complete implementation.

I am probably using it wrongly, could you please give me some advice?

Here is a footage of the issue happening:
https://www.youtube.com/watch?v=qL75XVQ7Rl0

Device Description

ESP32 + PCM1502 and a PAM8403 amplifier.

Sketch

#include <Arduino.h>
#include "Keyboard.h"
#include "AudioTools.h"
#include "StkAll.h"

// Define constants
const float AMPLITUDE = 64.0;
const int GROUP = 0;

// Create instances of the audio components
I2SStream i2s;
ArdStreamOut output(i2s, 1);
Clarinet clarinet(440); // Create a clarinet instance
Voicer voicer;
Keyboard keyboard;

// Define key press and release handlers
int getNote(int key) {
    return key + 60;
}

void noteOn(int key) {
    int note = getNote(key);

    Serial.print("Note ");
    Serial.print(note);
    Serial.print(" ON at voice ");
    Serial.println(key);

    voicer.noteOn(note, AMPLITUDE, key);
}

void noteOff(int key) {
    int note = getNote(key);

    Serial.print("Note ");
    Serial.print(note);
    Serial.print(" OFF at voice ");
    Serial.println(key);
    voicer.noteOff(note, AMPLITUDE, key);
}

void setup() {
    Serial.begin(115200);

    // Initialize the keyboard
    keyboard.begin();
    keyboard.onKeyPress(noteOn);
    keyboard.onKeyRelease(noteOff);

    // Add the clarinets to the voicer
    for (int i = 0; i < keyboard.getTotalKeys(); i++) {
        voicer.addInstrument(&clarinet, i);
    }

    // Configure the audio output
    auto cfg = i2s.defaultConfig(TX_MODE);
    cfg.bits_per_sample = 16;
    cfg.sample_rate = Stk::sampleRate();
    cfg.channels = 1;
    cfg.pin_bck = 26;
    cfg.pin_ws = 25;
    cfg.pin_data = 22;
    i2s.begin(cfg);
}

void loop() {
    // Update keyboard and process audio
    keyboard.update();
    output.tick(voicer.tick());
}

Other Steps to Reproduce

No response

What is your development environment

PlatformIO

I have checked existing issues, discussions and online documentation

  • I confirm I have checked existing issues, discussions and online documentation

I bet your Keyboard class is the source of trouble and adds to much delay: you call it way too often..

Have a look at this example. If you dont have an AudioKit you can replace it with I2SStream and for the actions you can use the AudioActions class.

Hi there, thanks for the prompt response.

I had a look at the example and what I got from it is that AudioActions would be ideal if I had one key per pin (or one pin per key), but in my case, it has to be a matrix because I intend to have 72 keys on the bass plus 32 on the treble (it is an accordion).

How could I circumvent that? How would you implement an e-piano for example?

Thanks!

Just have one more look at the sketch how I process e.g. 1k of audio samples for each input from the keyboard.
You need to experiment a bit to find your sweet spot...

Hello Mr. Schatzmann, good news! I combined your approach of processing 1k audio samples and created a task pinned to core 0 to process the keyboard input, it works!

Now, what if I want to write my own instrument? The goal is to create an accordion, like this https://www.youtube.com/watch?v=3mif1_ttTgk

The options are:

  • Extending Instrmnt.h and create the Accordion.h;
  • Use the Synthesizer.h from arduino-audio-tools and not use arduino-stk anymore (I understand you kind of "deprecated" it in favour of the arduino-audio-tools, am I right?)
  • Synthesize the sounds using SoundGenerator.h and other classes from arduino-audio-tools. According to the video I showed you, I need to be able to reduce the spectrum of a waveform and apply a comb filter to it. Does your library support this sort of stuff?

Anyway, I am pretty noob in C++, Synths, microcontrollers, etc. But I will get there. Thanks for your help and here is the sketch I have that is working with the Clarinet.h:

#include <Arduino.h>
#include "Keyboard.h"
#include "AudioTools.h"
#include "StkAll.h"

// Define constants
const float AMPLITUDE = 16.0;
const int GROUP = 0;

// Create instances of the audio components
I2SStream i2s;
ArdStreamOut output(i2s, 1);
Clarinet clarinet(440); // Create a clarinet instance
Voicer voicer;
Keyboard keyboard;

// Define key press and release handlers
int getNote(int key) {
    return key + 60;
}

void noteOn(int key) {
    int note = getNote(key);
    voicer.noteOn(note, AMPLITUDE, GROUP);
}

void noteOff(int key) {
    int note = getNote(key);
    voicer.noteOff(note, AMPLITUDE, GROUP);
}

void keyboardTask(void *pvParameters) {
    while (true) {
        keyboard.update();
        vTaskDelay(10 / portTICK_PERIOD_MS);
    }
}

void setup() {
    Serial.begin(115200);

    // Add the clarinet to the voicer
    voicer.addInstrument(&clarinet, GROUP);

    // Configure the audio output
    auto cfg = i2s.defaultConfig(TX_MODE);
    cfg.bits_per_sample = 16;
    cfg.sample_rate = Stk::sampleRate();
    cfg.channels = 1;
    cfg.pin_bck = 26;
    cfg.pin_ws = 25;
    cfg.pin_data = 22;
    i2s.begin(cfg);

    // Initialize the keyboard
    keyboard.begin();
    keyboard.onKeyPress(noteOn);
    keyboard.onKeyRelease(noteOff);

    // Pin the keyboard scan task to a core 0
    xTaskCreatePinnedToCore(keyboardTask, "Keyboard Task", 4096, NULL, 1, NULL, 0);
}

void loop() {
  for (int i = 0; i < 1024; i++) {
    output.tick(voicer.tick());
  }
}

You can consider the audio tools library as the preferred output library for the STK framework: So, STK is not deprecated and it is still interesting to use the existing instruments and provided potentially new instruments.

Currently the AudioTools does not provide any comb filter, so feel free to provide some additional functionality via a pull request.