grame-cncm / faust

Functional programming language for signal processing and sound synthesis
http://faust.grame.fr
Other
2.57k stars 322 forks source link

faust on LyraT esp32. faust out of scope when setParamValue is used in a task / function. #499

Closed mrSilkie closed 4 years ago

mrSilkie commented 4 years ago

Hi there. I've had the lyrat for less than a week but I've managed to get some beeps n boops out of it using faust. Just to preface, this is essentially my introduction into embedded systems. I haven't programmed outside of arduino before and the work i've done within arduino has been relatively simple. I'm still wrapping my head around the task concepts and also working with faust

The big issue I have atm is that if I declare FaustDSP faust(SR,BS); in main() or even, outside, as a global 'variable' ( I think it's a class but I'm not sure how to reference a c++ class in c). None of my functions have access. The only solution I have that's worked is to have a task, faustTask that contains faust.start(). It worked by receiving inputs via xQueueReceive and the latency was high. This makes it rather impracticable to actually interact with the DSP as if i wanted to have an accelerometer controller, it would have to embedded into my faustTask. Also, since faust.start starts its own task, the stack size of faustTask has been 'experimentally' shown to impact the reliability of faust.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h" //https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "driver/gpio.h" // https://demo-dijiudu.readthedocs.io/en/latest/api-reference/peripherals/gpio.html
                         // http://www.lucadentella.it/en/2017/02/07/esp32-9-basic-io/

#include "../faust_includes/drumtest.h"
#include "../faust_includes/drumtest.cpp"
#include "es8388.h"
#include "es8388.cpp"

#define GPIO_INPUT_PLAY     (gpio_num_t)33
#define GPIO_INPUT_SET      (gpio_num_t)32
#define GPIO_INPUT_VOL1     (gpio_num_t)13
#define GPIO_INPUT_VOL2     (gpio_num_t)27
#define GPIO_INPUT_REC      (gpio_num_t)36 //REC?
#define GPIO_INPUT_MODE     (gpio_num_t)39 //MODE
#define BLINK_GPIO          (gpio_num_t)22

// drumtest drum(48000,8); < --- where I think drum should be declared

extern "C" {
    void app_main(void);
}

void pinSetup() {
    gpio_pad_select_gpio(BLINK_GPIO);
    gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
    gpio_set_level(BLINK_GPIO, 0);

    gpio_pad_select_gpio(GPIO_INPUT_PLAY);
    gpio_set_direction(GPIO_INPUT_PLAY, GPIO_MODE_INPUT);
    gpio_set_level(GPIO_INPUT_PLAY, 0);

    gpio_pad_select_gpio(GPIO_INPUT_SET);
    gpio_set_direction(GPIO_INPUT_SET, GPIO_MODE_INPUT);
    gpio_set_level(GPIO_INPUT_SET, 0);

}

void myUART(void * pvParameters)
{
    uint8_t* data = (uint8_t*) malloc(200);

    while (1){

        // Constantly check the length of the buffer
        int len = uart_read_bytes(UART_NUM_0, data, 3, 10 / portTICK_RATE_MS);
        // If we got our 3 bytes then len will not be 1.
        if(len != 0) {
            switch((int)data[0]) {

                case 144 : 
                gpio_set_level(BLINK_GPIO, 1);
                drum.setParamValue("gate",1); // <--- drum out of scope
                //drum.setParamValue("gate4", 1);
                uart_flush_input(UART_NUM_0);
                break;

                case 128 :
                gpio_set_level(BLINK_GPIO, 0);
                drum.setParamValue("gate",0);
                //drum.setParamValue("gate4", 0);
                uart_flush_input(UART_NUM_0);
                break;

                case 176 :
                //gpio_set_level(BLINK_GPIO, 1); 
                break;

                default :
                uart_flush_input(UART_NUM_0);
                //gpio_set_level(BLINK_GPIO, 1); 
                break;
            }           
        } else {
            gpio_set_level(BLINK_GPIO, 0);
        }       
        vTaskDelay(1);
    }

}

void app_main()
{

    pinSetup();

    /*Configuration options for the es8388*/
    audio_hal_codec_config_t config; 
    config.adc_input = AUDIO_HAL_ADC_INPUT_ALL; 
    config.dac_output = AUDIO_HAL_DAC_OUTPUT_ALL; 
    config.codec_mode = AUDIO_HAL_CODEC_MODE_BOTH; 
    config.i2s_iface.samples = AUDIO_HAL_48K_SAMPLES; 
    config.i2s_iface.bits = AUDIO_HAL_BIT_LENGTH_32BITS; 
    es8388 codec;
    codec.es8388_init(&config);
    codec.es8388_config_i2s(config.codec_mode, &config.i2s_iface); 
    audio_hal_ctrl_t state = AUDIO_HAL_CTRL_START; 
    codec.es8388_ctrl_state(config.codec_mode, state); 

    /* control functions */
    codec.es8388_set_voice_volume(40); 
    codec.es8388_set_voice_mute(false); 
    codec.es8388_set_mic_gain(MIC_GAIN_12DB); 

    /* read functions */
    codec.es8388_read_all(); //print out all the register values in order to serial

    drumtest drum(48000,8); //  <--- Where drum NEEDS to be declared
    drum.start();

    BaseType_t task_2 = xTaskCreatePinnedToCore (
                myUART,             // Function that implements the task. 
                "uart",             // Text name for the task. 
                3000,               // Stack size in words, not bytes. 
                ( void * ) 1,       // Parameter passed into the task.
                10,                 // Priority at which the task is created. 
                NULL,               // Used to pass out the created task's handle. 
                0 );

    for( ;; ){
        if(gpio_get_level(GPIO_INPUT_PLAY) || gpio_get_level(GPIO_INPUT_SET) ){ 
            //if no sound, try power cycling since we never close the audio decoder
            gpio_set_level(BLINK_GPIO, 1);
            drum.setParamValue("gate", 1);
        } else {
            drum.setParamValue("gate", 0);
        }
    }   

I've commented where faust is in scope / out of scope. I've set faust to run on cpu 1, therefore myUART task runs on cpu 0. This actually fixed some bugs for me. myUART is a ghetto midi function since I'm using an arduino as a midi controller and this way I've been able to hone in on what I'm sending and receiving. I've actually got the UART part working pretty well and am almost finished with my 50+ hour project. The final piece that I am stuck on is simply getting fuast in to scope across all my tasks. I'd love to break my tasks down / include more but without a means providing scope this isn't possible. I have got myUART to interface with faust by embedding faust into a myFaust task and using xQueue to transmit commands between. This method worked but I was met with constant crashing and high input latency.

I'm not the best at coding and am still learning so would also love some feedback / a potential fix to my scope issue.

sletz commented 4 years ago

You can pass the drumtest object as a parameter at task creation time (xTaskCreatePinnedToCore) to be received in the pvParameters, and declare drumtest drum(48000,8); as global.

So something like: xTaskCreatePinnedToCore(myUART, "uart", 3000, (void*)&drum, 10, NULL, 0); and in void myUART(void* pvParameters) drumtest* obj = (drumtest*)pvParameters; then you can do obj->setParamValue("gate",0); in the task.

mrSilkie commented 4 years ago

Hey there Steph.

Thanks for your reply, I got my code working with that little snippet you shared. Unfortunately, I don't think I have the resources available to run another task as Faust seems to crash with just a single oscillator. Instead I found embedding the UART / Button code inside a loop within the main function to work.

sletz commented 4 years ago

" Faust seems to crash with just a single oscillator. " this is probably something else happening, we now for sure that more complex Faust DSP can run on the esp32.