【ESP32S3 接入MiniMax文本语音大模型对话&语音克隆教程】

讲解视频:

ESP32S3 接入MiniMax文本语音大模型对话&语音克隆教程

1. 前言

随着物联网技术的快速发展,智能设备的交互方式也在不断进化。语音合成技术(TTS)作为人机交互的重要组成部分,正变得越来越普及。今天的教程将围绕如何构建一个功能丰富的语音交互系统展开,与之前的【ESP32S3 Sense接入语音识别+MiniMax模型+TTS模块语音播报】的有所不同,这次采用Minimax语音大模型替换了TTS模块语音,而且支持多种音色、语调的调节,最后还带来了语音复刻功能,让我们一起期待接下来的作品吧
如果你是新手建议先看开箱教程:【Seeed Studio XIAO ESP32S3 Sense 开箱Arduino教程】
在这里插入图片描述

目前这是我使用的ESP32S3官方硬件👍👍👍(小小的身材有大大的力量)只需要35元加摄像头麦克风79元,后期我会整理相关专栏进行Arduino系统学习😘😘😘。有需要可以购买xiao开发板💕💕💕,SeeedXIAO ESP32S3 Sense硬件购买地址:https://s.click.taobao.com/lekazrt
在这里插入图片描述

在后续的研究过程中,我们用到了MiniMax的端到端自研多模态大语言模型和语音模型(MiniMax)。
在这里插入图片描述

首先,基于MiniMax端到端自研多模态大语言模型在大多数文本处理场景中都非常适用,它能够以自然语言交互的形式帮助企业用户或个人开发者大大提高文本相关的生产效率,如文本续写、文案生成、文本扩写、文本改写、内容摘要、代码生成、知识检索等。👍👍👍

在这里插入图片描述

其次,MiniMax语音大模型依托于新一代AI大模型,具有多品质语音复刻(TTS,即Text-to-Speech)、语音在线合成、自动适配文本等功能。👍👍👍
在这里插入图片描述

其中,能够以精准、快速的方式实现TTS是MiniMax语音大模型的一大亮点。TTS是指将文本转换为人类语音,即利用特定的声音样本来创建一个数字化的声音模型,使得生成听起来与原始声音样本非常相似的语音输出。TTS使计算机能够以自然的语音形式传达信息,促进了信息的无障碍获取和传播。
在这里插入图片描述
**简单来说,基于MiniMax的这两大模型就可以实现精准、快速的文字交互和语音复刻啦!**😁😁😁

2. 功能模块概述

首先串口输入“12345”字符切换音色,通过按键触发麦克风采集3s声音数据,对接百度在线语音识别,将返回文本结果丢入MiniMax文本大模型,进而返回第二次结果文本,实现语言对话文本效果。经过以上两次调用后,集合MiniMax语音大模型的语音复刻和MAX98357 I2S 音频放大器,就可以完整对话,实现精准的语音复刻和播报功能。

2.1 语音识别接入

百度在线语音接入教程:
【ESP32S3 Sense接入百度在线语音识别】
在这里插入图片描述

使用Seeed XIAO ESP32S3 Sense开发板接入百度智能云实现在线语音识别。自带麦克风模块用做语音输入,通过串口发送字符“1”来控制数据的采集和上传。 💕💕💕

2.2 文本大模型接入

国产大模型接入分享如下:
【ESP32接入国产大模型之MiniMax】
【ESP32接入语言大模型之智谱清言】
【ESP32接入国产大模型之文心一言】
【ESP32接入语言大模型之通义千问】

下面是不标准测评,强烈推荐使用MiniMax文本大模型
在这里插入图片描述

MiniMax是一家中国科技公司,一直致力于进行大模型相关的研究。近期提出了注册认证即赠送1亿tokensTPM 扩容不收费,为全球 AI大模型领域创业公司以及优秀的个人开发者提供了丰富的资源。
在这里插入图片描述

