Page 1 of 1

How to Share an I2S Initialization Function Between ESP-ADF and ESP-Skainet Frameworks?

Posted: Wed Dec 25, 2024 8:38 pm
by sleepy
hello,

i am using ESP Skainet framework to use ESP Wakenet for the wake word detection on the esp32 s3, and also the ESP ADF framework (audio developement framework) to convert audio to text with a google cloud api and ESP ADF to make it easier to manage the http stream and everything.

Both the two functionalities works fine when used separatly, but the problems happend when combining both.

The problem is : the ESP Skainet framework is trying to create the i2s channel on the i2s microphone which works fine but also the ESP ADF framework is trying to create the i2s channel on the same microphone which leads to errors because the i2s channel is already in use by ESP Skainet.

here is the function that initialize the i2s driver on the ESP Skainet framework :

Code: Select all

static esp_err_t bsp_i2s_init(i2s_port_t i2s_num, uint32_t sample_rate, int channel_format, int bits_per_chan)
{
    esp_err_t ret_val = ESP_OK;

#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(i2s_num, I2S_ROLE_MASTER);

    ret_val |= i2s_new_channel(&chan_cfg, NULL, &rx_handle);
    i2s_std_config_t std_cfg = I2S_CONFIG_DEFAULT(16000, I2S_SLOT_MODE_MONO, 32);
    std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;
    // std_cfg.clk_cfg.mclk_multiple = EXAMPLE_MCLK_MULTIPLE;   //The default is I2S_MCLK_MULTIPLE_256. If not using 24-bit data width, 256 should be enough
    ret_val |= i2s_channel_init_std_mode(rx_handle, &std_cfg);
    ret_val |= i2s_channel_enable(rx_handle);
#else
    // i2s_config_t i2s_config = I2S_CONFIG_DEFAULT(16000, I2S_CHANNEL_FMT_ONLY_LEFT, 32);
    i2s_config_t i2s_config = I2S_CONFIG_DEFAULT(sample_rate, I2S_CHANNEL_FMT_ONLY_LEFT, bits_per_chan);

    i2s_pin_config_t pin_config = {
        .bck_io_num = GPIO_I2S_SCLK,
        .ws_io_num = GPIO_I2S_LRCK,
        .data_out_num = GPIO_I2S_DOUT,
        .data_in_num = GPIO_I2S_SDIN,
        .mck_io_num = GPIO_I2S_MCLK,
    };

    ret_val |= i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
    ret_val |= i2s_set_pin(i2s_num, &pin_config);
#endif
[Codebox=c file=Untitled.c][/Codebox]
    return ret_val;
}
And here is the function that initialize the i2s driver in the esp adf framework :

Code: Select all

