WhyCan Forum

本站无需注册,无需积分,无需回复可下载所有资料,如果真的喜欢小站,请您注册之后请至少回复一个帖子激活Id,谢谢支持! 站长QQ: 516333132 (挖坑网/填坑网) admin@whycan.cn

您尚未登录。

#1 2018-11-23 10:07:55

dy_xie
会员
注册时间: 2018-11-23
累计积分: 7

esp32+8388边录边播问题

刚刚接触esp32,想要实现边录边播,想法是通过两个I2S,分别控制录音和播放。
我的代码实现如下:
公共部分的初始化:

    esp_log_level_set("*", ESP_LOG_WARN);
    esp_log_level_set(TAG, ESP_LOG_INFO);
 
    esp_periph_config_t periph_cfg = { 0 };
    esp_periph_init(&periph_cfg);
 
    periph_sdcard_cfg_t sdcard_cfg = {
        .root = "/sdcard",
        .card_detect_pin = SD_CARD_INTR_GPIO, //GPIO_NUM_34
    };
    esp_periph_handle_t sdcard_handle = periph_sdcard_init(&sdcard_cfg);
    esp_periph_start(sdcard_handle);
   
    while (!periph_sdcard_is_mounted(sdcard_handle)) {
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
 
    periph_touch_cfg_t touch_cfg = {
        .touch_mask = TOUCH_SEL_SET | TOUCH_SEL_PLAY | TOUCH_SEL_VOLUP | TOUCH_SEL_VOLDWN,
        .tap_threshold_percent = 70,
    };
    touch_periph = periph_touch_init(&touch_cfg);
 
    esp_periph_start(touch_periph);
   
    audio_hal_codec_config_t audio_hal_codec_cfg =  AUDIO_HAL_ES8388_DEFAULT();
    audio_hal_codec_cfg.i2s_iface.samples = AUDIO_HAL_44K_SAMPLES;
    hal = audio_hal_init(&audio_hal_codec_cfg, 0);
    audio_hal_ctrl_codec(hal, AUDIO_HAL_CODEC_MODE_BOTH, AUDIO_HAL_CTRL_START);

播放部分的初始化:

    audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
    pipeline_mp3 = audio_pipeline_init(&pipeline_cfg);
    mem_assert(pipeline_mp3);
 
    i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
    i2s_cfg.i2s_port = I2S_NUM_0;
    i2s_cfg.type = AUDIO_STREAM_WRITER;
    i2s_stream_writer = i2s_stream_init(&i2s_cfg);
 
    mp3_decoder_cfg_t mp3_cfg = DEFAULT_MP3_DECODER_CONFIG();
    mp3_decoder = mp3_decoder_init(&mp3_cfg);
    audio_element_set_read_cb(mp3_decoder, my_sdcard_read_cb, NULL);
 
    audio_pipeline_register(pipeline_mp3, mp3_decoder, "mp3");
    audio_pipeline_register(pipeline_mp3, i2s_stream_writer, "i2s_mp3");
 
    ESP_LOGI(TAG, "Link it together [my_sdcard_read_cb]-->mp3_decoder-->i2s_stream-->[codec_chip]");
    audio_pipeline_link(pipeline_mp3, (const char *[]) {"mp3", "i2s_mp3"}, 2);
 
    audio_event_iface_cfg_t evt_mp3_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
    evt_mp3 = audio_event_iface_init(&evt_mp3_cfg);
 
    audio_pipeline_set_listener(pipeline_mp3, evt_mp3);
    audio_event_iface_set_listener(esp_periph_get_event_iface(), evt_mp3);

录音部分的初始化:

    audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
    pipeline_rec = audio_pipeline_init(&pipeline_cfg);
    mem_assert(pipeline_rec);
   
    fatfs_stream_cfg_t fatfs_cfg = FATFS_STREAM_CFG_DEFAULT();
    fatfs_cfg.type = AUDIO_STREAM_WRITER;
    fatfs_cfg.task_core = 1;
    fatfs_stream_writer = fatfs_stream_init(&fatfs_cfg);
 
    i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
 
    i2s_cfg.i2s_port = I2S_NUM_1;
    i2s_cfg.task_core = 1;
    i2s_cfg.i2s_config.sample_rate = 8000;
    i2s_cfg.i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
    i2s_cfg.type = AUDIO_STREAM_READER;
    i2s_stream_reader = i2s_stream_init(&i2s_cfg);
   
    amrnb_encoder_cfg_t amr_enc_cfg = DEFAULT_AMRNB_ENCODER_CONFIG();
    amr_enc_cfg.task_core = 1;
    amr_encoder = amrnb_encoder_init(&amr_enc_cfg);
   
    audio_pipeline_register(pipeline_rec, i2s_stream_reader, "i2s_rec");
    audio_pipeline_register(pipeline_rec, amr_encoder, "amr");
    audio_pipeline_register(pipeline_rec, fatfs_stream_writer, "file");
 
    audio_pipeline_link(pipeline_rec, (const char *[]) {"i2s_rec", "amr", "file"}, 3);
 
    audio_element_set_uri(fatfs_stream_writer, "/sdcard/rec.amr");
 
    audio_event_iface_cfg_t evt_rec_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
    evt_rec = audio_event_iface_init(&evt_rec_cfg);
 
    audio_pipeline_set_listener(pipeline_rec, evt_rec);

播放事件:

    int player_volume = 50;
    audio_hal_set_volume(hal, player_volume);
    audio_pipeline_run(pipeline_mp3);
    while (1) {
        /* Handle event interface messages from pipeline
           to set music info and to advance to the next song
        */
        audio_event_iface_msg_t msg;
        esp_err_t ret = audio_event_iface_listen(evt_mp3, &msg, portMAX_DELAY);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "[ * ] Event interface error : %d", ret);
            continue;
        }
        if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT){
            // Set music info for a new song to be played
            if (msg.source == (void *) mp3_decoder
                && msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) {
                audio_element_info_t music_info = {0};
                audio_element_getinfo(mp3_decoder, &music_info);
                ESP_LOGI(TAG, "[ * ] Received music info from mp3 decoder, sample_rates=%d, bits=%d, ch=%d",
                    music_info.sample_rates, music_info.bits, music_info.channels);
                audio_element_setinfo(i2s_stream_writer, &music_info);
                i2s_stream_set_clk(i2s_stream_writer, music_info.sample_rates, music_info.bits, music_info.channels);
                continue;
            }
            // Advance to the next song when previous finishes
            if (msg.source == (void *) i2s_stream_writer
                && msg.cmd == AEL_MSG_CMD_REPORT_STATUS) {
                audio_element_state_t el_state = audio_element_get_state(i2s_stream_writer);
                if (el_state == AEL_STATE_FINISHED) {
                    ESP_LOGI(TAG, "[ * ] Finished, advancing to the next song");
                    audio_pipeline_stop(pipeline_mp3);
                    audio_pipeline_wait_for_stop(pipeline_mp3);
                    get_file(NEXT);
                    audio_pipeline_run(pipeline_mp3);
                }
                continue;
            }
        }