模型响应时间内容质量免费token次数地址
MiniMax3s8分1亿tokenhttps://www.minimaxi.com/
智谱清言7s8分300万https://open.bigmodel.cn/
文心一言10s9分500万https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Nlks5zkzu
通义千问8s8分800万https://tongyi.aliyun.com/qianwen/

结合以上图表对比,无能是回复文本的质量与速度方面,都是MiniMax领先!!!😘😘😘
这里有个细节大家需要注意:建议在MiniMax账号中充值10元,你将解锁每秒120次的调用,非常划算(否则可能玩着就没啥反应,限速了,本人在百度和MiniMax账号中都充值10元,飞一样的感觉)🤣🤣🤣

2.3 语音大模型接入

MiniMax的语音大模型的TTS模块不仅具备了理解人类语言中复杂含义的能力,而且能够生成极为贴合自然人声的语音语调,其优势显而易见。

在这里插入图片描述

  1. 与传统TTS相比,MiniMax能够智能预测文本的情绪、语调等信息,根据上下文生成更为真实、个性化的语音,使听者能够更好地理解和感受到文本所表达的意思。
  2. MiniMax的TTS模块具有高保真度,产生的语音听起来极为自然,难以辨别是否为真人录制,这在提高用户体验方面具有重要意义。
  3. MiniMax的TTS模块还具有低成本和高效率的优势,用户无需花费大量资金和时间来录制和编辑语音,只需简单输入文本即可生成高质量的语音,极大地节省了人力和物力成本。

特别值得一提的是,MiniMax的speech-01语音大模型不仅能够满足基本的语音合成需求,还能够根据不同的语境智能调整语音的情感色彩和语调,使生成的语音更加贴近文本所表达的意思和情感。此外,MiniMax的speech-02模型更是具备了强大的多语言、多性格、多场景适配能力。

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

在这里插入图片描述

总而言之,MiniMax语音大模型不仅在技术上取得了重大突破,还为用户提供了更加便捷、高效、个性化的语音合成服务,极大地丰富了用户的体验,是一款值得推荐和使用的语音合成工具。 👏👏👏

2.4 语音复刻

目前Minimax语音复刻只支持企业认证才可以开通,因此需要联系相关工作人员对接权限,才可以使用此功能,而且voice_id是和企业用户id绑定,有相关保护措施,如果不需要此功能可忽略voice_id配置,官网地址:https://www.minimaxi.com/document/guides/T2A-model/replica?id=658043250cfd44861b7f774f

在这里插入图片描述

2.4.1 录制语音mp3

首先录制语音mp3信息,具体使用教程见:【一键录音,轻松转换:用Python打造个性化音频记录工具】

下面给出python源码:
record_mp3.py

import sounddevice as sd
import soundfile as sf
from pydub import AudioSegment
import keyboard
import threading
import sys

# 设置录音参数
fs = 44100  # 采样率
is_recording = False
record_thread = None

def start_recording():
    """录音线程函数"""
    global is_recording
    try:
        print("录音开始...")
        recording = sd.rec(int(fs * 120), samplerate=fs, channels=1)
        while is_recording:
            sd.sleep(100)
        sd.stop()
        sf.write('temp_recording.wav', recording[:len(recording)], fs)
        print("录音结束.")
        convert_to_mp3()
    except Exception as e:
        print(f"录音过程中发生错误: {e}")

def convert_to_mp3():
    """转换录音为MP3格式"""
    try:
        audio = AudioSegment.from_wav("temp_recording.wav")
        audio.export("output.mp3", format="mp3")
        print("文件已转换为MP3.")
    except Exception as e:
        print(f"转换录音为MP3时发生错误: {e}")

def on_key_press(e):
    """键盘监听回调函数"""
    global is_recording
    global record_thread
    try:
        if e.event_type == 'down':
            if e.name == 'i':
                if not is_recording:
                    is_recording = True
                    record_thread = threading.Thread(target=start_recording)
                    record_thread.start()
            elif e.name == 's':
                if is_recording:
                    is_recording = False
            elif e.name == 'q' or e.name == 'Q':  # 按下'Q'或'q'键退出程序
                print("程序即将退出...")
                sys.exit()  # 使用sys.exit()安全退出程序
    except Exception as e:
        print(f"处理键盘事件时发生错误: {e}")

