ESP32使用百度语音合成 实现文字转语音播放

esp32 播放多个音频源的音频,例如播放本地flash的mp3,http或者蓝牙传来的音频流等。

一,简介

本次例程介绍更加简单的播放器初始化,并在播放器中添加http_streamflash_tone_stream等输入流来实现 百度语音合成播放本地音频 的效果。

二,播放器初始化

esp-idf提供了一套更加便捷的创建pipeline管道的api函数,接口在esp_audio.h中定义。使用这套api能更快的初始化一个音频处理管道。

一个音频管道就是三四个部分组成,输入流->编解码->适配->输出流,而esp_audio.h中提供了几个重要的函数来实现以上:其中必须要有的element就是输入,编解码,输出。

几个比较重要的函数:
1,添加音频输入源,这里添加了http和flash_tone

esp_audio_input_stream_add(player, http_stream_reader);

2,添加音频输出源,这里添加了i2s流,输出到ac101播放音频

esp_audio_output_stream_add(player, i2s_stream_writer);

3,添加音频编解码仓库

esp_audio_codec_lib_add(player, AUDIO_CODEC_TYPE_DECODER, mp3_decoder_init(&mp3_dec_cfg));

具体使用的流程:

    /*板子音频芯片初始化 音频引脚初始化 更换芯片或者板子时要注意*/
    esp_audio_cfg_t cfg = DEFAULT_ESP_AUDIO_CONFIG();
    ESP_LOGI(TAG, "audio_board_init");
    //音频硬件初始化
    audio_board_handle_t board_handle = audio_board_init();
    cfg.vol_handle = board_handle->audio_hal;
    cfg.prefer_type = ESP_AUDIO_PREFER_MEM;
    
    ESP_LOGI(TAG, "create player");
    //!对于我们的开发板,要使用48k才能正常说话(喇叭)
    cfg.resample_rate = 48000;  
    player = esp_audio_create(&cfg);

    //音频编解码芯片ac101初始化
    ESP_LOGI(TAG, "init codec");
    audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, AUDIO_HAL_CTRL_START);

    //创建http流
    ESP_LOGI(TAG, "[2.1] Create http stream to read data");
    http_stream_cfg_t http_cfg = HTTP_STREAM_CFG_DEFAULT();
    //设置http回调函数
    http_cfg.event_handle = _http_stream_event_handle;
    http_cfg.type = AUDIO_STREAM_READER;
    audio_element_handle_t http_stream_reader = http_stream_init(&http_cfg);
    esp_audio_input_stream_add(player, http_stream_reader);   

    //创建flash_tone流
    ESP_LOGI(TAG, "create flash tone stream reader");
    tone_stream_cfg_t tone_cfg = TONE_STREAM_CFG_DEFAULT();
    tone_cfg.type = AUDIO_STREAM_READER;
    ESP_LOGI(TAG, "add stream reader to player");
    esp_audio_input_stream_add(player, tone_stream_init(&tone_cfg));

    //mp3解码
    ESP_LOGI(TAG, "create mp3 decoder and add to player");
    mp3_decoder_cfg_t  mp3_dec_cfg  = DEFAULT_MP3_DECODER_CONFIG();
    esp_audio_codec_lib_add(player, AUDIO_CODEC_TYPE_DECODER, mp3_decoder_init(&mp3_dec_cfg));

    //创建i2s writer并添加到player的输出源
    ESP_LOGI(TAG, "create i2s writer and add to player");
    i2s_stream_cfg_t i2s_writer = I2S_STREAM_CFG_DEFAULT();
    //!注意要和player的采样率48000相同
    i2s_writer.i2s_config.sample_rate = 48000; 
    //右声道
    i2s_writer.i2s_config.channel_format = I2S_CHANNEL_FMT_ALL_RIGHT;   
    i2s_writer.type = AUDIO_STREAM_WRITER;
    audio_element_handle_t i2s_stream_writer = i2s_stream_init(&i2s_writer);
    esp_audio_output_stream_add(player, i2s_stream_writer);

    ESP_LOGI(TAG, "http_stream_reader->mp3decode->player->i2s->ac101");
    ESP_LOGI(TAG, "flash_stream_reader->mp3decode->player->i2s->ac101");

    esp_audio_vol_set(player, 80);

