【基于MAX98357的Minimax(百度)长文本语音合成TTS 接入教程】

news2025/1/15 6:35:02

【基于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 软件准备

  1. Arduino IDE:下载并安装 Arduino IDE;
  2. ESP32 开发板库:在 Arduino IDE 中添加 ESP32 支持;
    参考博客:【esp32c3配置arduino IDE教程】
    为安装过程留出一些时间,具体时间可能因您的互联网连接而异。
  3. ESP32-audioI2S库安装
  • 首先到GITHUB下载「ESP32-audioI2S」 仓库地址:https://github.com/schreibfaul1/ESP32-audioI2S/
  • 解压源码包
  • 把解压的文件放进Arduino IDE的libraries文件夹
    在这里插入图片描述
  1. 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

  1. group ID获取
    在这里插入图片描述
  2. api KEy获取
    在这里插入图片描述
  3. 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播放音频】

如果你有任何问题,可以通过下面的二维码加入鹏鹏小分队,期待与你思维的碰撞😘😘😘

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1641263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Mac】Lightroom Classic 2024 v13.1安装教程

软件介绍 Lightroom Classic 2024是Adobe公司推出的一款专业的数字图像处理软件&#xff0c;旨在为摄影师提供强大的工具和功能&#xff0c;以管理、编辑和分享他们的照片作品。以下是Lightroom Classic 2024的主要特点和功能&#xff1a; 数字照片管理&#xff1a; 提供直观…

YOLOv5手势物体识别(附代码)

之前是做的yolov3手势物体识别&#xff0c;最近几天我将该项目进行了重新的整理和升级&#xff0c;实现了yolov5手势物体识别&#xff0c;同时为了方便更多的人直接拿来应用&#xff0c;我生成了支持windows系统的应用小程序&#xff0c;即便你电脑上没有安装pytorch,没有安装c…

【题解】NC109 岛屿数量(BFS / DFS)

https://www.nowcoder.com/practice/0c9664d1554e466aa107d899418e814e?tpId196&tqId37167&ru/exam/oj dfs #include <vector> class Solution { public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可…

电脑数据怎么拷贝到u盘?操作指南与数据丢失防范

在数字时代&#xff0c;数据的传输与备份已成为我们日常生活和工作中不可或缺的一部分。U盘作为一种便捷、高效的移动存储设备&#xff0c;广泛应用于各种数据拷贝场景。无论是个人文件的备份&#xff0c;还是工作资料的传输&#xff0c;U盘都发挥着举足轻重的作用。那么&#…

HTML_CSS学习:背景、鼠标相关属性