try:
    # 尝试注册键盘监听
    keyboard.on_press(on_key_press)
    print("按'I'键开始录音,按'S'键结束录音并转换为MP3,按'Q'键退出程序。")
    keyboard.wait()  # 保持程序运行,等待键盘事件
except KeyboardInterrupt:
    print("\n检测到键盘中断,程序正在退出...")
    sys.exit()  # 优雅地退出程序
except Exception as e:
    print(f"初始化键盘监听时发生未知错误: {e}")
    sys.exit(1)  # 如果出现其他错误,则退出程序isq

使用方法: 按’I’键开始录音,按’S’键结束录音并转换为MP3,按’Q’键退出程序。
在这个过程中采集你需要克隆的声音,比如说你自己的声音,然后朗读下面一段文字

🌹🌹🌹在那无垠的蔚蓝之中,他轻轻地展开了双翼,不是羽翼,而是梦想编织的翅膀。天空,那片浩瀚而神秘的画布,对他而言,从来就不是终点,而是一个全新的起点。他的目光穿越云层,投向更远的天际,那里有星辰大海,有未被探索的奇迹。

“天空不是他的极限”,这句话如同晨曦中的第一缕光,温暖而充满希望。它讲述的不仅仅是一个关于飞翔的故事,更是对生命无限可能的颂歌。在这个故事里,每一次跃起都是对自我的超越,每一次翱翔都是对未知世界的勇敢探索。

他相信,真正的旅程从不畏惧高度,而是渴望深邃。就如同鹰击长空,不满足于掠过云巅,而是以更高的视角,去洞察这个世界未展露的美。天空之下,是已知与习惯;天空之上,则是挑战与梦想。对他来说,天空的辽阔,是心灵的自由,是想象力驰骋的原野。

“这只是一个开始”,是他心中的宣言,是对未来无尽憧憬的低语。每一个黎明的到来,都意味着新的机会,每一次日落,则是反思与蓄力的时刻。在他的旅途中,没有终点,只有一个个崭新的起点,串联起一段段非凡的历程。

于是,他继续前行,在天空的怀抱中绘制属于自己的轨迹。不为到达,只为探索;不求征服,但求理解。天空,这个曾经被视为极限的存在,如今只是他伟大征程的起点,一个激励他不断向上、向外、向深处探索的起点。在这里,梦想与现实交织,勇气与智慧并行,一切的一切,都证明了——天空,真的只是一个开始。🌹🌹🌹

朗读完毕后会获得两个音频文件,output.mp3目标文件;temp_recording.wav中间过程文件,接下来开始上传训练文件,将voice_id成功连接到Minimax语音大模型中。

2.4.2 生成voice_id

通过下面的upload_mp3.py

import json

import requests
#替换api_key 和group_id 
api_key = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiIyMzQ1dm9yIiwiVXNlck5hbWUiOiIyYaoGpLeiViYxOmTrYQn-Rcet_aii_0CmjFz0tnklRvk0w-OZnn2a3nLQPqnAbrleWSQOzErWy1xZD8u501OPZ71ltgoci7v32baoBR7iNT-sOx0_uwdBaswIev_V8YNBns5EoSZYPW3esaRuqUM7gphVcP1P858iaW42qj_t7q0Rw4gndxtIM1dLU0NB7ON4WB3A"
group_id = "17594821804"
#音频上传
url = 'https://api.minimax.chat/v1/files/upload?GroupId='+group_id
headers1 = {
    'authority': 'api.minimax.chat',
    'Authorization': 'Bearer '+api_key
}

data = {
    'purpose': 'voice_clone'
}

