#include <freertos/stream_buffer.h>
/********************************* 一些公共函数,不用管 ***********************************/
#define BUZZER_PIN 4 // 蜂鸣器的引脚
#define BUZZER_CHANNEL 0 // PWM通道
// 蜂鸣器发声结构体
typedef struct {
int note; // 音符
int octave; // 八度音阶
int rest; // 播放时长
} BuzzerNote;
/**
* @brief 音频解码算法
* 对输入的字符串进行音频解码,并通过PWM控制蜂鸣器播放
* @param[in] music 音频字符串,格式为 音符,八度,间隔时间;下一个音符,
*/
static void decode(String music) {
ledcAttachPin(BUZZER_PIN, BUZZER_CHANNEL); // 初始化PWM,指定使用引脚和PWM通道
BuzzerNote note; // 定义音符结构
if(music.indexOf(';')==0){
music = music.substring(1,music.length());
}
do {
int index = music.indexOf(';'); // 找到下一个音符开始的位置
String val = music.substring(0, index);
note.note = val.substring(0, 1).toInt();
note.octave = val.substring(2, 3).toInt();
note.rest = val.substring(4, 7).toInt();
ledcWriteNote(BUZZER_CHANNEL, (note_t)note.note, note.octave); // 播放音符
printf("[PLAY] %d,%d,%d , %s\n",note.note,note.octave,note.rest,val.c_str());
vTaskDelay(note.rest); // 播放间隔
// 获取下一个音符
if (music.indexOf(';') < 0) music = "";
music = music.substring(index + 1, music.length());
} while (music.length() > 0);
ledcDetachPin(BUZZER_PIN);
}
/**
* @brief 随机生成音符
* @return 返回随机生成的音符序列,每组音频共有8个字符组成
*/
static String randomMusic() {
String randomNote;
for (int i = 0; i < random(5, 20); i++) { // 随机生成5~20组音符
char note[10];
sprintf(note, "%d,%d,%d", random(0, 9), random(5, 9), random(100, 999)); // 随机一个音符格式为 音符,八度,间隔时间
randomNote = randomNote + ";" + String(note); // 多个音符之间用 ; 分割
}
return randomNote;
}
/********************************* 留缓冲区例程 ***********************************/
#define BUFFER_SIZE 512 // 留缓冲区大小
#define TRIGGER_LEVEL 8 // 最小读出单位
#define READ_SIZE 80 // 最大读出宽度
StreamBufferHandle_t xStreamMusic = NULL; // 声明一个流缓冲区指针
uint16_t read_counter=0, write_counter=0; // 读出和写入次数计数器,纯计数没卵用
// 模拟下载线程
void download_task(void* param_t){
printf("[DOWN] 下载器启动...\n");
String music; // 下载待写入的数据
size_t size; // 存放实际写入数据的大小
while(1){
// 模拟从网上下载随机长度的音乐
music = randomMusic();
// 并写入到流缓冲区中,并返回实际写入的数据长度
size = xStreamBufferSend(xStreamMusic, // 缓冲区句柄
(void *)&music, // 要写入的数据
music.length(), // 写入的数据大小
portMAX_DELAY); // 当缓冲区满后的等待时间
if(size != music.length()){
// 如果实际写入的大小与音乐本身大小不一致,说明缓冲区写入满了,无法再继续写入
printf("[DOWN] 音乐写入失败,缓冲区已满!%d - %d\n",size, music.length());
}else{
printf("[DOWN] 第 %d 次写入,大小 %d, 数据大小:%d, 数据:[%S]\n", ++write_counter, size, music.length(), music.c_str());
}
vTaskDelay(pdMS_TO_TICKS(random(100,500)));
}
}
// 模拟播放线程
void paly_task(void* param_t){
printf("[PLAY] 播放器启动...\n");
size_t size; // 存放实际读出数据的大小
String music=""; // 读出数据的存放位置
while(1){
printf("[PLAY] 准备读取音乐\n");
size = xStreamBufferReceive(xStreamMusic, // 流缓冲区指针
(void *)&music, // 读出数据的存放变量指针
READ_SIZE, // 预读出大小
portMAX_DELAY ); // 等待时间
if(size>0){
// 解码播放音乐
printf("[PLAY] 第 %d 次读出,大小 %d, 数据大小:%d, 数据:[%S]\n",
++read_counter, size, music.length(), music.c_str());
decode(music);
printf("[PLAY] 第 %d 段播放完毕!\n",read_counter);
}
}
}
// 数据监控线程
void monitor_task(void* param_t){
while(1){
if (xStreamBufferIsFull(xStreamMusic) == pdTRUE){
printf("[MONI] 缓冲区已满!\n");
}else{
printf("[MONI] 已用 : %d , 剩余 : %d\n", xStreamBufferBytesAvailable(xStreamMusic),
xStreamBufferSpacesAvailable(xStreamMusic));
}
vTaskDelay(1000);
}
}
void setup() {
Serial.begin(115200);
Serial.println("Hello, ESP32-S3!");
pinMode(BUZZER_PIN, OUTPUT);
ledcSetup(0, 5000, 16); // 初始化PWM
// ledcWriteNote(BUZZER_CHANNEL, (note_t)6, 4);
// vTaskDelay(1000);
// decode(";1,8,999;2,8,999");
// uint8_t ucRxData[ 20 ];
// // 初始化流缓冲区
xStreamMusic = xStreamBufferCreate(BUFFER_SIZE, TRIGGER_LEVEL);
if ( xStreamMusic == NULL ){
printf("流缓冲区初始化失败,请检查内存是否够用!\n");
}
// 创建线程
xTaskCreate(download_task, "Downloader", 10240, NULL, 1, NULL);
xTaskCreate(monitor_task, "Monitor", 10240, NULL, 1, NULL);
vTaskDelay(10000);
xTaskCreate(paly_task, "Player", 10240, NULL, 1, NULL);
}
void loop() {
delay(10);
}
esp:0
esp:1
esp:2
esp:3
esp:4
esp:5
esp:6
esp:7
esp:8
esp:9
esp:10
esp:11
esp:12
esp:13
esp:14
esp:15
esp:16
esp:17
esp:18
esp:19
esp:20
esp:21
esp:35
esp:36
esp:37
esp:38
esp:39
esp:40
esp:41
esp:42
esp:45
esp:46
esp:47
esp:48
esp:3V3.1
esp:3V3.2
esp:RST
esp:5V
esp:GND.1
esp:GND.2
esp:TX
esp:RX
esp:GND.3
esp:GND.4
bz1:1
bz1:2