#include <freertos/stream_buffer.h>
/********************************* 一些公共函数,不用管 ***********************************/
#define BUZZER_PIN 4 // 蜂鸣器的引脚
#define BUZZER_CHANNEL 0 // PWM通道
/**
* @brief 音频解码算法
* 对输入的字符串进行音频解码,并通过PWM控制蜂鸣器播放
* @param[in] music 音频字符串,格式为 音符,八度,间隔时间;下一个音符,
* @怕扰民[in] size 长度
*/
static void decode(char *notes, size_t size) {
ledcAttachPin(BUZZER_PIN, BUZZER_CHANNEL); // 初始化PWM,指定使用引脚和PWM通道
size_t len = size/3;
for(int i=0; i<len; i++){
int note = notes[i*3];
int octave = notes[i*3+1];
int rest = notes[i*3+2]*5;
ledcWriteNote(BUZZER_CHANNEL, (note_t)note, octave);
vTaskDelay(rest);
}
ledcDetachPin(BUZZER_PIN);
}
/**
* @brief 随机生成音符
* param[out] notes 音符序列
* @return 返回字符串长度
*/
static size_t randomMusic(char **notes) {
uint8_t len = random(1, 21); // 生成1~20 组音符,每组有3位
char *p =(char *)pvPortMalloc(sizeof(char)*len*3); // 动态分配内存
// 随机生成数据,第一个字节数据是0~12,第二个是5~9,第三个是20~200的5倍
for(int i=0; i<len; i++){
p[i*3] = random(0,13);
p[i*3+1] = random(5,10);
p[i*3+2] = random(20,200);
}
*notes = p;
return len*3;
}
/**
* @brief 测试用打印音乐
* @param[in] notes 音乐数据序列
* @param[in] size 音乐长度,3个一组打印
**/
static void printMusic(char *notes, size_t size){
// taskENTER_CRITICAL();
printf("[");
for(int i=0; i<size; i++){
if(i % 3 ==0){printf(" ");}
printf("%d,", notes[i]);
}
printf("]\n");
// taskEXIT_CRITICAL();
}
/********************************* 留缓冲区例程 ***********************************/
#define BUFFER_SIZE 600 // 留缓冲区大小
#define TRIGGER_LEVEL 3 // 最小读出单位
#define READ_SIZE 90 // 最大读出宽度
StreamBufferHandle_t xStreamMusic = NULL; // 声明一个流缓冲区指针
uint16_t read_counter=0, write_counter=0; // 读出和写入次数计数器,纯计数没卵用
// 模拟下载线程
void download_task(void* param_t){
printf("[DOWN] 下载器启动...\n");
char *music= NULL; // 下载待写入的数据
size_t music_size; // 随机生成的音乐大小
size_t size; // 存放实际写入数据的大小
while(1){
// 模拟从网上下载随机长度的音乐
music_size = randomMusic(&music);
printf("[DOWN] 随机生成音乐长度: %d \n", music_size);
// printMusic(music, music_size);
// 并写入到流缓冲区中,并返回实际写入的数据长度
size = xStreamBufferSend(xStreamMusic, // 缓冲区句柄
(void *)music, // 要写入的数据
music_size, // 写入的数据大小
portMAX_DELAY); // 当缓冲区满后的等待时间
vPortFree(music);
if(size != music_size){
// 如果实际写入的大小与音乐本身大小不一致,说明缓冲区写入满了,无法再继续写入
printf("[DOWN] 音乐写入失败,缓冲区已满!%d - %d\n",size, music_size);
}else{
printf("[DOWN] 第 %d 次写入,大小 %d\n", ++write_counter, size);
}
vTaskDelay(pdMS_TO_TICKS(random(100,500)));
}
}
// 模拟播放线程
void paly_task(void* param_t){
printf("[PLAY] 播放器启动...\n");
size_t size; // 存放实际读出数据的大小
char music[READ_SIZE]; // 读出数据的存放位置
while(1){
printf("[PLAY] 准备读取音乐\n");
size = xStreamBufferReceive(xStreamMusic, // 流缓冲区指针
(void *)music, // 读出数据的存放变量指针
READ_SIZE, // 预读出大小
portMAX_DELAY); // 等待时间
if(size>0){
// 解码播放音乐
printf("[PLAY] 第 %d 次读出,大小 %d\n", ++read_counter, size);
// printMusic(music, size);
decode(music, size);
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
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);
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