乐鑫技术交流
直播中

李军

7年用户 1234经验值
私信 关注
[问答]

请问afe_handle->fetch获得的音频数据如何播放?

目前我在基于ESP-SkaiNet的sample,开发语音助手。当提示词唤醒后对用户输入的 语音保存和处理。

我想请教一下,当我通过afe_handle->fetch,res->data获得的音频数据是什么格式?PCM吗?获取的音频数据如何播放出来?


void feed_Task(void *arg)
{
    esp_afe_sr_data_t *afe_data = arg;
    int audio_chunksize = afe_handle->get_feed_chunksize(afe_data);
    int nch = afe_handle->get_channel_num(afe_data);
    int feed_channel = esp_get_feed_channel();
    assert(nch <= feed_channel);
    int16_t *i2s_buff = malloc(audio_chunksize * sizeof(int16_t) * feed_channel);
    assert(i2s_buff);

    while (task_flag) {
        esp_get_feed_data(false, i2s_buff, audio_chunksize * sizeof(int16_t) * feed_channel);
        afe_handle->feed(afe_data, i2s_buff);
    }
    if (i2s_buff) {
        free(i2s_buff);
        i2s_buff = NULL;
    }
    vTaskDelete(NULL);
}


int16_t *buff;
int buff_index;
int buff_total;
int afe_chunksize;

void detect_Task(void *arg)
{
    esp_afe_sr_data_t *afe_data = arg;
    int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data);
    char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_CHINESE);
    printf("multinet:%sn", mn_name);
    esp_mn_iface_t *multinet = esp_mn_handle_from_name(mn_name);
    model_iface_data_t *model_data = multinet->create(mn_name, 6000);

    esp_mn_commands_update_from_sdkconfig(multinet, model_data); // Add speech commands from sdkconfig

    int mu_chunksize = multinet->get_samp_chunksize(model_data);
    assert(mu_chunksize == afe_chunksize);

    //print active speech commands
    multinet->print_active_speech_commands(model_data);

    printf("------------detect start------------n");
    while (task_flag) {
        
        afe_fetch_result_t* res = afe_handle->fetch(afe_data);
        
        if (!res || res->ret_value == ESP_FAIL) {
            printf("fetch error!n");
            break;
        }

        if (res->wakeup_state == WAKENET_DETECTED) {
            printf("WAKEWORD DETECTEDn");
            multinet->clean(model_data);  // clean all status of multinet
            afe_chunksize = afe_handle->get_fetch_chunksize(afe_data);
            buff = malloc(1024 * afe_chunksize * sizeof(int16_t));
            buff_index = 0;
            buff_total = 0;
            assert(buff);
        } else if (res->wakeup_state == WAKENET_CHANNEL_VERIFIED) {
            printf("WAKENET_CHANNEL_VERIFIEDn");
            play_voice = -1;
            detect_flag = 1;
            printf("AFE_FETCH_CHANNEL_VERIFIED, channel index: %dn", res->trigger_channel_id);
        }

        if (detect_flag == 1) {

            if (res->vad_state == AFE_VAD_SPEECH) {
                if (res    res->ret_value != ESP_FAIL) {
                    memcpy(buff + buff_index, res->data, afe_chunksize * 1 * sizeof(int16_t));
                    buff_index += afe_chunksize * 1 * sizeof(int16_t);
                }   
            }

            esp_mn_state_t mn_state = multinet->detect(model_data, res->data);

            if (mn_state == ESP_MN_STATE_DETECTING) {
                continue;
            }

            if (mn_state == ESP_MN_STATE_DETECTED) {
                printf("Detectedn");
                esp_mn_results_t *mn_result = multinet->get_results(model_data);
                for (int i = 0; i < mn_result->num; i++) {
                    printf("TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %fn",
                    i+1, mn_result->command_id[i], mn_result->phrase_id[i], mn_result->string, mn_result->prob[i]);
                }
                printf("n-----------listening-----------n");
            }

            if (mn_state == ESP_MN_STATE_TIMEOUT) {

                printf("n-----------playing user speeking-----------n");
                printf("length %dn", buff_index);

                wake_up_action();
                play_audio_voice(buff, buff_index);

                esp_mn_results_t *mn_result = multinet->get_results(model_data);
                printf("timeout, string:%sn", mn_result->string);
                afe_handle->enable_wakenet(afe_data);
                detect_flag = 0;
                free(buff);
                printf("n-----------awaits to be waken up-----------n");
                continue;

            }

        }
    }

    if (model_data) {
        multinet->destroy(model_data);
        model_data = NULL;
    }

    printf("detect exitn");
    vTaskDelete(NULL);
   
}
                                                                                                                                          

