请问有没有人做过用ESP32调用百度语音合成的API或者讯飞语音合成的API来进行播报,用arduino编写的
该回答引用ChatGPT-3.5
#include <stdio.h>
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "esp_http_client.h"
#include "esp_audio.h"
#include "audio_board.h"
#include "audio_pipeline.h"
#include "audio_element.h"
#include "i2s_stream.h"
#include "http_stream.h"
#include "raw_stream.h"
// 百度语音合成API接口地址
#define BAIDU_API_URL "https://tsn.baidu.com/text2audio"
// 百度语音合成API接口参数
#define BAIDU_API_KEY "your_api_key"
#define BAIDU_SECRET_KEY "your_secret_key"
#define BAIDU_PER "0" // 发音人选择,0为女声,1为男声
#define BAIDU_SPD "5" // 语速,取值0-15,默认为5中语速
#define BAIDU_PIT "5" // 音调,取值0-15,默认为5中语调
#define BAIDU_VOL "7" // 音量,取值0-9,默认为第5中音量
static const char *TAG = "baidu_tts";
// HTTP请求头部
static esp_err_t http_event_handle(esp_http_client_event_t *evt)
{
switch (evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGE(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
// 百度语音合成API请求参数
static void get_params(char *params, int max_len, const char *text)
{
snprintf(params, max_len, "tex=%s&cuid=esp32&lan=zh&per=%s&spd=%s&pit=%s&vol=%s&tok=24.02b8fbff6d0c56b289f1b07a70939e2c.2592000.1626938935.282335-24234292",
text, BAIDU_PER, BAIDU_SPD, BAIDU_PIT, BAIDU_VOL);
}
// 请求百度语音合成API,返回语音文件数据
static esp_err_t request_tts_data(const char *text, uint8_t **data, uint32_t *size)
{
char url[256];
char params[512];
get_params(params, sizeof(params), text);
esp_http_client_config_t http_cfg = {
.url = BAIDU_API_URL,
.event_handler = http_event_handle,
};
esp_http_client_handle_t client = esp_http_client_init(&http_cfg);
esp_http_client_set_method(client, HTTP_METHOD_POST);
esp_http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
esp_http_client_set_post_field(client, params, strlen(params));
esp_err_t err = esp_http_client_perform(client);
if (err != ESP_OK) goto end;
*size = esp_http_client_fetch_headers(client);
*data = (uint8_t*)malloc(*size);
esp_http_client_read_response(client, (char*)*data, *size);
end:
esp_http_client_cleanup(client);
return err;
}
// 播放语音文件
static void play_tts_data(uint8_t *data, uint32_t size)
{
audio_pipeline_handle_t pipeline;
audio_element_handle_t i2s_stream_writer, http_stream_reader, raw_read_el;
i2s_stream_cfg_t i2s_cfg = {
.type = AUDIO_STREAM_WRITER,
.i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
.sample_rate = 16000,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
},
.dma_buf_count = 5,
.dma_buf_len = 300,
};
i2s_stream_writer = i2s_stream_init(&i2s_cfg);
http_stream_cfg_t http_cfg = {
.type = AUDIO_STREAM_READER,
.event_handle = http_event_handle,
.task_stack = 4 * 1024,
.task_prio = 2,
.out_rb_size = 8192,
.buffer_size = 1024,
};
http_stream_reader = http_stream_init(&http_cfg);
http_stream_set_url(http_stream_reader, "");
raw_stream_cfg_t raw_cfg = {
.type = AUDIO_STREAM_WRITER,
};
raw_read_el = raw_stream_init(&raw_cfg);
pipeline = audio_pipeline_init();
audio_pipeline_register(pipeline, http_stream_reader, "http");
audio_pipeline_register(pipeline, raw_read_el, "raw");
audio_pipeline_register(pipeline, i2s_stream_writer, "i2s");
audio_pipeline_link(pipeline, (const char *[]) {"http", "raw", "i2s"}, 3);
audio_element_set_uri(http_stream_reader, "");
audio_element_run(pipeline);
int offset = 0;
while (offset < size) {
int wlen = i2s_stream_writer_write(i2s_stream_writer, data + offset, size - offset, 100 / portTICK_RATE_MS);
if (wlen > 0) offset += wlen;
else break;
}
audio_element_stop(pipeline);
audio_element_wait_for_stop(pipeline);
audio_pipeline_terminate(pipeline);
audio_pipeline_unregister(pipeline, http_stream_reader);
audio_pipeline_unregister(pipeline, raw_read_el);
audio_pipeline_unregister(pipeline, i2s_stream_writer);
audio_pipeline_remove_listener(pipeline);
audio_pipeline_deinit(pipeline);
mem_free(data);
}
// 主函数
void app_main()
{
ESP_LOGI(TAG, "Start");
esp_err_t err;
uint8_t *data;
uint32_t size;
// 请求语音文件数据
err = request_tts_data("百度语音合成API测试", &data, &size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Request TTS data failed");
return;
}
// 播放语音文件
err = audio_board_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Initialize audio board failed");
free(data);
return;
}
err = esp_audio_hal_ctrl_codec_mode(ESP_AUDIO_CODEC_MODE_ENCODE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Set audio codec mode failed");
free(data);
return;
}
play_tts_data(data, size);
}
以上代码需要设置好BAIDU_API_KEY和BAIDU_SECRET_KEY,即百度开发者平台所给的API Key和Secret Key。
请注意,此示例仅供参考,实际使用中还需处理网络连接超时等可能出现的异常情况。同时,本示例仅支持16KHz采样率、16位采样精度、单声道格式的语音播放,如需修改,请适当调整代码。
ESP-IDF: 请切换到 release v4.2 tag 版本:
git checkout v4.2
ESP8266_RTOS_SDK: 请切换到 v3.3 tag 版本:
git checkout v3.3
由于我是一个AI语言模型,我没有具体的编程能力和环境,无法提供完整的代码实现,但我可以提供一些基本步骤和思路供参考:
以下是一个简单的代码示例,仅供参考:
// 引入需要使用的库
#include "esp_http_client.h"
#include "esp_log.h"
static const char *TAG = "speech_synthesis";
// 百度语音合成API Key和Secret Key
#define BAIDU_API_KEY "YOUR_BAIDU_API_KEY"
#define BAIDU_SECRET_KEY "YOUR_BAIDU_SECRET_KEY"
// 百度语音合成API地址
#define BAIDU_API_URL "http://tsn.baidu.com/text2audio"
// 合成文本
#define SYNTHESIS_TEXT "您好,欢迎使用ESP32语音合成功能!"
// HTTP请求回调函数,用于接收响应数据
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
switch(evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGE(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
// 将获取到的音频数据发送到DAC或扬声器
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
void app_main()
{
esp_err_t err;
// 创建HTTP客户端
esp_http_client_config_t config = {
.url = BAIDU_API_URL,
.event_handler = http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
// 设置参数和Header
char *buffer = malloc(strlen(SYNTHESIS_TEXT) + strlen(BAIDU_API_KEY) + strlen(BAIDU_SECRET_KEY) + 128);
sprintf(buffer, "tex=%s&lan=zh&cuid=esp32&ctp=1&aue=3&tok=%s.%s", SYNTHESIS_TEXT, BAIDU_API_KEY, BAIDU_SECRET_KEY);
esp_http_client_set_method(client, HTTP_METHOD_GET);
esp_http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
esp_http_client_set_header(client, "Accept", "audio/mp3");
esp_http_client_set_header(client, "User-Agent", "Mozilla/5.0");
// 发送HTTP请求
err = esp_http_client_perform(client);
if (err != ESP_OK) {
ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err));
}
// 释放资源
free(buffer);
esp_http_client_cleanup(client);
}
这里仅是一个基础的示例代码,仍需要根据具体的API和开发环境等情况进行相应的调整。如果对此部分有任何疑问或需要更详细的指导,请参考官方文档或在相关社区寻求帮助。