files = {   
    'file': open('output.mp3', 'rb')
}
response = requests.post(url, headers=headers1, data=data, files=files)
file_id = response.json().get("file").get("file_id")
print(file_id)
#注意这里的voice_id需要自己修改,而且是唯一标识符,只能是字母组成,不能有数字
voice_id = "vor_test"
#音频复刻
url = "https://api.minimax.chat/v1/voice_clone?GroupId="+group_id
payload2 = json.dumps({
  "file_id": file_id,
  "voice_id": voice_id
})
headers2 = {
  'authorization': 'Bearer '+api_key,
  'content-type': 'application/json'
}
response = requests.request("POST", url, headers=headers2, data=payload2)
print(response.text)

修改两点
1.apikey和group_id

#替换api_key 和group_id 
api_key = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiIyMzQ1dm9yIiwiVXNlck5hbWUiOiIyYaoGpLeiViYxOmTrYQn-Rcet_aii_0CmjFz0tnklRvk0w-OZnn2a3nLQPqnAbrleWSQOzErWy1xZD8u501OPZ71ltgoci7v32baoBR7iNT-sOx0_uwdBaswIev_V8YNBns5EoSZYPW3esaRuqUM7gphVcP1P858iaW42qj_t7q0Rw4gndxtIM1dLU0NB7ON4WB3A"
group_id = "17594821804"

2.设置合适标识的voice_id

#注意这里的voice_id需要自己修改,而且是唯一标识符,只能是字母组成,不能有数字
voice_id = "vor_test"

当返回数字和success

${file_id}    
{"input_sensitive":false,"input_sensitive_type":0,"base_resp":{"status_code":0,"status_msg":"success"}}    

在这里插入图片描述

3. 先决条件

这一次还是采用Arduino编程就会轻松许多开发。**这样就可以实现40元装进大模型交互以及语音复刻啦!**🤣🤣🤣

在继续此项目之前,请确保检查以下先决条件。我们将使用 Arduino IDE 对 ESP32/ESP8266 开发板进行编程,因此在继续本教程之前,请确保已在 Arduino IDE 中安装这些开发板。💕💕💕

3.1 硬件准备

要学习本教程,您需要1个ESP32S3 Sense,建议使用ESP32S3,笔者发现同样的代码后者可以轻松调用,ESP32真不行

名称端口连接商品地址价格/元
ESP32S3 XIAO开发板/https://s.click.taobao.com/lekazrt35
MAX9814麦克风模块GPIO2/OUThttps://m.tb.cn/h.gd0d6G16.5
MAX98357 I2S 音频放大器模块GPIO6/DIN GPIO5/CLK GPIO4/LRChttps://s.click.taobao.com/79dqAnt5.65
扬声器模块接音频放大器输出https://s.click.taobao.com/FPpDnnt4.5
大按键GPIO3/OUThttps://s.click.taobao.com/jTRoAnt0.6
母对母杜邦线/https://s.click.taobao.com/7bVGnnt0.7
总价52.86

🌹🌹🌹请大家多多支持我推广的硬件,只有点击链接加购物车并且一起购买才有效,虽然只有1.5%的返利,这对我持续开源有重要帮助,鹏鹏期待您的支持💕💕💕

3.2 软件准备

  1. Arduino IDE:下载并安装 Arduino IDE;
  2. ESP32 开发板库:在 Arduino IDE 中添加 ESP32 支持;
    参考博客:【esp32c3配置arduino IDE教程】
    为安装过程留出一些时间,具体时间可能因您的互联网连接而异。
  3. ESP32-audioI2S库安装
  1. ArduinoJson库安装
    在这里插入图片描述
    安装成功如上

3.3 接线

为了更好的实现语音复刻和播报,我们还在模型中集成了MAX98357 I2S 音频放大器模块。😃😃😃

只需要将ESP32的I2S接口与MAX98357的相应引脚连接即可。具体接线如下:

  • XIAO D1连接到MAX9814的OUT
  • XIAO D2连接到按键的OUT
  • XIAO D3连接到MAX98357的LRC
  • XIAO D4连接到MAX98357的BCLK
  • XIAO D5连接到MAX98357的DIN