回帖(1)

杨万富

2024-7-19 17:32:54
首先,我们需要了解afe_handle->fetch()方法的作用。这个方法用于从音频前端(AFE)获取音频数据。在ESP-SkaiNet中,AFE负责处理音频输入,如麦克风信号的采集、预处理等。

根据您的代码片段,我们可以分析以下几个步骤:

1. 获取音频数据的通道数和块大小:
   - `int audio_chunksize = afe_handle->get_feed_chunksize(afe_data);`:获取音频数据的块大小。
   - `int nch = afe_handle->get_channel_num(afe_data);`:获取音频数据的通道数。

2. 获取音频数据的格式:
   - 通常情况下,AFE处理后的音频数据以PCM(脉冲编码调制)格式存储。PCM是一种未压缩的音频格式,通常用于存储原始音频数据。

3. 播放音频数据:
   - 要播放音频数据,您需要将PCM数据发送到音频输出设备,如扬声器或耳机。在ESP-SkaiNet中,您可以使用音频播放库(如ESP32-A2DP)来实现音频播放。

以下是一个简单的示例,展示如何使用ESP32-A2DP库播放PCM音频数据:

```c
#include
#include
#include

#define I2S_NUM I2S_NUM_0
#define I2S_SAMPLE_RATE 44100
#define I2S_CHANNEL_NUM 2

static const char *TAG = "A2DP_SINK";

void feed_Task(void *arg) {
    esp_afe_sr_data_t *afe_data = arg;
    int audio_chunksize = afe_handle->get_feed_chunksize(afe_data);
    int nch = afe_handle->get_channel_num(afe_data);
    int feed_channel = esp_get_feed_channel();

    // 初始化I2S
    i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
        .sample_rate = I2S_SAMPLE_RATE,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
        .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
        .dma_buf_count = 8,
        .dma_buf_len = 1024
    };
    i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);

    // 初始化A2DP
    esp_a2d_sink_cfg_t a2d_sink_cfg = {
        .codec_fmt = ESP_A2D_Codectype_SBC,
        .BitsPerSample = 16,
        .ChannelNum = I2S_CHANNEL_NUM,
        .SampleRate = I2S_SAMPLE_RATE
    };
    esp_a2d_sink_register(&a2d_sink_cfg);

    // 获取音频数据并播放
    while (1) {
        esp_err_t ret;
        size_t bytes_written;
        size_t chunk_size = audio_chunksize * nch * 2; // 2 bytes per sample
        uint8_t *buffer = malloc(chunk_size);
        if (buffer == NULL) {
            ESP_LOGE(TAG, "Failed to allocate memory for audio buffer");
            break;
        }

        ret = afe_handle->fetch(afe_data, buffer, chunk_size);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "Failed to fetch audio data");
            free(buffer);
            break;
        }

        ret = i2s_write_bytes(I2S_NUM, buffer, chunk_size, &bytes_written, portMAX_DELAY);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "Failed to write audio data to I2S");
        }

        free(buffer);
    }

    // 卸载I2S驱动
    i2s_driver_uninstall(I2S_NUM);
    esp_a2d_sink_unregister();
}

void app_main() {
    // 初始化AFE
    afe_handle = afe_init(...);
    // 创建任务
    xTaskCreate(feed_Task, "feed_Task", 4096, afe_handle, 1, NULL);
}
```

在这个示例中,我们首先初始化I2S和A2DP,然后通过afe_handle->fetch()方法获取音频数据,并使用i2s_write_bytes()方法将数据发送到I2S。这样,音频数据就可以通过扬声器播放出来。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分