audio_element_handle_t i2s_stream_init(i2s_stream_cfg_t *config)
{
    audio_element_cfg_t cfg = DEFAULT_AUDIO_ELEMENT_CONFIG();
    audio_element_handle_t el = NULL;
    cfg.open = _i2s_open;
    cfg.close = _i2s_close;
    cfg.process = _i2s_process;
    cfg.destroy = _i2s_destroy;
    cfg.task_stack = config->task_stack;
    cfg.task_prio = config->task_prio;
    cfg.task_core = config->task_core;
    cfg.stack_in_ext = config->stack_in_ext;
    cfg.out_rb_size = config->out_rb_size;
    cfg.multi_out_rb_num = config->multi_out_num;
    cfg.tag = "iis";
    config->buffer_len = config->buffer_len > 0 ? config->buffer_len : I2S_STREAM_BUF_SIZE;
    cfg.buffer_len = config->buffer_len;
    if (cfg.buffer_len % I2S_BUFFER_ALINED_BYTES_SIZE) {
        ESP_LOGE(TAG, "The size of buffer must be a multiple of %d, current size is %d", I2S_BUFFER_ALINED_BYTES_SIZE, cfg.buffer_len);
        return NULL;
    }
    int i2s_port = config->chan_cfg.id;
    i2s_dir_t i2s_dir = 0;
    if (i2s_port == I2S_NUM_AUTO) {
        ESP_LOGE(TAG, "Please specify the port number of I2S.");
        return NULL;
    }
    i2s_stream_t *i2s = audio_calloc(1, sizeof(i2s_stream_t));
    AUDIO_MEM_CHECK(TAG, i2s, return NULL);

    i2s->type = config->type;
    i2s->use_alc = config->use_alc;
    i2s->volume = config->volume;
    i2s->uninstall_drv = config->uninstall_drv;
    i2s->port = i2s_port;
    i2s->buffer_length = config->buffer_len;
    if (config->type == AUDIO_STREAM_READER) {
        cfg.read = _i2s_read;
        i2s_dir = I2S_DIR_RX;
    } else if (config->type == AUDIO_STREAM_WRITER) {
        cfg.write = _i2s_write;
        i2s_dir = I2S_DIR_TX;
    }

#if (!defined (CONFIG_IDF_TARGET_ESP32) && !defined (CONFIG_IDF_TARGET_ESP32S2))
    if (config->need_expand && config->transmit_mode == I2S_COMM_MODE_STD) {
        config->std_cfg.slot_cfg.slot_bit_width = config->std_cfg.slot_cfg.data_bit_width;
        config->std_cfg.slot_cfg.data_bit_width = config->expand_src_bits;
    }
#endif // (defined (CONFIG_IDF_TARGET_ESP32) && defined(CONFIG_IDF_TARGET_ESP32S2))
    memcpy(&i2s->config, config, sizeof(i2s_stream_cfg_t));

    el = audio_element_init(&cfg);
    AUDIO_MEM_CHECK(TAG, el, {
        audio_free(i2s);
        return NULL;
    });
    audio_element_setdata(el, i2s);

    i2s_stream_setup_music_info(el, i2s);
    /* backup i2s configure */
    i2s_config_backup(&i2s->config);

    // In order to be compatible with esp32 and esp32s2, the Tx and TRx of i2s channel use the same controller.
    // However, there is a problem that if RX channel is enabled and then TX channel is enabled, RX channel needs to be turned off first,
    // which will cause it to be turned off during rx operation. Therefore, mutex is used for protection and will never be released
    if (i2s_key_slot[i2s_port].i2s_refcount > 0) {
        i2s_safe_lock_create(s_i2s_tx_mutex[i2s_port]);
        i2s_safe_lock_create(s_i2s_rx_mutex[i2s_port]);
    }
    i2s_safe_lock(s_i2s_tx_mutex[i2s_port]);
    i2s_safe_lock(s_i2s_rx_mutex[i2s_port]);
    if (i2s_key_slot[i2s_port].dir) {
        if (i2s_key_slot[i2s_port].dir & i2s_dir) {
            ESP_LOGW(TAG, "I2S(%d) already startup", i2s_dir);
            goto i2s_end;
        } else {
            i2s_driver_cleanup(i2s, false);
        }
    }
    i2s_key_slot[i2s_port].dir |= i2s_dir;

    if (i2s_driver_startup(el, &i2s->config) != ESP_OK) {
        ESP_LOGE(TAG, "I2S stream init failed");
        i2s_safe_unlock(s_i2s_tx_mutex[i2s_port]);
        i2s_safe_unlock(s_i2s_rx_mutex[i2s_port]);
        return NULL;
    }
    if (i2s_key_slot[i2s_port].dir & I2S_DIR_TX) {
        i2s_channel_enable(i2s_key_slot[i2s_port].tx_handle);
    }
    if (i2s_key_slot[i2s_port].dir & I2S_DIR_RX) {
        i2s_channel_enable(i2s_key_slot[i2s_port].rx_handle);
    }

i2s_end:
    i2s_safe_unlock(s_i2s_tx_mutex[i2s_port]);
    i2s_safe_unlock(s_i2s_rx_mutex[i2s_port]);
    i2s_key_slot[i2s_port].i2s_refcount++;
    return el;
}
the problem is that the two functions are in a separate frameworks in separate files and i can not combine them to make them create the i2s driver only one time for the two frameworks and share the same handler.