注意:

  1. XIAO开发板的GND和VCC不够,可以采用两根母母杜邦线中间铰接,采用热缩管或胶带封装,即可实现1拖3功能。
  2. 其他端口悬空就好
    在这里插入图片描述
    最后的接线图效果如下
    在这里插入图片描述

4. 核心代码

下面准备进行了基于ESP32S3 Sense 的硬件测试,此部分有源码分享和代码解析两部分

4.1 源码分享

Arduino代码如下

#include <Arduino.h>
#include "base64.h"
#include "WiFi.h"
#include "HTTPClient.h"
#include "cJSON.h"
#include <ArduinoJson.h>
#include "Audio.h"
// #按键和max9814麦克风
#define key 3
#define ADC 2
//max98357扬声器引脚
#define I2S_DOUT 6  // DIN connection
#define I2S_BCLK 5  // Bit clock
#define I2S_LRC 4   // Left Right Clock
Audio audio;
HTTPClient http,http1,http2;
String voice_id = "female-tianmei-jingpin";  //青年大学生音色:male-qn-daxuesheng;甜美女性音色:female-tianmei;男性主持人:presenter_male;女性主持人:presenter_female
// 1. Replace with your network credentials
const char *ssid = "J09 502";
const char *password = "qwertyuiop111";
// 2. Check your Aduio port
const int ledPin = 21;  // the number of the LED pin
hw_timer_t *timer = NULL;
const int adc_data_len = 16000 * 3;
const int data_json_len = adc_data_len * 2 * 1.4;
uint16_t *adc_data;
char *data_json;
uint8_t adc_start_flag = 0;     //开始标志
uint8_t adc_complete_flag = 0;  //完成标志


// 3. Replace with your MiniMax API key
const char *mini_apiKey = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiIyMzQ1dm9yIiwiVXNlck5hWfIf40Z_X8Aog3_bUAqsqZZmbb7ig7zTDAfoAWjmeUHdQ1TX16rg6dV5aPuJj02yU6gjROQZZYUPk3lUqdHV2WLUvQcZFlerkW0F9AtkfLblDCyEEHvQLG5dbcPCTGiRWLt3I3AZ_l1YSWmPJkazrQPUN5BVlb5AHjIVKwWyETDd63eydY7DmpqOSGJYjc4sRJFMCccpOjTa66iLBhfBsrkfZsg";
const char *tts_url = "https://api.minimax.chat/v1/t2a_pro?GroupId=17595904";
const char *chat_url = "https://api.minimax.chat/v1/text/chatcompletion_v2";
const char *stt_url = "http://vop.baidu.com/server_api";
// 4. Replace with your baidu voice detect token
String baidu_token = "24.d977d47af91cdbb34fee2b2282335-57722200";
String mini_token_key = String("Bearer ") + mini_apiKey;
// Send request to MiniMax API
String inputText = "你好,minimax!";

String response, question, aduiourl;
String answer = "我是鹏鹏的小助手,你好鸭";
DynamicJsonDocument jsonDoc(1024);
uint32_t num = 0;
uint32_t time1, time2;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
// 函数声明
void IRAM_ATTR onTimer();
String sendToSTT();
String getGPTAnswer(String inputText);
String getvAnswer(String ouputText);

void setup() {
  Serial.begin(115200);
  adc_data = (uint16_t *)ps_malloc(adc_data_len * sizeof(uint16_t));  //ps_malloc 指使用片外PSRAM内存
  if (!adc_data) {
    Serial.println("Failed to allocate memory for adc_data");
  }
  data_json = (char *)ps_malloc(data_json_len * sizeof(char));  // 根据需要调整大小
  if (!data_json) {
    Serial.println("Failed to allocate memory for data_json");
  }
  pinMode(ADC, ANALOG);
  pinMode(key, INPUT_PULLUP);
  audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
  audio.setVolume(18);  // 0...21
  pinMode(ledPin, OUTPUT);
  uint8_t count = 0;
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    count++;
    if (count >= 75) {
      Serial.printf("\r\n-- wifi connect fail! --");
      ESP.restart();  // 执行软件重启
    }
    vTaskDelay(200);
  }
  Serial.printf("\r\n-- wifi connect success! --\r\n");
  http.setTimeout(5000);
  // gain_token();
  //设置定时器16k采样音频
  timer = timerBegin(0, 40, true);    //  80M的时钟 40分频 2M
  timerAlarmWrite(timer, 125, true);  //  2M  计125个数进中断  16K
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmEnable(timer);
  timerStop(timer);  //先暂停
}

