/*
Connect a piezo buzzer or speaker to pin 11 or select a new pin.
More songs available at https://github.com/robsoncouto/arduino-songs
Original code by: Robson Couto, 2019
Modified by: tree, 2023
*/
#include "notes.h"
#include "songs.h"
#include <Bounce2.h>
#include <LiquidCrystal.h>
#define seprint(a) /*Serial.print(a)*/;
#define seprintln(a) /*Serial.println(a)*/;
int buzzer = 11;
Bounce2::Button shuffleButton = Bounce2::Button();
Bounce2::Button previousButton = Bounce2::Button();
Bounce2::Button playPauseButton = Bounce2::Button();
Bounce2::Button nextButton = Bounce2::Button();
Bounce2::Button repeatButton = Bounce2::Button();
const int rs = 8;
const int en = 7;
const int d4 = 13;
const int d5 = 12;
const int d7 = 9;
const int d6 = 10;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// const int shuffleButton = 2;
// const int previousButton = 3;
// const int playPauseButton = 4;
// const int nextButton = 5;
//const int repeatButton = 6;
int thisNote = 0;
bool repeat = false;
bool shuffled = false;
int queue[amountOfSongs];
int currentSong = 0;
int songStatus; // 0: stopped, 1: playing, 2: paused
int randomUnique(int arr[], int len/*, int num, int repetitions*/) {
// if (repetitions > 50) return num;
// repetitions++;
// seprintln("generating random int");
int n;
// for (int i = 0; i < len; i++) {
// if (arr[i] == n) {
// seprintln(n);
// seprintln(arr[i]);
// n = random(0, max);
// seprintln(n);
// randomUnique(arr, len, n, repetitions);
// }
// }
// return n;
bool unique;
do {
unique = true;
n = random(0, len);
// seprintln(n);
for(int j = 0; j < len; j++) {
if(arr[j] == n) {
unique = false;
break;
}
// seprint("unique = ");
// seprintln(unique);
}
} while(!unique);
return n;
// for(int i = 0; i < len; i++)
// {
// bool unique;
// do {
// unique = true;
// n = random(0, max);
// for(int j = 0; j < i; j++) {
// if(arr[j] == n) {
// unique = false;
// break
// }
// }
// } while(!unique);
// arr[i]=n;
// }
}
int transpose(int note, int transposeAmount) {
return note * pow(2, transposeAmount/12.0);
}
void playNote(int note, int noteDuration, int transposeAmount) {
if (transposeAmount != 0) note = transpose(note, transposeAmount);
// we only play the note for 90% of the duration, leaving 10% as a pause
tone(buzzer, note, noteDuration * 0.9);
seprint(note);
seprint(" ");
seprintln(noteDuration);
// Wait for the specief duration before playing the next note.
delay(noteDuration);
// stop the waveform generation before the next note.
noTone(buzzer);
}
void playSong(Song &song, int transposeAmount) {
int initialTranspose = transposeAmount;
int tempo = song.tempo, len = song.len;
transposeAmount += song.defaultTranspose;
int ae = song.melody[0];
//int len = sizeof(melody);
/*int melody[len];
for(int i = 0; i < len; i++) {
melody[i] = pgm_read_word(&song.melody[i]);
}*/
bool alreadyRepeated = false;
// sizeof gives the number of bytes, each int value is composed of two bytes (16 bits)
// there are two values per note (pitch and duration), so for each note there are four bytes
int notes = len / sizeof(pgm_read_word(&song.melody[0])) / 2;
// this calculates the duration of a whole note in ms
int wholenote = (60000 * 4) / tempo;
int divider = 0, noteDuration = 0;
// iterate over the notes of the melody.
// Remember, the array is twice the number of notes (notes + durations)
for (thisNote = (songStatus == 2) ? thisNote : 0; thisNote < notes * 2; thisNote = thisNote + 2) {
int currentNote = pgm_read_word(&song.melody[thisNote]);
int currentNoteDuration = pgm_read_word(&song.melody[thisNote+1]);
updateButtons();
if (playPauseButton.pressed()) {
seprintln("pausing song");
songStatus = 2;
return;
}
listenForButtons();
if (currentNote == -10 && !alreadyRepeated) {
thisNote = 0;
alreadyRepeated = true;
currentNote = pgm_read_word(&song.melody[thisNote]);
currentNoteDuration = pgm_read_word(&song.melody[thisNote+1]);
}
// calculates the duration of each note
divider = currentNoteDuration;
if (divider > 0) {
// regular note, just proceed
noteDuration = (wholenote) / divider;
} else if (divider < 0) {
// dotted notes are represented with negative durations!!
noteDuration = (wholenote) / abs(divider);
noteDuration *= 1.5; // increases the duration in half for dotted notes
}
playNote(currentNote, noteDuration, transposeAmount);
}
songStatus = 0;
if (repeat) {
delay(1000);
playSong(song, initialTranspose);
}
}
void pauseSong() {
}
void repeatSong() {
}
int shiftQueue(int amount) {
if (amount > 0) {
currentSong = (currentSong == amountOfSongs-1) ?
0 : currentSong + amount;
}
else if (amount < 0) {
currentSong = (currentSong == 0) ?
amountOfSongs-1 : currentSong + amount;
}
songStatus = 0;
thisNote = 0;
return currentSong;
}
void shuffleSongs() {
seprintln("shuffling");
for (int i = 0; i < amountOfSongs; i++) {
queue[i] = -1;
}
for (int i = 0; i < amountOfSongs; i++) {
seprint(queue[i]);
seprint(", ");
}
seprintln("");
for (int i = 0; i < amountOfSongs; i++) {
// seprint("i = ");
// seprintln(i);
queue[i] = randomUnique(queue, amountOfSongs/*, random(0, amountOfSongs), 0*/);
}
for (int i = 0; i < amountOfSongs; i++) {
seprint(queue[i]);
seprint(", ");
}
seprintln("");
lcd.clear();
lcd.print(songList[queue[currentSong]].name);
}
void resetQueue() {
for (int i = 0; i < amountOfSongs; i++) {
queue[i] = i;
}
for (int i = 0; i < amountOfSongs; i++) {
seprint(queue[i]);
seprint(", ");
}
seprintln("");
lcd.clear();
lcd.print(songList[queue[currentSong]].name);
}
void listenForButtons() {
if (playPauseButton.pressed()) {
seprintln("playing song");
songStatus = (songStatus == 0) ? 1 : 2;
delay(300);
seprint("playing song ");
seprint(queue[currentSong]);
seprint(": ");
lcd.clear();
lcd.print(songList[queue[currentSong]].name);
playSong(songList[queue[currentSong]], 0);
} else if (nextButton.pressed()) {
shiftQueue(1);
seprintln("skipping song");
seprint("current song: ");
seprint(currentSong);
seprint(": ");
lcd.clear();
lcd.print(songList[queue[currentSong]].name);
// seprintln("next button pressed");
} else if (previousButton.pressed()) {
shiftQueue(-1);
seprintln("skipping song");
seprint("current song: ");
seprintln(currentSong);
seprint(": ");
lcd.clear();
lcd.print(songList[queue[currentSong]].name);
// seprintln("next button pressed");
} else if (repeatButton.pressed()) {
repeat = !repeat;
seprintln(repeat);
} else if (shuffleButton.pressed()) {
if (shuffled) {
seprintln("resetting queue");
resetQueue();
} else {
seprintln("randomizing queue");
shuffleSongs();
}
shuffled = !shuffled;
songStatus = 0;
thisNote = 0;
currentSong = 0;
}
}
void updateButtons() {
shuffleButton.update();
previousButton.update();
nextButton.update();
playPauseButton.update();
repeatButton.update();
}
void setup() {
pinMode(buzzer, OUTPUT);
//playSong(santaClauseIsComingToTownMelody, santaClauseIsComingToTownTempo, santaClauseIsComingToTownLen);
// pinMode(shuffleButton, INPUT);
// pinMode(previousButton, INPUT);
// pinMode(playPauseButton, INPUT);
// pinMode(nextButton, INPUT);
//pinMode(repeatButton, INPUT);
shuffleButton.setPressedState(HIGH);
shuffleButton.attach(2);
shuffleButton.interval(50);
previousButton.setPressedState(HIGH);
previousButton.attach(3);
previousButton.interval(50);
playPauseButton.setPressedState(HIGH);
playPauseButton.attach(4);
playPauseButton.interval(50);
nextButton.setPressedState(HIGH);
nextButton.attach(5);
nextButton.interval(50);
repeatButton.setPressedState(HIGH);
repeatButton.attach(6);
repeatButton.interval(50);
resetQueue();
randomSeed(analogRead(0));
Serial.begin(9600);
while (!Serial) {
delay(10); // wait for serial port to connect. Needed for native USB
}
int aee[] = {3, 5, 3, 6};
seprintln(randomUnique(aee, 4/*, 3, 0*/));
for (int i = 0; i < 4; i++) {
seprint("aee [");
seprint(i);
seprint("] = ");
seprintln(aee[i]);
}
/*seprint("sizeof(MiiChannelThemeMelody) = ");
seprintln(sizeof(MiiChannelThemeMelody));
seprintln(MiiChannelThemeMelody[6]);
//playSong(santaClausIsComingToTown, 12);
delay(1000);
playSong(MiiChannelTheme, 0);*/
// Use the object as needed
}
void loop() {
updateButtons();
listenForButtons();
}