shorepine / amy

AMY - A high-performance fixed-point Music synthesizer librarY for microcontrollers
https://shorepine.github.io/amy/
MIT License
213 stars 12 forks source link

Help to make the ESP32 dual core example to work using PlatformIO #204

Closed campidelli closed 2 months ago

campidelli commented 2 months ago

Hi everyone, thanks for this amazing library!

I am trying to make this example https://github.com/shorepine/amy/blob/main/examples/AMY_Test_ESP32_DualCore/AMY_Test_ESP32_DualCore.ino to run using the PlatformIO on VSCode.

It works just fine on Arduino IDE, but I need to make it work on PlatformIO. The main issue is that PlatformIO doesn't support the version 3 of the arduino-esp32 yet, so I can't use the class:

#include <ESP_I2S.h>
I2SClass I2S;

So I am trying to use the old I2S library but it is not working, I keep getting the error:

Guru Meditation Error: Core  1 panic'ed (StoreProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x400dc152  PS      : 0x00060d30  A0      : 0x800d1bab  A1      : 0x3ffe6600  
A2      : 0x00000000  A3      : 0x3ffbc130  A4      : 0x00000200  A5      : 0x00000000  
A6      : 0xffffffff  A7      : 0x00000000  A8      : 0x00000000  A9      : 0x3ffbc32c  
A10     : 0x000000cc  A11     : 0x00000100  A12     : 0x00000100  A13     : 0x00000001  
A14     : 0x00000001  A15     : 0x3ffde26c  SAR     : 0x00000000  EXCCAUSE: 0x0000001d  
EXCVADDR: 0x00000000  LBEG    : 0x400d3b99  LEND    : 0x400d3bf1  LCOUNT  : 0x00000000  

Backtrace: 0x400dc14f:0x3ffe6600 0x400d1ba8:0x3ffe6640

ELF file SHA256: 0cd3803e2b827bab

Rebooting...

Here is my sketch, I hope someone cal help me. Thanks!

#include <AMY-Arduino.h>
#include <driver/i2s.h>

#define I2S_BCLK      26 
#define I2S_LRCLK     25
#define I2S_DIN       22
#define BUFFER_LENGTH AMY_BLOCK_SIZE * AMY_NCHANS * BYTES_PER_SAMPLE

AMY amy;

// mutex that locks writes to the delta queue
SemaphoreHandle_t xQueueSemaphore;

// Task handles 
TaskHandle_t amy_render_handle;
TaskHandle_t amy_fill_buffer_handle;

void initI2S() {
    i2s_config_t i2s_config = { 
        .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
        .sample_rate = AMY_SAMPLE_RATE,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
        .communication_format = I2S_COMM_FORMAT_STAND_I2S,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
        .dma_buf_count = 16,
        .dma_buf_len = BUFFER_LENGTH
    };
    i2s_pin_config_t pin_config = {
        .bck_io_num = I2S_BCLK,
        .ws_io_num = I2S_LRCLK,
        .data_out_num = I2S_DIN,
        .data_in_num = I2S_PIN_NO_CHANGE
    };

    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    i2s_set_pin(I2S_NUM_0, &pin_config);
}

// Play 10 Juno-6 notes 
void polyphony(uint32_t start, uint16_t patch) {
    struct event e = amy.default_event();
    e.time = start;
    e.load_patch = patch;
    strcpy(e.voices, "0,1,2,3,4,5,6,7,8,9");
    amy.add_event(e);
    start += 250;
    uint8_t note = 40;
    for(uint8_t i=0;i<10;i++) {
        e = amy.default_event();
        e.time = start;
        e.velocity=0.5;
        sprintf(e.voices, "%d", i);
        e.midi_note = note;
        amy.add_event(e);
        start += 1000;
        note += 2;
    }
}   