void loop() {
  audio.loop();
  if (digitalRead(key) == 1) {
    delay(10);
    if (digitalRead(key) == 1) {
      Serial.printf("Start recognition\r\n");
      digitalWrite(ledPin, LOW);
      adc_start_flag = 1;
      timerStart(timer);
      while (!adc_complete_flag)  //等待采集完成
      {
        ets_delay_us(10);
      }
      timerStop(timer);
      adc_complete_flag = 0;  //清标志
      digitalWrite(ledPin, HIGH);
      question = sendToSTT();
      if (question != "error") {
        Serial.println("Input:" + question);
        answer = getGPTAnswer(question);
        if (answer != "error") {
          Serial.println("Answer: " + answer);
          aduiourl = getvAnswer(answer);
          if (aduiourl != "error") {
            audio.stopSong();
            audio.connecttohost(aduiourl.c_str());  //  128k mp3
          }
        }
      }
      Serial.println("Recognition complete\r\n");
    }
  }
  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;
       case '5':
       //5.replace your clone voice_id
        voice_id = "vor_test";
        break;
    }
    Serial.println(voice_id);
  }
  vTaskDelay(5);
}
//录音函数
void IRAM_ATTR onTimer() {
  // Increment the counter and set the time of ISR
  portENTER_CRITICAL_ISR(&timerMux);
  if (adc_start_flag == 1) {
    //Serial.println("");
    adc_data[num] = analogRead(ADC);
    num++;
    if (num >= adc_data_len) {
      adc_complete_flag = 1;
      adc_start_flag = 0;
      num = 0;
      //Serial.println(Complete_flag);
    }
  }
  portEXIT_CRITICAL_ISR(&timerMux);
}

