// ᗜ‿ᗜ
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
#include "SD.h"
#include "myTone.h"
// Define SD card connection
#define SD_MOSI_PIN 23
#define SD_MISO_PIN 19
#define SD_SCLK_PIN 18
#define SD_CS_PIN 5
#define BUZZER_PIN 21
// Define music data
#define BEAT_FREQUENCY 50
// #define QUARTER_NOTE_DURATION 433.86 // ms // (bad apple BPM = 138)
#define QUARTER_NOTE_DURATION 416.66 // ms // (otenbakoimusume BPM = 144)
#define SHUT_DURATION 200
#define SHEET_LENGTH 917
// Define buzzer pins
#define BUZZER_PIN_1 21
#define BUZZER_PIN_2 15
#define BUZZER_PIN_3 2
#define BUZZER_PIN_4 4
// Configure PWM parameters
#define TIMER_FREQUENCY 5000 // Timer frequency in Hz
#define CHANNEL_1 0
#define CHANNEL_2 1
#define RESOLUTION 8 // PWM resolution (bits)
// init settings
void load_SD_card(){
// load SD card
if (!SD.begin(SD_CS_PIN)) {
Serial.println("Card initialization failed!");
while (true);
}
}
// key to C0 function
string* key_to_C0[61]; // example: {{"10", "42"},...} for F♯大調
void set_key_dict(string key){
/*
example key: "42" -> F♯大調
example key: "10" -> C大調
*/
// 10,12(21),20,22,30,40,42,50,52,60,62,70 共 12 鍵,而 p2=(p+1)1 for p = 1,2,4,5,6
for (string* p: key_to_C0){
delete [] p;
}
if (key[1] == '1'){
key[0] = key[0]-1;
key[1] = '2';
}
string arr[12] = {"10","12","20","22","30","40","42","50","52","60","62","70"};
int push_num;
for (int i = 0; i < 12; i++){
if (arr[i] == key){
push_num = i;
break;
}
}
string temp[84] = {"...10","...12","...20","...22","...30","...40","...42","...50","...52","...60","...62","...70","..10","..12","..20","..22","..30","..40","..42","..50","..52","..60","..62","..70",".10",".12",".20",".22",".30",".40",".42",".50",".52",".60",".62",".70","10","12","20","22","30","40","42","50","52","60","62","70","1.0","1.2","2.0","2.2","3.0","4.0","4.2","5.0","5.2","6.0","6.2","7.0","1..0","1..2","2..0","2..2","3..0","4..0","4..2","5..0","5..2","6..0","6..2","7..0","1...0","1...2","2...0","2...2","3...0","4...0","4...2","5...0","5...2","6...0","6...2","7...0"};
for (int i = 0; i < 60; i++){
string* p = new string[2];
p[0] = temp[12 - push_num + i]; p[1] = temp[12 + i];
key_to_C0[i] = p;
}
string* p = new string[2];
p[0] = "00"; p[1] = "00";
key_to_C0[60] = p;
}
int line_num = 0;
string getLine(File textFile){
if (!textFile.available()){
Serial.println("no more lines in file!");
while (true);
}
line_num += 1;
string result;
char c_temp = textFile.read();
while (c_temp != '\n'){
result += c_temp;
c_temp = textFile.read(); // output an unknown char if read a '\n'
}
result.erase(result.size() - 1); // remove an unknown char at last
if (result.size() == 0){
result = getLine(textFile);
}
if (result[0] == '/' && result[1] == '/'){
result = getLine(textFile);
}
return result;
}
const vector<string> split(const string& str, const string& pattern) {
vector<string> result;
string::size_type begin, end;
end = str.find(pattern);
begin = 0;
while (end != string::npos) {
if (end - begin != 0) {
result.push_back(str.substr(begin, end-begin));
}
begin = end + pattern.size();
end = str.find(pattern, begin);
}
if (begin != str.length()) {
result.push_back(str.substr(begin));
}
return result;
}
string find_freq_from_C0(string s){
if (s[0] == 'x'){
return to_string(BEAT_FREQUENCY);
}
for (string* s_arr:tone_hz_arr_C0){
if (s_arr[0] == s){
return s_arr[1];
}
}
return "error";
}
string find_freq(string s){
if (s[0] == 'x'){
return to_string(BEAT_FREQUENCY);
}
if (s[s.size()-1] == '1'){
s[0] = s[0]-1;
s[s.size()-1] = '2';
}
for (string* s_arr: key_to_C0){
if (s_arr[0] == s){
return find_freq_from_C0(s_arr[1]);
}
}
return "error";
}
// tone object
int buzzer_pin_arr[] = {BUZZER_PIN_1, BUZZER_PIN_2, BUZZER_PIN_3, BUZZER_PIN_4};
void IRAM_ATTR onTimer1() {
static bool state = false;
// change state
digitalWrite(buzzer_pin_arr[0], state);
state = !state;
}
void IRAM_ATTR onTimer2() {
static bool state = false;
// change state
digitalWrite(buzzer_pin_arr[1], state);
state = !state;
}
void IRAM_ATTR onTimer3() {
static bool state = false;
// change state
digitalWrite(buzzer_pin_arr[2], state);
state = !state;
}
void IRAM_ATTR onTimer4() {
static bool state = false;
// change state
digitalWrite(buzzer_pin_arr[3], state);
state = !state;
}
void (*onTimerFuncArr[])() = {&onTimer1, &onTimer2, &onTimer3, &onTimer4};
void playTone(float freq, float duration, int buzzer_num = 0){
if (freq <= 20 || freq >= 20000) {
delay(duration);
return;
}
// set
int timer_num = buzzer_num;
hw_timer_t* timer = timerBegin(timer_num, 80, true);
timerAttachInterrupt(timer, onTimerFuncArr[buzzer_num], true);
timerAlarmWrite(timer, 1000000 / freq / 2, true); // set frequency (change per mu sec)
// play
timerAlarmEnable(timer);
delay(duration);
timerAlarmDisable(timer);
timerDetachInterrupt(timer);
}
void playChord(float* freq_arr, int freq_num, float duration, int* buzzer_num_arr = nullptr){
hw_timer_t* timer_arr[freq_num];
for (int i = 0; i < freq_num; i++){
int buzzer_num;
if (buzzer_num_arr == nullptr){
buzzer_num = i;
}else{
buzzer_num = buzzer_num_arr[i];
}
// set
int timer_num = buzzer_num;
timer_arr[i] = timerBegin(timer_num, 80, true);
// play
timerAttachInterrupt(timer_arr[i], onTimerFuncArr[buzzer_num], true);
timerAlarmWrite(timer_arr[i], 1000000 / freq_arr[i] / 2, true);
timerAlarmEnable(timer_arr[i]);
}
delay(duration);
for (hw_timer_t* timer: timer_arr){
timerAlarmDisable(timer);
timerDetachInterrupt(timer);
}
}
/*
【音符設定】 頻,長,連與否(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){
File audioFile = *((File *)audioFilePointer); // 取得傳入的參數
for (int i = 0; i < SHEET_LENGTH; i++){
string line = getLine(audioFile);
if (line[0] == '/' && line[1] == '*'){
string key_temp;
key_temp += line[4]; key_temp += line[5];
set_key_dict(key_temp);
line = getLine(audioFile);
}
vector<vector<string>> chord;
for (string s:split(line, " ")){
chord.push_back(split(s, ","));
}
int chord_size = chord.size();
if (chord_size == 1){
vector<string> v = chord[0];
float freq = stof(find_freq(v[0] + v[3]));
cout << line_num << "; " << v[0] << v[3] << ": " << freq << "; " << v[1] << endl;
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);
}
} else {
cout << line_num << "; " << chord[0][0] << ": " << "null" << "; " << chord[0][1] << " ";
cout << line_num << "; " << chord[1][0] << ": " << "null" << "; " << chord[1][1] << endl;
if (stoi(chord[0][2])){
float duration = stof(chord[0][1]) * QUARTER_NOTE_DURATION;
float freq_arr[chord_size];
for (int j = 0; j < chord_size; j++){
vector<string> v = chord[j];
float freq = stof(find_freq(v[0] + v[3]));
freq_arr[j] = freq;
}
playChord(freq_arr, chord_size, duration);
} else {
float duration = stof(chord[0][1]) * (QUARTER_NOTE_DURATION - SHUT_DURATION);
float shut_duration = stof(chord[0][1]) * (SHUT_DURATION);
float freq_arr[chord_size];
for (int j = 0; j < chord_size; j++){
vector<string> v = chord[j];
float freq = stof(find_freq(v[0] + v[3]));
freq_arr[j] = freq;
}
playChord(freq_arr, chord_size, duration);
delay(shut_duration);
}
}
}
// vTaskDelete(NULL); // 任務執行完畢後刪除自身
}
void setup() {
pinMode(BUZZER_PIN_1, OUTPUT);
pinMode(BUZZER_PIN_2, OUTPUT);
pinMode(BUZZER_PIN_3, OUTPUT);
pinMode(BUZZER_PIN_4, OUTPUT);
load_SD_card();
// reading file from the card:
File audioFile = SD.open("/sheet-01.txt", FILE_READ);
if (!audioFile) {
Serial.println("file was not opened");
return ;
}
// play music
// play_music(&audioFile);
// cout << millis() << endl;
// playTone(300, 500, 1);
// cout << millis() << endl;
// delay(100);
// playChord(new float[1] {300}, 1, 1000);
// float freq_arr[] = {300,800};
// cout << millis() << endl;
// playChord(freq_arr, 2, 212);
// cout << millis() << endl;
// playChord(new float[3] {300,500,800}, 3, 1000);
// playChord(new float[4] {300,500,800,1000}, 4, 1000);
// playTone(300, 1000);
// delay(1000);
// playTone(800, 1000);
}
void loop() {
playTone(493.883, 2000);
delay(500);
tone(BUZZER_PIN_1, 493.883, 2000);
delay(500);
}