录音事件:

    audio_pipeline_run(pipeline_rec);
    int second_recorded = 0;
    while (1) {
        audio_event_iface_msg_t msg;
        if (audio_event_iface_listen(evt_rec, &msg, 1000 / portTICK_RATE_MS) != ESP_OK) {
            second_recorded++;
            ESP_LOGI(TAG, "[ * ] Recording ... %d", second_recorded);
            if (second_recorded >= 5) {
                ESP_LOGI(TAG, "Finishing amr recording");
                break;
            }
            continue;
        }
 
        /* Stop when the last pipeline element (i2s_stream_reader in this case) receives stop event */
        if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *)i2s_stream_reader && msg.cmd ==
            AEL_MSG_CMD_REPORT_STATUS && (int)msg.data == AEL_STATUS_STATE_STOPPED) {
            ESP_LOGW(TAG, "[ * ] Stop event received");
            break;
        }
    }

两个线程单独运行ok,如果同步运行的话,会出现播放卡顿,录音出来的声音是很尖锐的噪音。
请问是不是我的处理思路有问题,或是配置有问题?烦请解答,不胜感激!

最近编辑记录 dy_xie (2018-11-23 10:11:52)

离线

#2 2018-11-23 11:59:09

basicdev
会员
注册时间: 2017-10-02
累计积分: 159

Re: esp32+8388边录边播问题

采集线程与播放线程如何通讯呢?

离线

#3 2018-11-23 13:34:54

dy_xie
会员
注册时间: 2018-11-23
累计积分: 7

Re: esp32+8388边录边播问题

basicdev 说:

采集线程与播放线程如何通讯呢?

这里没有考虑通讯,只是在测试可行性,如果要通讯的话可能会通过锁来同步。

离线

#4 2018-11-23 14:11:00

lilo
会员
注册时间: 2017-10-15
累计积分: 214

Re: esp32+8388边录边播问题

这个ESP32框架代码没看懂,这是录到 TF 卡,然后播放?
或者录在RAM里面,然后播放线程再从内存播放?

离线

#5 2018-11-23 14:16:01

dy_xie
会员
注册时间: 2018-11-23
累计积分: 7

Re: esp32+8388边录边播问题

lilo 说:

这个ESP32框架代码没看懂,这是录到 TF 卡,然后播放?
或者录在RAM里面,然后播放线程再从内存播放?

是这样的:TF卡里有MP3资源,先启动播放线程读取卡中的资源并播放。开始播放之后启动录音线程采集并保存到TF卡中。
实际上就是做到自带BGM的录音笔小demo。

离线

#6 2018-11-23 14:30:30

lilo
会员
注册时间: 2017-10-15
累计积分: 214

Re: esp32+8388边录边播问题