// Render the second core
void* esp_render_task(void *) {
    while(1) {
        // Wait for a message that it's time to start rendering
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        amy_render(0, AMY_OSCS/2, 1);
        // Tell the other core we're done rendering
        xTaskNotifyGive(amy_fill_buffer_handle);
    }
}

void* esp_fill_audio_buffer_task(void *param) {
    while(1) {
        // Get ready to render
        amy_prepare_buffer();
        // Tell the other core to start rendering
        xTaskNotifyGive(amy_render_handle);
        // Render me
        amy_render(AMY_OSCS / 2, AMY_OSCS, 0);
        // Wait for the other core to finish
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

        // Write to i2s
        int16_t *block = amy_fill_buffer();
        esp_err_t err = i2s_write(I2S_NUM_0, (const char *)block, BUFFER_LENGTH, NULL, portMAX_DELAY);
        if (err != ESP_OK) {
            Serial.printf("Error writing to I2S: %s\n", esp_err_to_name(err));
        }
    }
}

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

  // Set your I2S pins. 
  initI2S();

  // Start up AMY
  amy.begin(/* cores= */ 2, /* reverb= */ 1, /* chorus= */ 1);

  // We create a mutex for changing the event queue and pointers as two tasks do it at once
  xQueueSemaphore = xSemaphoreCreateMutex();

  // Create the second core rendering task
  xTaskCreatePinnedToCore(
    (TaskFunction_t) esp_render_task,
    "renderAudio",
    8192,
    NULL,
    configMAX_PRIORITIES - 1,
    &amy_render_handle,
    0
  );

  // Wait for the render tasks to get going before starting the i2s task
  delay(100);

  // And the fill audio buffer thread, combines, does volume & filters
  xTaskCreatePinnedToCore(
    (TaskFunction_t) esp_fill_audio_buffer_task,
    "fillAudioBuffer",
    8192,
    NULL,
    configMAX_PRIORITIES - 1,
    &amy_fill_buffer_handle,
    1);

  amy_reset_oscs();
  polyphony(amy.sysclock(), 0);
}

void loop() {
  // nothing -- the audio is rendered in the esp_fill_audio_buffer_task
}
RocketManRC commented 2 months ago

@campidelli I was able to get both esp32 examples to work by adding the following 3 lines to platformio.ini:

platform_packages= framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.4 framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.4/esp32-arduino-libs-3.0.4.zip

You will probably have to delete the .platformio directory in your home directory and the .pio directory in your project to get it to build. This may cause issues with your other projects that use the espressif32 platform unless you explicitly state the platform version in those projects. In my case this is platform = espressif32@6.0.1 instead of platform = espressif32. In any case you could always delete the .platformio directory before opening your other projects if you need to.

I hope this helps and doesn't cause more problems for you than it solves!

campidelli commented 2 months ago

Thanks a lot, @RocketManRC, that worked beautifully! It is hard to go back to Arduino IDE after using VS Code hehe.

If you are curious, this is my code https://github.com/campidelli/arduino-accordion/tree/amy

Cheers!

RocketManRC commented 2 months ago

Glad to hear you had success @campidelli and I will check out your project tomorrow!

I have so many projects with different microcontrollers and libraries that I couldn't survive anymore without PlatformIO.

bwhitman commented 2 months ago

This is great, thank you @RocketManRC . Is there something we can add to the AMY repository to make it easier for people using PlatformIO?

RocketManRC commented 2 months ago

@bwhitman Yes for sure Brian and I will be happy to work on that. I'm not a PlatformIO expert but I know where to look for answers :-)

campidelli commented 2 months ago

Hey @bwhitman, if you want to check what I have done so far, this maybe a good example to add to the ESP32 dual core examples: https://hackaday.io/project/197399-arduino-esp32-standalone-accordion/log/232631-found-the-synth-library-amy

Cheers!

znmeb commented 2 months ago

+1 for PlatformIO - I'm testing it out on RP2350s at the moment. Currently it's just Forth written in C though.