【基于MAX98357的Minimax(百度)长文本语音合成TTS 接入教程】
- 1. 前言
- 2. 先决条件
- 2.1 硬件准备
- 2.2 软件准备
- 2.3 接线
- 3. 核心代码
- 3.1 驱动实现
- 3.2 代码解析
- 4. 播放文本
- 5. 结论
视频地址:
SeeedXIAO ESP32S3 Sense【基于MAX98357的Minimax(百度)长文本语音合成TTS 接入教程】
1. 前言
随着物联网技术的快速发展,智能设备的交互方式也在不断进化。语音合成技术(TTS)作为人机交互的重要组成部分,正变得越来越普及。本文将介绍如何利用SeeedXIAO ESP32S3 Sense开发板与MAX98357音频放大器结合,实现与Minimax长文本语音合成服务的集成,打造一个功能丰富的语音交互系统。
2. 先决条件
2.1 硬件准备
- ESP32开发板(推荐XIAO开发板)
- MAX98357 I2S 音频放大器模块
- 扬声器
- 跳线(杜邦线)
目前这是我使用的ESP32S3官方硬件👍👍👍(小小的身材有大大的力量)只需要35元加摄像头麦克风79元,后期我会整理相关专栏进行Arduino系统学习😘😘😘。有需要可以购买xiao开发板💕💕💕,SeeedXIAO ESP32S3 Sense硬件购买地址:https://s.click.taobao.com/lekazrt
2.2 软件准备
- Arduino IDE:下载并安装 Arduino IDE;
- ESP32 开发板库:在 Arduino IDE 中添加 ESP32 支持;
参考博客:【esp32c3配置arduino IDE教程】
为安装过程留出一些时间,具体时间可能因您的互联网连接而异。 - ESP32-audioI2S库安装
- 首先到GITHUB下载「ESP32-audioI2S」 仓库地址:https://github.com/schreibfaul1/ESP32-audioI2S/
- 解压源码包
- 把解压的文件放进Arduino IDE的libraries文件夹
- ArduinoJson库安装
安装成功如上
2.3 接线
ESP32与MAX98357的接线非常简单,只需要将ESP32的I2S接口与MAX98357的相应引脚连接即可。具体接线如下:
- XIAO D4( ESP32的I2S_BCLK_5)连接到MAX98357的BCLK
- XIAO D3( ESP32的I2S_LRC_4)连接到MAX98357的LRC
- XIAO D5( ESP32的I2S_DOUT_6)连接到MAX98357的DIN
3. 核心代码
下面准备进行了基于ESP32 的硬件测试,此部分有源码分享和代码解析两部分
3.1 驱动实现
在Arduino IDE中,首先需要安装ESP32-audioI2S
库和ArduinoJson
库。然后将ESP32S3N8R8开发板与MAX98357音频放大器结合,实现与Minimax长文本语音合成TTS服务的集成。以下是一个简单的示例接入Minimax 代码:
#include <Audio.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "Arduino.h"
//1. Replace with your network credentials
const char* ssid = "J09 502";
const char* password = "qwertyuiop111"; // Change this to your WiFi password
String voice_id = "female-tianmei-jingpin"; //青年大学生音色:male-qn-daxuesheng;甜美女性音色:female-tianmei;男性主持人:presenter_male;女性主持人:presenter_female
// 2. Replace with your OpenAI API key
const char* apiKey = "eyJhbGciOiaWwiOiIiLCJDcmVhdGVUaW1lIjoiMjAyNC0wMy0xNiAxMzoyNDoxOCIsImlzcyI6Im1pbmltYXgifQ.WlEj8Nk0j_WOMXZE9SbIC8sHpwJ6R6Pi8Spl5mahJsW3-Jsz7Ev53sGGz3v__Bd5dDkt7o9-Y8BOW0WZq2ImaN7Rof7YNtYnYnvPNDyGx23_xRqq5co9P5UkC3ciYEcIch2SUZ5QPkXR-sMUPzhdowSYvfdu1N25kdKJ8GE_63NfCnsdDVt8mv0wQSSweJK0yf_C8a8ADdB1uF4vg_WKMDjHlvzERsoNZgX6FYtr-bee85rIyu4U-OrbUvEpR1FLPXa7lTlx65QvhVIYGbIKde7ERIT_7QLOQoVFvPz0gX-H6V7UlmSRgRy4LK_R9mvV5TqCy3v90WK_AFuwEhPXcg";
const char* group_id = "1759482180095975904";
const char* url = "https://api.minimax.chat/v1/t2a_pro?GroupId=1759482180095975904";
char myCharPointer;
//扬声器引脚
#define I2S_DOUT 6 // DIN connection
#define I2S_BCLK 5 // Bit clock
#define I2S_LRC 4 // Left Right Clock
Audio audio;
String answerv;
String getvAnswer(void) {
HTTPClient http1;
http1.setTimeout(10000);
http1.begin(url);
http1.addHeader("Content-Type", "application/json");
String token_key = String("Bearer ") + apiKey;
http1.addHeader("Authorization", token_key);
// 创建一个StaticJsonDocument对象,足够大以存储JSON数据
StaticJsonDocument<200> doc;
// 填充数据
doc["text"] = "我是鹏鹏的小助手,你好鸭";
doc["model"] = "speech-01";
doc["audio_sample_rate"] = 32000;
doc["bitrate"] = 128000;
doc["voice_id"] = voice_id;
// 创建一个String对象来存储序列化后的JSON字符串
String jsonString;
// 序列化JSON到String对象
serializeJson(doc, jsonString);
int httpResponseCode1 = http1.POST(jsonString);
if (httpResponseCode1 != 200) {
Serial.println("HTTP Request Failed");
http1.end();
return "<error>";
}
String reason = http1.getString();
Serial.println("Received response:");
Serial.println(reason);
http1.end();
DynamicJsonDocument jsonDoc1(1024);
deserializeJson(jsonDoc1, reason);
String outputText = jsonDoc1["audio_file"];
return outputText;
}
void set_voice() {
answerv = getvAnswer();
Serial.print(answerv);
char myCharPointer[answerv.length() + 1]; // 分配足够的空间来存储字符串
strcpy(myCharPointer, answerv.c_str()); // 复制字符串到 myCharPointer
audio.connecttohost(myCharPointer); // 128k mp3
}
void setup() {
// Initialize Serial
Serial.begin(115200);
// Connect to Wi-Fi network
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println(WiFi.localIP());
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(20); // 0...21
// audio.connecttohost("https://minimax-algeng-chat-tts.oss-cn-wulanchabu.aliyuncs.com/audio%2Ftts-mp3-20240406123922-amZgimLG.mp3?Expires=1712493562&OSSAccessKeyId=LTAI5tGLnRTkBjLuYPjNcKQ8&Signature=omJ5fmpPvXMoR1nk9D8UbJqR3L4%3D"); // 128k mp3
}
void loop() {
audio.loop();
while (Serial.available() > 0) {
char voice = Serial.read();
// Serial.println(voice);
switch (voice) {
case '1':
voice_id = "male-qn-daxuesheng";
break;
case '2':
voice_id = "female-tianmei";
break;
case '3':
voice_id = "presenter_male";
break;
case '4':
voice_id = "presenter_female";
break;
}
Serial.println(voice_id);
set_voice();
}
}
百度TTS接入代码如下:
#include <WiFi.h>
#include <HTTPClient.h>
#include <UrlEncode.h>
#include "Arduino.h"
#include "WiFiMulti.h"
#include "Audio.h"
// 1、修改MAX98357喇叭接口
#define I2S_DOUT 6
#define I2S_BCLK 5
#define I2S_LRC 4
Audio audio;
WiFiMulti wifiMulti;
// 2、修改WiFi密码
const char *ssid = "J09 502";
const char *password = "qwertyuiop111"; // Change this to your WiFi password
// 3、修改百度语音助手的用户信息
const char *API_KEY = "BXL2YS5w67Xw5XDq";
const char *SECRET_KEY = "pb2zIW2Nch2uNtceKX";
// 4、修改播放文本内容
String encodedText = "人家刚满18岁";
const int PER = 4;
const int SPD = 5;
const int PIT = 5;
const int VOL = 5;
const int AUE = 6;
const char *TTS_URL = "https://tsn.baidu.com/text2audio";
String url = TTS_URL;
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
encodedText = urlEncode(urlEncode(encodedText));
tts_get();
player();
}
void tts_get() {
const char *headerKeys[] = { "Content-Type", "Content-Length" };
// 5、修改百度语音助手的token
url += "?tok=24.e169f864a91715325118.282335-57722200";
url += "&tex=" + encodedText;
url += "&per=" + String(PER);
url += "&spd=" + String(SPD);
url += "&pit=" + String(PIT);
url += "&vol=" + String(VOL);
url += "&aue=" + String(AUE);
url += "&cuid=esp32s3";
url += "&lan=zh";
url += "&ctp=1";
HTTPClient http;
Serial.print("URL: ");
Serial.println(url);
http.begin(url);
http.collectHeaders(headerKeys, 2);
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
if (httpResponseCode == HTTP_CODE_OK) {
Serial.print("Content-Type = ");
Serial.println(http.header("Content-Type"));
String contentType = http.header("Content-Type");
if (contentType.startsWith("audio")) {
Serial.println("合成成功,返回的是音频文件");
// 处理音频文件,保存到SD卡或者播放
} else if (contentType.equals("application/json")) {
Serial.println("合成出现错误,返回的是JSON文本");
// 处理错误信息,根据需要进行相应的处理
} else {
Serial.println("未知的Content-Type");
// 可以添加相应的处理逻辑
}
} else {
Serial.println("Failed to receive audio file");
}
} else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
http.end();
}
void loop() {
audio.loop();
if (Serial.available()) { // put streamURL in serial monitor
audio.stopSong();
String r = Serial.readString();
r.trim();
if (r.length() > 5) audio.connecttohost(r.c_str());
log_i("free heap=%i", ESP.getFreeHeap());
}
}
void player() {
// WiFi.mode(WIFI_STA);
// wifiMulti.addAP(ssid.c_str(), password.c_str());
// wifiMulti.run();
// if(WiFi.status() != WL_CONNECTED){
// WiFi.disconnect(true);
// wifiMulti.run();
// }
const char *host = url.c_str();
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(12); // 0...21
audio.connecttohost(host); // 128k mp3
}
同样需要修改WiFi密码和百度TTS配置
3.2 代码解析
- 修改网络连接:使用WiFi.begin(ssid, password)连接到Wi-Fi网络。
//1. Replace with your network credentials
const char* ssid = "J09 502";
const char* password = "qwertyuiop111"; // Change this to your
- 修改API密钥:将获取的apiKey和group_id替换到代码中相应的位置。
// 2. Replace with your OpenAI API key
const char* apiKey = "eyJhbGciOiaWwiOiIiLCJDcmVhdGVUaW1lIjoiMjAyNC0wMy0xNiAxMzoyNDoxOCIsImlzcyI6Im1pbmltYXgifQ.WlEj8Nk0j_WOMXZE9SbIC8sHpwJ6R6Pi8Spl5mahJsW3-Jsz7Ev53sGGz3v__Bd5dDkt7o9-Y8BOW0WZq2ImaN7Rof7YNtYnYnvPNDyGx23_xRqq5co9P5UkC3ciYEcIch2SUZ5QPkXR-sMUPzhdowSYvfdu1N25kdKJ8GE_63NfCnsdDVt8mv0wQSSweJK0yf_C8a8ADdB1uF4vg_WKMDjHlvzERsoNZgX6FYtr-bee85rIyu4U-OrbUvEpR1FLPXa7lTlx65QvhVIYGbIKde7ERIT_7QLOQoVFvPz0gX-H6V7UlmSRgRy4LK_R9mvV5TqCy3v90WK_AFuwEhPXcg";
const char* group_id = "1759482180095975904";
const char* url = "https://api.minimax.chat/v1/t2a_pro?GroupId=1759482180095975904";
去Minimax官网获取:https://www.minimaxi.com/user-center/basic-information
- group ID获取
- api KEy获取
const char* url = "https://api.minimax.chat/v1/t2a_pro?GroupId=1759482180095975904";
请求不需要变化
- 音色选择:通过
1234
分别选择//青年大学生音色:male-qn-daxuesheng
;甜美女性音色:female-tianmei
;男性主持人:presenter_male
;女性主持人:presenter_female
- 音频播放:使用audio.connecttohost(myCharPointer)将获取的音频文件URL传递给MAX98357进行播放。
以上通过发送HTTP请求到Minimax TTS API,将文本转换为语音。将转换后的语音文件通过MAX98357播放出来。
4. 播放文本
先上烧录配置
在ESP32上运行上述代码后,它将自动连接到WiFi并开始播放指定的MP3音乐文件。你也可以通过串口发送1234
分别选择//青年大学生音色:male-qn-daxuesheng
;甜美女性音色:female-tianmei
;男性主持人:presenter_male
;女性主持人:presenter_female
,以实现在线播放。
打开串口115200
波特率,选择没有结束符,输入1
等待答复
5. 结论
🥳🥳🥳现在,我们在本教程中,您学习了通过本教程,您应该能够成功地将ESP32S3N8R8开发板与MAX98357音频放大器结合,实现与Minimax长文本语音合成TTS服务的集成。这不仅能够为您的项目增加语音交互功能,还能够提升用户体验。🛹🛹🛹从而实现对外部世界进行感知,充分认识这个有机与无机的环境,在实际应用中,您可能还需要考虑如何优化网络请求的效率、如何处理不同语言和口音的文本,以及如何提高系统的稳定性和响应速度。希望本教程能够作为您开始探索语音合成技术的一个起点,随着技术的不断进步,未来将有更多创新的应用等待着我们去发现和实现。🥳🥳🥳科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣
参考资料:
- ESP32-audioI2S库
- MAX98357芯片手册
- https://github.com/schreibfaul1/ESP32-audioI2S/
- 【ESP32使用MAX98357播放音频】
如果你有任何问题,可以通过下面的二维码加入鹏鹏小分队,期待与你思维的碰撞😘😘😘