//stt语音识别
String sendToSTT() {
  memset(data_json, '\0', data_json_len * sizeof(char));
  strcat(data_json, "{");
  strcat(data_json, "\"format\":\"pcm\",");
  strcat(data_json, "\"rate\":16000,");
  strcat(data_json, "\"dev_pid\":1537,");
  strcat(data_json, "\"channel\":1,");
  strcat(data_json, "\"cuid\":\"57722200\",");
  strcat(data_json, "\"token\":\"");
  strcat(data_json, baidu_token.c_str());
  strcat(data_json, "\",");
  sprintf(data_json + strlen(data_json), "\"len\":%d,", adc_data_len * 2);
  strcat(data_json, "\"speech\":\"");
  strcat(data_json, base64::encode((uint8_t *)adc_data, adc_data_len * sizeof(uint16_t)).c_str());
  strcat(data_json, "\"");
  strcat(data_json, "}");
  http.begin(stt_url);  //https://vop.baidu.com/pro_api
  http.addHeader("Content-Type", "application/json");
  // Serial.print(data_json);
  int httpResponseCode = http.POST(data_json);
  if (httpResponseCode == 200) {
    response = http.getString();
    http.end();
    Serial.print(response);
    deserializeJson(jsonDoc, response);
    String question = jsonDoc["result"][0];
    // 访问"result"数组,并获取其第一个元
    return question;
  } else {
    http.end();
    Serial.printf("stt_error: %s\n", http.errorToString(httpResponseCode).c_str());
    return "error";
  }
}
//chatgpt对话
String getGPTAnswer(String inputText) {
  http1.begin(chat_url);
  http1.addHeader("Content-Type", "application/json");
  http1.addHeader("Authorization", mini_token_key);
  String payload = "{\"model\":\"abab5.5s-chat\",\"messages\":[{\"role\": \"system\",\"content\": \"你是鹏鹏的生活助手机器人,要求下面的回答严格控制在256字符以内。\"},{\"role\": \"user\",\"content\": \"" + inputText + "\"}]}";
  int httpResponseCode = http1.POST(payload);
  if (httpResponseCode == 200) {
    response = http1.getString();
    http1.end();
    Serial.println(response);
    deserializeJson(jsonDoc, response);
    String answer = jsonDoc["choices"][0]["message"]["content"];
    return answer;
  } else {
    // http1.end();
    response = http1.getString();
    http1.end();
    Serial.println(response);
    Serial.printf("chatError %i \n", httpResponseCode);
    return "error";
  }
}
//tts语音播报
String getvAnswer(String ouputText) {
  http2.begin(tts_url);
  http2.addHeader("Content-Type", "application/json");
  http2.addHeader("Authorization", mini_token_key);
  // 创建一个StaticJsonDocument对象,足够大以存储JSON数据
  StaticJsonDocument<200> doc;
  // 填充数据
  doc["text"] = ouputText;
  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 httpResponseCode = http2.POST(jsonString);
  if (httpResponseCode == 200) {
    response = http2.getString();
    Serial.println(response);
    http2.end();
    deserializeJson(jsonDoc, response);
    String aduiourl = jsonDoc["audio_file"];
    return aduiourl;
  } else {
    Serial.printf("tts %i \n", httpResponseCode);
    http2.end();
    return "error";
  }
}
// optional
void audio_info(const char *info) {
  Serial.print("info        ");
  Serial.println(info);
}
void audio_eof_mp3(const char *info) {  //end of file
  Serial.print("eof_mp3     ");
  Serial.println(info);
}

4.2 代码解析

这段代码是一个用于Arduino平台的项目,实现了一个集成语音识别(STT)、自然语言处理(通过ChatGPT类似接口)以及语音合成(TTS)功能的智能语音交互系统。主要功能包括通过麦克风录制语音、将录音转换为文本、与远程服务器进行对话处理并获取回复、最后将回复文本转为语音播放。以下是代码的主要部分解析:

注意:需要根据自己的硬件接口修改以上五个位置,并且voice_id需要更换,才可以实现语音克隆与对话👍👍👍
如果不需要克隆功能可忽略voice_id配置,因此也不能输入“5”


// 1. Replace with your network credentials
const char *ssid = "J09 502";
const char *password = "qwertyuiop111";
// 2. Check your Aduio port
// #按键和max9814麦克风
#define key 3
#define ADC 2
//max98357扬声器引脚
#define I2S_DOUT 6  // DIN connection
#define I2S_BCLK 5  // Bit clock
#define I2S_LRC 4   // Left Right Clock

// 3. Replace with your MiniMax API key
const char *mini_apiKey = "eyJhbGciOiJSUzI1NiHjIVKwWyETDd63eydY7DmpqOSGJYjc4sRJFMCccpOjTa66iLBhfBsrkfZsg";
const char *tts_url = "https://api.minimax.chat/v1/t2a_pro?GroupId=17595904";

// 4. Replace with your baidu voice detect token
String baidu_token = "24.d977d47af91cdbb34fe7722200";

//5.replace your clone voice_id
voice_id = "vor_test";

4.2.1 初始化与设置

  • 包含头文件:引入了必要的库,如Arduino核心库、Base64编码/解码库、WiFi和HTTP客户端库、JSON解析库等。
  • 定义引脚和变量:设置了按键输入、模拟输入(用于麦克风)、I2S输出(用于扬声器)、定时器、内存分配等。
  • 网络配置与初始化:配置了Wi-Fi连接信息,确保设备能接入指定的无线网络。
  • 音频硬件设置:配置了Audio库来管理扬声器输出。
  • API密钥与URL:包含了访问MiniMax语音服务和百度语音识别服务所需的API密钥及URL。

