#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