一、背景相关属性 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>背景相关属性</title><style>body{background-color: greenyellow;}div{width: 400px;height: …

光头强:IBM收购HashCorp (Terraform)有多大意义?

StrongBear公司在光头强强总以及合伙人熊大熊二的艰苦努力下&#xff0c;最近公司进了一次扩容。甚至将原来一些甲方的研发人员也拉入旗下&#xff0c;其中就包括与熊二共事多年的小玲子以及小强同学。 光头强也注意到最近在IT软件领域&#xff0c;频频发生一些并购事件。比如…

nodejs实战——搭建websocket服务器

本博客主要介绍websocket服务器库安装&#xff0c;并举了一个简单服务器例子。 服务器端使用websocket需要安装nodejs websocket。 cd 工程目录 # 此刻我们需要执行命令&#xff1a; sudo npm init上述命令创建package.json文件&#xff0c;系统会提示相关配置。 我们也可以使…

知乎广告开户流程,知乎广告的优势是什么?

社交媒体平台不仅是用户获取知识、分享见解的场所&#xff0c;更是品牌展示、产品推广的重要舞台。知乎作为国内知名的知识分享社区&#xff0c;以其高质量的内容生态和庞大的用户基础&#xff0c;成为了众多企业进行广告投放的优选之地。云衔科技通过其专业服务&#xff0c;助…

C++证道之路第十六章string类和标准模板库

一、string类 string实际上是模板具体化basic_string的一个typedef&#xff0c;省略了与内存管理相关的参数。 string类将string::npos定义为字符串的最大长度。 string库提供了很多其他的工具&#xff0c;包括完成下述功能的函数&#xff1a; 删除字符串的部分或全部内容&…

POWERBI==官网教程

地址 COVID-19 tracking sample for US state and local governments - Power BI | Microsoft Learn 已经非常全面了

【简单介绍下Lisp的学习历程】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【docker 】push 镜像到私服

查看镜像 docker images把这个hello-world 推送到私服 docker push hello-world:latest 报错了。不能推送。需要标记镜像 标记Docker镜像 docker tag hello-world:latest 192.168.2.1:5000/hello-world:latest 将Docker镜像推送到私服 docker push 192.168.2.1:5000/hello…

自动驾驶-第02课软件环境基础(ROSCMake)

1. 什么是ros 2. 为什么使用ros 3. ROS通信 3.1 Catkin编译系统

uniapp 文字转语音(文字播报、语音合成)、震动提示插件 Ba-TTS

简介&#xff08;下载地址&#xff09; Ba-TTS 是一款uniapp语音合成&#xff08;tts&#xff09;插件&#xff0c;支持文本转语音&#xff08;无服务费&#xff09;&#xff0c;支持震动提示。 支持语音合成&#xff0c;文本转语音支持震动&#xff08;可自定义任意震动效果…

python笔记:dataclass

1 引子&#xff1a;其他类似实现方法的局限性 假设我们现在需要实现这样的内容&#xff1a; nameChinaarea960population140967 1.1 tuple/list country1_tuple(China,960,140967) country1_tuple[0] #China 缺点&#xff1a;需要记住各个属性是list/tuple第几位的属性&am…

MYSQL从入门到精通(二)

1、MYSQL高级概述 【1】架构概述 【2】索引优化 【3】查询截取 【4】mysql锁机制 【5】主从复制 2、MYSQL概述 【1】mysql内核 【2】sql优化工程师 【3】mysql服务器的优化 【4】各种参数常量设定 【5】查询语句优化 【6】主从复制 【7】软硬件升级 【8】容灾百分 【9】sql编…

【005_音频开发_基础篇_ALSA_Codec_驱动-MA120x0P功放】

005_音频开发_基础篇_ALSA_Codec_驱动-MA120x0P功放 文章目录 005_音频开发_基础篇_ALSA_Codec_驱动-MA120x0P功放创作背景MA120X0P输出模式BTLSEPBTLSEBTL 硬件配置方式/硬件Limiter限幅器限幅器作用过程 主要寄存器操作指令 ma120x0p.cma120x0p.h 创作背景 学历代表过去、能…

ASP.NET视频点播系统的设计与实现

摘 要 本文阐述了基于WEB的交互式视频点播系统的协议原理、软件结构和设计实现。本视频点播系统根据流媒体传输原理&#xff0c;在校园局域网的基础上模拟基于Web的视频点播系统&#xff0c;实现用户信息管理、视频文件的添加、删除、修改及在线播放和搜索功能。本系统是一个…

golang学习笔记(内存逃逸分析)

golang的内存逃逸 逃逸分析&#xff08; Escape analysis&#xff09; 是指由编译器决定内存分配的位置&#xff0c; 不需要程序员指定。 函数中申请一个新的对象。 如果分配在栈中&#xff0c; 则函数执行结束可自动将内存回收&#xff1b;如果分配在堆中&#xff0c; 则函数…

微服务总览

微服务保护 微服务总览 微服务总览 接入层&#xff1a;反向代理功能&#xff0c;可以将用户域名访问的地址以负载均衡的方式代理到网关地址&#xff0c;并且并发能力非常高&#xff0c;并且会采用主备nginx的方式防止nginx寄了&#xff0c;备份nginx监控主nginx状态&#xff0c…