4.2.2 功能函数

  1. setup(): 进行一次性的初始化工作,如启动串口通信、分配内存、设置引脚模式、连接Wi-Fi、初始化定时器等。
  2. loop(): 主循环,监测按键状态,触发语音识别流程。当按键按下,开始录音,录音结束后调用语音识别、ChatGPT对话处理、并将回复通过TTS转换为语音播放。
  3. IRAM_ATTR onTimer(): 定时器中断服务函数,用于周期性地从麦克风采集模拟信号。
  4. sendToSTT(): 将录制的音频数据转换为Base64编码,并构造JSON请求发送至百度语音识别服务,返回识别的文本。
  5. getGPTAnswer(): 使用ChatGPT类似的接口,根据用户输入的问题获取AI回复。
  6. getvAnswer(): 将获取到的文字回复通过MiniMax的TTS服务转换为语音链接,以便后续播放。

4.2.3 注意点

  • 内存管理:使用ps_malloc动态分配内存来存储大量数据,如音频样本和JSON数据,这适合于具有外部PSRAM的Arduino板。
  • 错误处理:在与远程API交互时有基本的错误检查,例如HTTP响应码验证。
  • 多任务处理:虽然没有直接使用操作系统级的多任务,但通过定时器和中断实现了异步录音和主循环的并发操作。

此代码展示了如何在嵌入式设备上实现复杂的语音交互应用,结合了网络通信、音频处理、JSON数据处理等技术。

5. 播放文本

5.1 下载代码

先上烧录配置,请仔细一一比对

注意要选opi psram,3MB APP存储
在这里插入图片描述

5.2 实现效果

在ESP32上运行上述代码后,它将自动连接到WiFi并开始播放指定的MP3音乐文件。你也可以通过串口发送12345分别选择//青年大学生音色:male-qn-daxuesheng;甜美女性音色:female-tianmei;男性主持人:presenter_male;女性主持人:presenter_female;克隆的声音:vor_test。切换音色以实现在线播放。
打开串口115200波特率,选择没有结束符,输入1等待答复
在这里插入图片描述
切换音色后,就可以通过按钮触发语音对话功能

重磅预告:你是否觉得这还不够快,迅速。后期可采用Minimax的Assistant助手功能,预计大大缩短对话响应。看下图😘😘😘

在这里插入图片描述

6. 总结

博主强烈推荐大家使用🎈🎈🎈MiniMax文本大模型MiniMax语音大模型,支持国产! 通过这两个模型的结合,不仅能够提高对话系统的准确性和流畅度,还能够为大家带来更加逼真、愉悦的交互体验。无论是在智能助手、虚拟客服还是其他对话应用中,这种结合将大大提升系统的人机交互质量,增强用户的参与感和满意度💕💕💕

  1. MiniMax 文本大模型对话模块具有出色的文本理解和生成能力,能够准确理解用户的输入并生成自然流畅的回复。这意味着即使是复杂的对话情境,系统也能够以符合人类逻辑的方式进行交互,使大家感受到真正的智能对话体验。👍👍👍
  2. 而结合 MiniMax 语音大模型,则可以将这种优秀的文本内容转化为生动、自然的语音输出。通过 TTS 技术,系统能够以多样化的语调、语速和语音风格将文本转化为声音,为大家带来更加丰富、沉浸式的交互体验。👍👍👍

🥳🥳🥳现在,我们在本教程中,您学习了如何【ESP32S3 接入MiniMax文本语音大模型对话&语音克隆教程】,从而实现语音复刻和播报功能。这不仅能够为您的项目增加语音交互功能,还能够提升用户体验。🛹🛹🛹希望本教程能够作为您开始探索语音合成技术的一个起点,随着技术的不断进步,未来将有更多创新的应用等待着我们去发现和实现。🥳🥳🥳科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣

参考资料:

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

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2345VOR

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值