// Bad Apple RESPECT!
/*
作品名稱:Bad Apple 播放器
功能:
1. 播放 Bad Apple PV(on 6x8 led matrix) 及 音樂(by buzzer)
2. 使用 SD card 存放字形檔,以解決記憶體不足的問題
3. 使用多執行續使得音畫能同時播出
*/
// include necessary standard library
#include <string>
#include <vector>
#include <iostream>
using namespace std;
// include SD card library
#include "SD.h"
// include libraries for threads
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "myPin.h"
#include "support.h"
// audio functions
void playTone(double frequency, double duration) {
// set PWM frequency
ledcWriteTone(0, frequency);
delay(duration);
ledcWrite(0, 0);
}
/*
【音符設定】 頻,長,連與否(1與0),降(1)升(2)無(0)
設定四分音符為一拍
四分音符的Do - 1,1
四分音符的Re - 2,1
八分音符的Do - 1,0.5
四分音符的高音Do(更高音以此類推) - 1.,1
四分音符的低音Do(更低音以此類推) - .1,1
四分音符的休止符 - 0,1
連音(標註於第一個音) - 1,1,1 例如:連音的四分音符Do-Re-Me為 1,1,1\n2,1,1\n3,1
四分音符的打擊 - x,1
四分音符的降So - 5,1,0,1
四分音符的升Fa - 4,1,0,2
*/
void play_music(void *audioFilePointer){
// cout << "start" << endl;
File audioFile = *((File *)audioFilePointer); // 取得傳入的參數
for (int i = 0; i < SHEET_LENGTH; i++){
string line = getLine(audioFile);
vector<string> v = split(line, ",");
float freq = stof(find_freq(v[0] + v[3]));
if (stoi(v[2])){
float duration = stof(v[1]) * QUARTER_NOTE_DURATION;
playTone(freq, duration);
}else{
float duration = stof(v[1]) * (QUARTER_NOTE_DURATION - SHUT_DURATION);
float shut_duration = stof(v[1]) * (SHUT_DURATION);
playTone(freq, duration);
delay(shut_duration);
}
}
// cout << "finished" << endl;
vTaskDelete(NULL); // 任務執行完畢後刪除自身
}
void setup() {
Serial.begin(115200);
set_pin_mode();
set_buzz_PWM();
load_SD_card();
set_key_dict("42");
// reading file from the card:
File screenFile = SD.open("/map.txt", FILE_READ);
File audioFile = SD.open("/sheet.txt", FILE_READ);
// check file is open
if (!screenFile) {
Serial.println("screenFile file was not opened");
return ;
}
if (!audioFile) {
Serial.println("file was not opened");
return ;
}
// use thread to play LED and music at the same time
// play LED
xTaskCreate(play_LED, "Play LED", 5000, &screenFile, 1, NULL);
// play music
play_music(&audioFile);
}
void loop() {
delay(50000);
}
// led functions
void play_LED(void *screenFilePointer){
File screenFile = *((File *)screenFilePointer); // 取得傳入的參數
for (int i = 0; i < PIC_NUM; i++){
char* matrix[HEIGHT];
for (int j = 0; j < HEIGHT; j++){
char* line_temp = new char[WIDTH];
for (int k = 0; k < WIDTH; k++){
line_temp[k] = screenFile.read();
}
screenFile.read(); // skip '\n'
screenFile.read(); // skip unknown char
matrix[j] = line_temp;
}
draw(matrix, FPS);
for (int j = 0; j < HEIGHT; j++){
delete[] matrix[j];
}
delay(REVISION_MS);
}
vTaskDelete(NULL); // 任務執行完畢後刪除自身
}
void closeAllLight(){
for (int p: pinLineHeadArr){
digitalWrite(p, HIGH);
}
for (int p: pinColTopArr){
digitalWrite(p, LOW);
}
}
void draw(char* matrix[HEIGHT], int fps){
float time_start = millis(); // get start time (ms)
float wait_ms = 1000.0 / fps;
while (millis() - time_start < wait_ms){
for (int i = 0; i < HEIGHT; i++){
drawLine(i, matrix[i]);
delay(DELAY_MS);
closeAllLight();
}
}
}
void drawLine(int lineNum, char* pos){
for (int i = 0; i < HEIGHT; i ++){
if (i != lineNum){
digitalWrite(pinLineHeadArr[i], HIGH); // high to close the line
}else{
digitalWrite(pinLineHeadArr[i], LOW);
}
}
for (int i = 0; i < WIDTH; i ++){
byte num = pos[i] - '0';
digitalWrite(pinColTopArr[i], num);
}
}