三,播放音频

3.1 播放本地音频

之前介绍过esp32 播放本地flash中的mp3,没看过的朋友可以参考一下:ESP32 ADF 离线播放mp3 mp3烧录flash

3.2 使用百度语音合成 实现文字转语音

语音合成是将中文合成音频,然后播放出来。

本例程使用的是百度AI开放平台的语音合成API,具体的接口文档参考百度AI开放平台的详细介绍。百度语音合成文档

使用百度的API需要先获得token,然后发送GET或者POST请求,请求中写入要合成的中文字符串,获取http音频流,将http音频流添加到以上的player中播放。

ADF为我们提供了获取百度token的代码,在ADF目录下components/adf_utils/cloud_services/baidu_access_token.c中,使用baidu_get_access_token()可以获取百度token。

调用以下函数实现语音播放,该函数会调用_http_stream_event_handle()回调函数,该函数负责处理这次http连接的所有操作。包括请求前的参数准备,请求开始,接收数据,断开连接等。如果成功返回http音频流,则会在player中播放

esp_audio_sync_play(player,"http://tsn.baidu.com/text2audio", 0);

在这个回调函数中我们主要处理的是http请求前的参数设置。包括token的获取,设置POST的body参数。其中text是一个全局变量,存放即将转成语音的字符串。

/*
 * http stream回调函数
 * 调用esp_audio_sync_play(player,BAIDU_TTS_URL, 0)会调用此回调函数
 */
int _http_stream_event_handle(http_stream_event_msg_t *msg)
{
    
    
    esp_http_client_handle_t http_client = (esp_http_client_handle_t)msg->http_client;
    switch(msg->event_id)
    {
    
    
        case HTTP_STREAM_PRE_REQUEST:

            //准备请求数据 检查token
            ESP_LOGI(TAG, "HTTP_STREAM_PRE_REQUEST");
            if (baidu_access_token == NULL) {
    
    
                // Must freed `baidu_access_token` after used 获得token
                ESP_LOGI(TAG, "try to get token");
                baidu_access_token = baidu_get_access_token(BAIDU_ACCESS_KEY, BAIDU_SECRET_KEY);
            }
            if (baidu_access_token == NULL) {
    
    
                ESP_LOGE(TAG, "Error issuing access token");
                return ESP_FAIL;
            }
            /* 组装body参数:lan:语言;cuid:设备id;ctp:...;aue:文件格式;spd:语速;pit:音调;vol:音量;per:人声*/
            int data_len = snprintf(request_data, 1024, "lan=zh&cuid=ppp&ctp=2&aue=3&spd=4&pit=5&vol=5&per=0&tok=%s&tex=%s", baidu_access_token, text);
            //将token装填进http请求体
            esp_http_client_set_post_field(http_client, request_data, data_len);
            //post
            esp_http_client_set_method(http_client, 1);
            break;

        case HTTP_STREAM_ON_REQUEST:
            ESP_LOGI(TAG, "HTTP_STREAM_ON_REQUEST");
            break;
        case HTTP_STREAM_ON_RESPONSE:
            ESP_LOGI(TAG, "HTTP_STREAM_ON_RESPONSE");
            break;
        case HTTP_STREAM_POST_REQUEST:
            ESP_LOGI(TAG, "HTTP_STREAM_POST_REQUEST");
            break;
        case HTTP_STREAM_FINISH_REQUEST:
            ESP_LOGI(TAG, "HTTP_STREAM_FINISH_REQUEST");
            break;    
        case HTTP_STREAM_RESOLVE_ALL_TRACKS:
            ESP_LOGI(TAG, "HTTP_STREAM_RESOLVE_ALL_TRACKS");
            break;
        case HTTP_STREAM_FINISH_TRACK:
            ESP_LOGI(TAG, "HTTP_STREAM_FINISH_TRACK");
            break;
        case HTTP_STREAM_FINISH_PLAYLIST:
            ESP_LOGI(TAG, "HTTP_STREAM_FINISH_PLAYLIST");
            break;        
    }
    return ESP_OK;
}

四,小结

http stream的播放并不复杂,主要的工作就是设置好http请求头,获取http流。
但不得不夸一下百度,他的语音合成速度非常快,实用性非常高
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44821644/article/details/109107687