/*
* FILE URL: song11_final.pde
* Nathan Hall-Snyder
* Code for use with the Strobe mp3 Player, built for the first offering of EE 47 - Press Play: Interactive Device Design.
* This code does the following:
* Controls a Parallel Display
* Plays Mp3s
* Lists songs on the above screen
* Accepts input from two rotary encoders, one pot, and three buttons
* Controls the led strobing action
/* import the avr's power library for the clock_prescale_set macro. */
#include <avr/power.h>
/* import the full fatfs wrapper & mp3 library functions & defines. */
#include <diskio.h>
#include <fatfs.h>
#include <ff.h>
#include <ffconf.h>
#include <integer.h>
#include <LiquidCrystal.h>
#include <string.h>
//Definitions for use with the file listing program
#define maxLength 30
#define maxFiles 32
#define maxFileLength 13
//Includes for mp3 playing
#include <mp3.h>
#include <mp3conf.h>
//Chip select Pin Definitions for the Mp3 player and SD Card
#define mp3_cs 0 // 'command chip select' to cs pin
#define mmc_cs 14 // 'chip select' to microsd cs pin
//State machine things
#define PLAY 0song11_final.pde song11_final.pde song11_final.pde song11_final.pde song11_final.pde
#define PAUSE 1
#define SONGLIST 2
//MP3 Player Pin Definitions
#define dcs 13 // 'data chip select' to bsync pin
#define dreq 12 // 'data request line' to dreq pin
#define rst 11 // 'reset' to decoder's reset pin
//SD Card
#define read_buffer 512 // size of the microsd read buffer
//Initialize the LCD
LiquidCrystal lcd(15, 17, 18, 19, 20, 21);
//Define the Encoder Pins
#define encoder0PinA 9
#define encoder0PinB 7
#define encoder1PinA 10
#define encoder1PinB 8
// Define Potentiometer Pins
#define potPin 5
// Define Encoder Buttons
#define button0pin 6
#define button1pin 5
#define button2pin 22
//Globals for the encoder positions
volatile signed int encoder0Pos = 200;
volatile signed int encoder0Last = 200;
volatile unsigned int encoder1Pos = 30;
volatile unsigned int encoder1Last = 0;
//Global for checking volume
volatile unsigned int checkVolume = 0;
//Global for volume control
volatile unsigned int curPot = 0;
//Another Global for volume control
volatile unsigned int mp3_vol = 200;
//The state machine state variable
volatile unsigned int state = 2;
//Defines the LED pin
int ledPin = 4;
//Currently playing song
char *nowPlaying[maxFileLength];
//Global for the current song control
volatile unsigned int currentSong = 0;
//Global for the amount of files on the SD card
unsigned int files_found = 0;
//Counter for the strobe light
volatile int ticker = 0;
//Declare the char array
char songs[maxFiles][maxFileLength];
/*
* initialize the processor speed, the encoders, setup the fatfs sd card (or, mms)
* filesystem, setup mp3 playback and register the pins used (device
* specific configuration)
*/
void setup() {
//Setup delay to keep the arduino from crashing
delay(100);
//Serial.begin(115200);
//Set the clock speed
// the avr atmega32u4 processor is only rated to 8mhz (rather than
// 16mhz) when powered at 3.3v (rather than 5v).
clock_prescale_set(clock_div_2); // fosc 8mhz as teensy is at 3.3V
delay(200);
//Start up the LCD
lcd.begin(16, 2);
//Nifty Startup Screen
startupScreen();
//Set up the LED
pinMode(ledPin,OUTPUT);
//Set up Encoder 0
pinMode(encoder0PinA, INPUT);
digitalWrite(encoder0PinA, HIGH); // turn on pullup resistor
pinMode(encoder0PinB, INPUT);
digitalWrite(encoder0PinB, HIGH); // turn on pullup resistor
//Set up Encoder 1
pinMode(encoder1PinA, INPUT);
digitalWrite(encoder1PinA, HIGH); // turn on pullup resistor
pinMode(encoder1PinB, INPUT);
digitalWrite(encoder1PinB, HIGH); // turn on pullup resistor
//Set up Buttons
digitalWrite(button0pin, HIGH);
digitalWrite(button1pin, HIGH);
digitalWrite(button2pin, HIGH);
//Attach interrupt pin
attachInterrupt(0, largebuttonPress, CHANGE); // encoder pin on interrupt 0 - pin 5
attachInterrupt(1, buttonPress, FALLING); // encoder pin on interrupt 1 - pin 6
attachInterrupt(2, doEncoder0, CHANGE); // encoder pin on interrupt 2 - pin 7
attachInterrupt(3, doEncoder1, CHANGE); // encoder pin on interrupt 3 - pin 8
//Starts up the MP3 Player
Mp3.begin(mp3_cs,dcs,rst,dreq); // decoder cs, dcs, rst, dreq pin
Mp3.volume(mp3_vol); // default volume level is silent
delay(200);
//Starts up the SD Card
FatFs.begin(mmc_cs); // register microsd card's cs pin
delay(100);
// while only rated to 8mhz, the processor seems to work at 16MHz,
// which is required to playback full-resolution 44.1kHz wav files
clock_prescale_set(clock_div_1); // fosc 16mhz only for full waves
//Initializes the state machine
state = SONGLIST;
}
// This method reads the contents of the SD card, printing what it finds into an array
void dir_list (void){
FILINFO file_info; // information on file size and type
files_found = 0;
FatFs.opendir();
do {
FatFs.readdir(&file_info);
if ((file_info.fattrib & AM_DIR) != AM_DIR) {
//Adds the song name top the thing
strcpy( songs[files_found] , file_info.fname);
files_found++;
delay(20);
}
}
while (file_info.fname[0] != 0);
}
//The interrupt function for the left encoder button
void buttonPress(){
if(state == PLAY){
state = SONGLIST;
delay(100);
} else {
//Serial.println("changed state to play");
state = PLAY;
delay(100);
}
}
//The interrupt function called for the large button
void largebuttonPress(){
//Serial.println("large button pressed");
delay(100);
if(digitalRead(button1pin) == HIGH && state == PLAY){
//Serial.println("pausing from the interrupt");
state = PAUSE;
}
if(state == PAUSE && digitalRead(button1pin) == LOW) state = PLAY;
if(state == SONGLIST && digitalRead(button1pin) == LOW){
state = PLAY;
}
}
//Plays a specific song in a directory
void dir_play (char song[]) {
//////Serial.println(song);
char * fn = song; // pointer to the filename
char s = strlen(song); // length of the filename
if ((fn[s-3] == 'M' && fn[s-2] == 'P' && fn[s-1] == '3') ||
(fn[s-3] == 'W' && fn[s-2] == 'A' && fn[s-1] == 'V')) {
if (FatFs.open(song, FA_READ) == FR_OK) {
lcd.clear();
lcd.setCursor(0,0);
lcd.print(">>");
lcd.println(song);
mp3_play();
}
}
//}
}
FILINFO fnfo; // information on file size & type, updated by readdir
//The interrupt call called by the encoder interrupt.
//Reading the encoders is really finicky. It takes a lot of debouncing and output filtering to get them to work properly.
void doEncoder0() {
if(state == PLAY || state == PAUSE){
state = SONGLIST;
}
if (digitalRead(encoder0PinA) == HIGH) { // found a low-to-high on channel A
if (digitalRead(encoder0PinB) == LOW) { // check channel B to see which way
// encoder is turning
encoder0Pos = encoder0Pos - 1; // CCW
}
else {
encoder0Pos = encoder0Pos + 1; // CW
}
}
else // found a high-to-low on channel A
{
if (digitalRead(encoder0PinB) == LOW) { // check channel B to see which way
// encoder is turning
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
}
//The interrupt call called by the encoder interrupt.
//Reading the encoders is really finicky. It takes a lot of debouncing and output filtering to get them to work properly.
//The second encoder either wasn't working right or was non-functional, but it would only increase, so
//this small bit of code lets you change the frequency of the flashing or turn off the flashing altogether
void doEncoder1() {
delay(50);
encoder1Pos += 2;
if(encoder1Pos > 100) encoder1Pos = 0;
}
//The main loop, proceses the state machine
void loop() {
switch( state )
{
case PLAY: playmp3();
case PAUSE: pause();
case SONGLIST: listsongs();
}
}
//The menu-type loop, which allows the user to select what song they would like.
//I tried hard to implement a reasonable-seeming scrolling system on a two line
//display, with a moving arrow and associated scrolling
void listsongs(){
dir_list();
int cursorPos = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(">");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(1, 0);
lcd.print(songs[currentSong]);
lcd.setCursor(1, 1);
lcd.print(songs[ currentSong + 1 ]);
encoder0Last = encoder0Pos;
while(state == SONGLIST){
if(currentSong < 10){
lcd.setCursor(15,1);
lcd.print(currentSong);
lcd.setCursor(14,1);
lcd.print(" ");
} else {
lcd.setCursor(14,1);
lcd.print(currentSong);
}
if(encoder0Pos != encoder0Last) delay(100);
//Encoder A turned right
if(encoder0Pos > encoder0Last){
encoder0Last = encoder0Pos;
if(currentSong < (files_found-1)){
currentSong ++;
if(cursorPos == 1){
lcd.setCursor(1, 1);
lcd.print(songs[currentSong]);
lcd.setCursor(1, 0);
lcd.print(songs[ currentSong - 1 ]);
} else {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(">");
cursorPos = 1;
}
}
}
//Encoder A turned left
if(encoder0Pos < encoder0Last){
encoder0Last = encoder0Pos;
if(currentSong > 0){
currentSong--;
if(cursorPos == 0){
lcd.setCursor(1, 0);
lcd.print(songs[currentSong]);
lcd.setCursor(1, 1);
//TODO
if(currentSong == (files_found-2)) lcd.print(" ");
else lcd.print(songs[ currentSong + 1 ]);
} else {
lcd.setCursor(0, 0);
lcd.print(">");
lcd.setCursor(0, 1);
lcd.print(" ");
cursorPos = 0;
}
}
}
}
lcd.clear();
lcd.setCursor(0,0);
}
//The pause function for the state machine
void pause(){
while(state == PAUSE){
delay(20);
}
}
//State machine function, plays an Mp3
void playmp3(void){
dir_play(songs[currentSong]);
}
void startupScreen(void){
lcd.clear();
lcd.print("MP3");
delay(100);
lcd.clear();
lcd.print("MP3.");
delay(100);
lcd.clear();
lcd.print("MP3..");
delay(100);
lcd.clear();
lcd.print("MP3...");
delay(100);
lcd.clear();
lcd.print("MP3...ENGAGE!");
delay(300);
lcd.clear();
}
//Actually does the byte-by-byte copying of the data from the SD card, sending it to the mp3 player.
//Also, this loop is used by a variety of functions of the strobe mp3. In addition to playing mp3s, it:
// - Checks to see if the music has been paused
// - takes care of the strobing action
// - Controls the volume based on the pot input
void mp3_play (void) {
unsigned char bytes[read_buffer]; // buffer to send to the decoder
unsigned int bytes_read; // number of bytes read from fat
curPot = analogRead(potPin);
do {
FatFs.read(bytes, read_buffer, &bytes_read);
Mp3.play(bytes, read_buffer);
if(state == PAUSE) {
while (1){
lcd.setCursor(4,1);
lcd.print("Paused");
delay(20);
if(state != PAUSE){
lcd.setCursor(4,1);
lcd.print(" ");
break;
}
}
}
if(state == SONGLIST) break;
ticker++;
if(encoder1Pos < 75 && encoder1Pos > 2) {
if(ticker >= encoder1Pos/4) digitalWrite(ledPin,HIGH);
if(ticker >= (encoder1Pos/4)+2){
digitalWrite(ledPin,LOW);
lcd.setCursor(10,1);
lcd.println(encoder1Pos);
ticker = 0;
}
} else {
lcd.setCursor(10,1);
lcd.println(encoder1Pos);
digitalWrite(ledPin,HIGH);
}
if(analogRead(potPin) != curPot){
mp3_vol = (analogRead(potPin))/4;
lcd.setCursor(0,1);
lcd.print(mp3_vol);
Mp3.volume(mp3_vol);
curPot = analogRead(potPin);
}
}
while (bytes_read == read_buffer);
currentSong++;
}
/*
* Nathan Hall-Snyder
* Code for use with the Strobe mp3 Player, built for the first offering of EE 47 - Press Play: Interactive Device Design.
* This code does the following:
* Controls a Parallel Display
* Plays Mp3s
* Lists songs on the above screen
* Accepts input from two rotary encoders, one pot, and three buttons
* Controls the led strobing action
/* import the avr's power library for the clock_prescale_set macro. */
#include <avr/power.h>
/* import the full fatfs wrapper & mp3 library functions & defines. */
#include <diskio.h>
#include <fatfs.h>
#include <ff.h>
#include <ffconf.h>
#include <integer.h>
#include <LiquidCrystal.h>
#include <string.h>
//Definitions for use with the file listing program
#define maxLength 30
#define maxFiles 32
#define maxFileLength 13
//Includes for mp3 playing
#include <mp3.h>
#include <mp3conf.h>
//Chip select Pin Definitions for the Mp3 player and SD Card
#define mp3_cs 0 // 'command chip select' to cs pin
#define mmc_cs 14 // 'chip select' to microsd cs pin
//State machine things
#define PLAY 0
#define PAUSE 1
#define SONGLIST 2
//MP3 Player Pin Definitions
#define dcs 13 // 'data chip select' to bsync pin
#define dreq 12 // 'data request line' to dreq pin
#define rst 11 // 'reset' to decoder's reset pin
//SD Card
#define read_buffer 512 // size of the microsd read buffer
//Initialize the LCD
LiquidCrystal lcd(15, 17, 18, 19, 20, 21);
//Define the Encoder Pins
#define encoder0PinA 9
#define encoder0PinB 7
#define encoder1PinA 10
#define encoder1PinB 8
// Define Potentiometer Pins
#define potPin 5
// Define Encoder Buttons
#define button0pin 6
#define button1pin 5
#define button2pin 22
//Globals for the encoder positions
volatile signed int encoder0Pos = 200;
volatile signed int encoder0Last = 200;
volatile unsigned int encoder1Pos = 30;
volatile unsigned int encoder1Last = 0;
//Global for checking volume
volatile unsigned int checkVolume = 0;
//Global for volume control
volatile unsigned int curPot = 0;
//Another Global for volume control
volatile unsigned int mp3_vol = 200;
//The state machine state variable
volatile unsigned int state = 2;
//Defines the LED pin
int ledPin = 4;
//Currently playing song
char *nowPlaying[maxFileLength];
//Global for the current song control
volatile unsigned int currentSong = 0;
//Global for the amount of files on the SD card
unsigned int files_found = 0;
//Counter for the strobe light
volatile int ticker = 0;
//Declare the char array
char songs[maxFiles][maxFileLength];
/*
* initialize the processor speed, the encoders, setup the fatfs sd card (or, mms)
* filesystem, setup mp3 playback and register the pins used (device
* specific configuration)
*/
void setup() {
//Setup delay to keep the arduino from crashing
delay(100);
//Serial.begin(115200);
//Set the clock speed
// the avr atmega32u4 processor is only rated to 8mhz (rather than
// 16mhz) when powered at 3.3v (rather than 5v).
clock_prescale_set(clock_div_2); // fosc 8mhz as teensy is at 3.3V
delay(200);
//Start up the LCD
lcd.begin(16, 2);
//Nifty Startup Screen
startupScreen();
//Set up the LED
pinMode(ledPin,OUTPUT);
//Set up Encoder 0
pinMode(encoder0PinA, INPUT);
digitalWrite(encoder0PinA, HIGH); // turn on pullup resistor
pinMode(encoder0PinB, INPUT);
digitalWrite(encoder0PinB, HIGH); // turn on pullup resistor
//Set up Encoder 1
pinMode(encoder1PinA, INPUT);
digitalWrite(encoder1PinA, HIGH); // turn on pullup resistor
pinMode(encoder1PinB, INPUT);
digitalWrite(encoder1PinB, HIGH); // turn on pullup resistor
//Set up Buttons
digitalWrite(button0pin, HIGH);
digitalWrite(button1pin, HIGH);
digitalWrite(button2pin, HIGH);
//Attach interrupt pin
attachInterrupt(0, largebuttonPress, CHANGE); // encoder pin on interrupt 0 - pin 5
attachInterrupt(1, buttonPress, FALLING); // encoder pin on interrupt 1 - pin 6
attachInterrupt(2, doEncoder0, CHANGE); // encoder pin on interrupt 2 - pin 7
attachInterrupt(3, doEncoder1, CHANGE); // encoder pin on interrupt 3 - pin 8
//Starts up the MP3 Player
Mp3.begin(mp3_cs,dcs,rst,dreq); // decoder cs, dcs, rst, dreq pin
Mp3.volume(mp3_vol); // default volume level is silent
delay(200);
//Starts up the SD Card
FatFs.begin(mmc_cs); // register microsd card's cs pin
delay(100);
// while only rated to 8mhz, the processor seems to work at 16MHz,
// which is required to playback full-resolution 44.1kHz wav files
clock_prescale_set(clock_div_1); // fosc 16mhz only for full waves
//Initializes the state machine
state = SONGLIST;
}
// This method reads the contents of the SD card, printing what it finds into an array
void dir_list (void){
FILINFO file_info; // information on file size and type
files_found = 0;
FatFs.opendir();
do {
FatFs.readdir(&file_info);
if ((file_info.fattrib & AM_DIR) != AM_DIR) {
//Adds the song name top the thing
strcpy( songs[files_found] , file_info.fname);
files_found++;
delay(20);
}
}
while (file_info.fname[0] != 0);
}
//The interrupt function for the left encoder button
void buttonPress(){
if(state == PLAY){
state = SONGLIST;
delay(100);
} else {
//Serial.println("changed state to play");
state = PLAY;
delay(100);
}
}
//The interrupt function called for the large button
void largebuttonPress(){
//Serial.println("large button pressed");
delay(100);
if(digitalRead(button1pin) == HIGH && state == PLAY){
//Serial.println("pausing from the interrupt");
state = PAUSE;
}
if(state == PAUSE && digitalRead(button1pin) == LOW) state = PLAY;
if(state == SONGLIST && digitalRead(button1pin) == LOW){
state = PLAY;
}
}
//Plays a specific song in a directory
void dir_play (char song[]) {
//////Serial.println(song);
char * fn = song; // pointer to the filename
char s = strlen(song); // length of the filename
if ((fn[s-3] == 'M' && fn[s-2] == 'P' && fn[s-1] == '3') ||
(fn[s-3] == 'W' && fn[s-2] == 'A' && fn[s-1] == 'V')) {
if (FatFs.open(song, FA_READ) == FR_OK) {
lcd.clear();
lcd.setCursor(0,0);
lcd.print(">>");
lcd.println(song);
mp3_play();
}
}
//}
}
FILINFO fnfo; // information on file size & type, updated by readdir
//The interrupt call called by the encoder interrupt.
//Reading the encoders is really finicky. It takes a lot of debouncing and output filtering to get them to work properly.
void doEncoder0() {
if(state == PLAY || state == PAUSE){
state = SONGLIST;
}
if (digitalRead(encoder0PinA) == HIGH) { // found a low-to-high on channel A
if (digitalRead(encoder0PinB) == LOW) { // check channel B to see which way
// encoder is turning
encoder0Pos = encoder0Pos - 1; // CCW
}
else {
encoder0Pos = encoder0Pos + 1; // CW
}
}
else // found a high-to-low on channel A
{
if (digitalRead(encoder0PinB) == LOW) { // check channel B to see which way
// encoder is turning
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
}
//The interrupt call called by the encoder interrupt.
//Reading the encoders is really finicky. It takes a lot of debouncing and output filtering to get them to work properly.
//The second encoder either wasn't working right or was non-functional, but it would only increase, so
//this small bit of code lets you change the frequency of the flashing or turn off the flashing altogether
void doEncoder1() {
delay(50);
encoder1Pos += 2;
if(encoder1Pos > 100) encoder1Pos = 0;
}
//The main loop, proceses the state machine
void loop() {
switch( state )
{
case PLAY: playmp3();
case PAUSE: pause();
case SONGLIST: listsongs();
}
}
//The menu-type loop, which allows the user to select what song they would like.
//I tried hard to implement a reasonable-seeming scrolling system on a two line
//display, with a moving arrow and associated scrolling
void listsongs(){
dir_list();
int cursorPos = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(">");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(1, 0);
lcd.print(songs[currentSong]);
lcd.setCursor(1, 1);
lcd.print(songs[ currentSong + 1 ]);
encoder0Last = encoder0Pos;
while(state == SONGLIST){
if(currentSong < 10){
lcd.setCursor(15,1);
lcd.print(currentSong);
lcd.setCursor(14,1);
lcd.print(" ");
} else {
lcd.setCursor(14,1);
lcd.print(currentSong);
}
if(encoder0Pos != encoder0Last) delay(100);
//Encoder A turned right
if(encoder0Pos > encoder0Last){
encoder0Last = encoder0Pos;
if(currentSong < (files_found-1)){
currentSong ++;
if(cursorPos == 1){
lcd.setCursor(1, 1);
lcd.print(songs[currentSong]);
lcd.setCursor(1, 0);
lcd.print(songs[ currentSong - 1 ]);
} else {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(">");
cursorPos = 1;
}
}
}
//Encoder A turned left
if(encoder0Pos < encoder0Last){
encoder0Last = encoder0Pos;
if(currentSong > 0){
currentSong--;
if(cursorPos == 0){
lcd.setCursor(1, 0);
lcd.print(songs[currentSong]);
lcd.setCursor(1, 1);
//TODO
if(currentSong == (files_found-2)) lcd.print(" ");
else lcd.print(songs[ currentSong + 1 ]);
} else {
lcd.setCursor(0, 0);
lcd.print(">");
lcd.setCursor(0, 1);
lcd.print(" ");
cursorPos = 0;
}
}
}
}
lcd.clear();
lcd.setCursor(0,0);
}
//The pause function for the state machine
void pause(){
while(state == PAUSE){
delay(20);
}
}
//State machine function, plays an Mp3
void playmp3(void){
dir_play(songs[currentSong]);
}
void startupScreen(void){
lcd.clear();
lcd.print("MP3");
delay(100);
lcd.clear();
lcd.print("MP3.");
delay(100);
lcd.clear();
lcd.print("MP3..");
delay(100);
lcd.clear();
lcd.print("MP3...");
delay(100);
lcd.clear();
lcd.print("MP3...ENGAGE!");
delay(300);
lcd.clear();
}
//Actually does the byte-by-byte copying of the data from the SD card, sending it to the mp3 player.
//Also, this loop is used by a variety of functions of the strobe mp3. In addition to playing mp3s, it:
// - Checks to see if the music has been paused
// - takes care of the strobing action
// - Controls the volume based on the pot input
void mp3_play (void) {
unsigned char bytes[read_buffer]; // buffer to send to the decoder
unsigned int bytes_read; // number of bytes read from fat
curPot = analogRead(potPin);
do {
FatFs.read(bytes, read_buffer, &bytes_read);
Mp3.play(bytes, read_buffer);
if(state == PAUSE) {
while (1){
lcd.setCursor(4,1);
lcd.print("Paused");
delay(20);
if(state != PAUSE){
lcd.setCursor(4,1);
lcd.print(" ");
break;
}
}
}
if(state == SONGLIST) break;
ticker++;
if(encoder1Pos < 75 && encoder1Pos > 2) {
if(ticker >= encoder1Pos/4) digitalWrite(ledPin,HIGH);
if(ticker >= (encoder1Pos/4)+2){
digitalWrite(ledPin,LOW);
lcd.setCursor(10,1);
lcd.println(encoder1Pos);
ticker = 0;
}
} else {
lcd.setCursor(10,1);
lcd.println(encoder1Pos);
digitalWrite(ledPin,HIGH);
}
if(analogRead(potPin) != curPot){
mp3_vol = (analogRead(potPin))/4;
lcd.setCursor(0,1);
lcd.print(mp3_vol);
Mp3.volume(mp3_vol);
curPot = analogRead(potPin);
}
}
while (bytes_read == read_buffer);
currentSong++;
}
Comments (0)
You don't have permission to comment on this page.