dy_xie 说:
lilo 说:

这个ESP32框架代码没看懂,这是录到 TF 卡,然后播放?
或者录在RAM里面,然后播放线程再从内存播放?

是这样的:TF卡里有MP3资源,先启动播放线程读取卡中的资源并播放。开始播放之后启动录音线程采集并保存到TF卡中。
实际上就是做到自带BGM的录音笔小demo。

播放和录音线程是操作同一个文件吗?

离线

#7 2018-11-23 14:34:57

dy_xie
会员
注册时间: 2018-11-23
累计积分: 7

Re: esp32+8388边录边播问题

lilo 说:
dy_xie 说:
lilo 说:

这个ESP32框架代码没看懂,这是录到 TF 卡,然后播放?
或者录在RAM里面,然后播放线程再从内存播放?

是这样的:TF卡里有MP3资源,先启动播放线程读取卡中的资源并播放。开始播放之后启动录音线程采集并保存到TF卡中。
实际上就是做到自带BGM的录音笔小demo。

播放和录音线程是操作同一个文件吗?

不是的,播放的是MP3文件,保存的是AMR文件。

离线

#8 2018-11-23 14:41:32

lilo
会员
注册时间: 2017-10-15
累计积分: 214

Re: esp32+8388边录边播问题

播放的I2S初始化:

i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
    i2s_cfg.i2s_port = I2S_NUM_0;
    i2s_cfg.type = AUDIO_STREAM_WRITER;
    i2s_stream_writer = i2s_stream_init(&i2s_cfg);

录音的I2S初始化:

i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();

    i2s_cfg.i2s_port = I2S_NUM_1;
    i2s_cfg.task_core = 1;
    i2s_cfg.i2s_config.sample_rate = 8000;
    i2s_cfg.i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
    i2s_cfg.type = AUDIO_STREAM_READER;
    i2s_stream_reader = i2s_stream_init(&i2s_cfg);

这是播放用I2S0 口, 录音用I2S1 口?

离线

#9 2018-11-23 14:44:35

dy_xie
会员
注册时间: 2018-11-23
累计积分: 7

Re: esp32+8388边录边播问题

lilo 说:

播放的I2S初始化:

i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
    i2s_cfg.i2s_port = I2S_NUM_0;
    i2s_cfg.type = AUDIO_STREAM_WRITER;
    i2s_stream_writer = i2s_stream_init(&i2s_cfg);

录音的I2S初始化:

i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();

    i2s_cfg.i2s_port = I2S_NUM_1;
    i2s_cfg.task_core = 1;
    i2s_cfg.i2s_config.sample_rate = 8000;
    i2s_cfg.i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
    i2s_cfg.type = AUDIO_STREAM_READER;
    i2s_stream_reader = i2s_stream_init(&i2s_cfg);

这是播放用I2S0 口, 录音用I2S1 口?

是的,这样有问题吗?

离线

#10 2018-11-23 15:13:36

lilo
会员
注册时间: 2017-10-15
累计积分: 214

Re: esp32+8388边录边播问题

这样做貌似都挺好,没有问题, 看来得用逻辑分析仪上了。

离线

#11 2018-11-23 15:16:41

dy_xie
会员
注册时间: 2018-11-23
累计积分: 7

Re: esp32+8388边录边播问题

lilo 说:

这样做貌似都挺好,没有问题, 看来得用逻辑分析仪上了。

条件有限,不知道如何从软件上来处理了。

离线

#12 2018-11-23 15:30:51

lilo
会员
注册时间: 2017-10-15
累计积分: 214

Re: esp32+8388边录边播问题

dy_xie 说:
lilo 说:

这样做貌似都挺好,没有问题, 看来得用逻辑分析仪上了。

条件有限,不知道如何从软件上来处理了。

淘宝上逻辑分析仪不贵, 大概就几十块钱,可以非常直观分析各种协议 I2C/SPI/UART/I2S 等

离线

#13 2018-11-23 15:40:59

dy_xie
会员
注册时间: 2018-11-23
累计积分: 7

Re: esp32+8388边录边播问题

lilo 说:
dy_xie 说:
lilo 说:

这样做貌似都挺好,没有问题, 看来得用逻辑分析仪上了。

条件有限,不知道如何从软件上来处理了。

淘宝上逻辑分析仪不贵, 大概就几十块钱,可以非常直观分析各种协议 I2C/SPI/UART/I2S 等

好的,非常感谢你的建议,我学习一下如何使用分析。

离线

#14 2019-07-10 14:25:45

熊宇
会员
注册时间: 2019-07-10
累计积分: 1

Re: esp32+8388边录边播问题

一个I2S就可以了呀~

离线

#15 2019-07-10 18:30:21

aozima
会员
注册时间: 2019-05-25
累计积分: 13

Re: esp32+8388边录边播问题

一个I2S就可以了呀~

录回来的重采样下就可以得到语音常用的8K或16K采样率。

离线

页脚