make it not sub
This commit is contained in:
parent
9c3c4db525
commit
43638abc8b
3 changed files with 265 additions and 0 deletions
242
Gordon_Cole_MP3_Player/Gordon_Cole_MP3_Player.ino
Normal file
242
Gordon_Cole_MP3_Player/Gordon_Cole_MP3_Player.ino
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
//
|
||||||
|
// Gordon Cole MP3 Player
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <SD.h>
|
||||||
|
#include <Adafruit_VS1053.h>
|
||||||
|
|
||||||
|
// Guide is based on Feather M0 Express
|
||||||
|
// ARDUINO_SAMD_FEATHER_M0 defines only
|
||||||
|
// VS1053 Pins
|
||||||
|
#define VS1053_RESET -1 // VS1053 reset pin (not used!)
|
||||||
|
#define VS1053_CS 6 // VS1053 chip select pin (output)
|
||||||
|
#define VS1053_DCS 10 // VS1053 Data/command select pin (output)
|
||||||
|
#define CARDCS 5 // Card chip select pin
|
||||||
|
#define VS1053_DREQ 9 // VS1053 Data request, ideally an Interrupt pin
|
||||||
|
// Button Pins
|
||||||
|
#define BUTTON_PLAY 13 // PLAY / STOP button
|
||||||
|
#define BUTTON_PAUSE 12 // PAUSE / RESUME button
|
||||||
|
#define BUTTON_NEXT 11 // NEXT button
|
||||||
|
// Status LED
|
||||||
|
#define LED_STATUS 19 // status LED
|
||||||
|
#define BLINK_RATE 500 // blink rate in ms
|
||||||
|
// Volume Control
|
||||||
|
#define KNOB_VOLUME 0 // volume knob
|
||||||
|
#define KNOB_MIN 0 // min ADC value
|
||||||
|
#define KNOB_MAX 1023 // max ADC value
|
||||||
|
#define VOL_MIN 0 // min volume (most loud)
|
||||||
|
#define VOL_MAX 50 // max volume (most quiet)
|
||||||
|
// Maximum number of files (tracks) to load
|
||||||
|
#define TRACKS_MAX 100
|
||||||
|
// Player behavior
|
||||||
|
#define AUTO_PLAY_NEXT true // true to automatically go to next track
|
||||||
|
|
||||||
|
unsigned long currentMillis, previousMillis;
|
||||||
|
int currentKnob, previousKnob;
|
||||||
|
int volume;
|
||||||
|
int currentTrack, totalTracks;
|
||||||
|
char trackListing[TRACKS_MAX][13] = {' '};
|
||||||
|
enum mode {
|
||||||
|
PLAYING,
|
||||||
|
PAUSED,
|
||||||
|
STOPPED
|
||||||
|
} currentMode = STOPPED;
|
||||||
|
|
||||||
|
Adafruit_VS1053_FilePlayer musicPlayer =
|
||||||
|
Adafruit_VS1053_FilePlayer(VS1053_RESET, VS1053_CS, VS1053_DCS, VS1053_DREQ, CARDCS);
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
// Leave commented for standalone operation, uncomment for troubleshooting
|
||||||
|
//while (!Serial) ;
|
||||||
|
|
||||||
|
// Initialize pins
|
||||||
|
pinMode(BUTTON_PLAY, INPUT_PULLUP);
|
||||||
|
pinMode(BUTTON_PAUSE, INPUT_PULLUP);
|
||||||
|
pinMode(BUTTON_NEXT, INPUT_PULLUP);
|
||||||
|
pinMode(LED_STATUS, OUTPUT);
|
||||||
|
|
||||||
|
// Initialize status LED
|
||||||
|
previousMillis = millis();
|
||||||
|
digitalWrite(LED_STATUS, LOW);
|
||||||
|
|
||||||
|
Serial.println("\n\nGordon Cole MP3 Player");
|
||||||
|
|
||||||
|
// Initialize the music player
|
||||||
|
if (! musicPlayer.begin()) {
|
||||||
|
Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
Serial.println(F("VS1053 found"));
|
||||||
|
musicPlayer.softReset();
|
||||||
|
|
||||||
|
// Make a tone to indicate VS1053 is working
|
||||||
|
musicPlayer.sineTest(0x44, 500);
|
||||||
|
|
||||||
|
// Set volume for left, right channels. lower numbers == louder volume!
|
||||||
|
previousKnob = analogRead(KNOB_VOLUME);
|
||||||
|
volume = map(previousKnob, KNOB_MIN, KNOB_MAX, VOL_MIN, VOL_MAX);
|
||||||
|
Serial.print("Volume = "); Serial.println(volume);
|
||||||
|
musicPlayer.setVolume(volume, volume);
|
||||||
|
|
||||||
|
// Initialize the SD card
|
||||||
|
if (!SD.begin(CARDCS)) {
|
||||||
|
Serial.println(F("SD failed, or not present"));
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
Serial.println("SD OK!");
|
||||||
|
|
||||||
|
// Load list of tracks
|
||||||
|
Serial.println("Track Listing");
|
||||||
|
Serial.println("=============");
|
||||||
|
totalTracks = 0;
|
||||||
|
loadTracks(SD.open("/"), 0);
|
||||||
|
currentTrack = 0;
|
||||||
|
|
||||||
|
// Setup interrupts (DREQ) for playback
|
||||||
|
musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void loop() {
|
||||||
|
// Check and set volume
|
||||||
|
updateVolume();
|
||||||
|
|
||||||
|
// Update status LED
|
||||||
|
updateStatusLED();
|
||||||
|
|
||||||
|
// Auto play next track if feature enabled
|
||||||
|
if (AUTO_PLAY_NEXT) {
|
||||||
|
if (currentMode==PLAYING && musicPlayer.stopped()) {
|
||||||
|
currentTrack = ++currentTrack < totalTracks ? currentTrack : 0;
|
||||||
|
Serial.print("Next ");
|
||||||
|
Serial.print(currentTrack); Serial.print("=");
|
||||||
|
Serial.println(trackListing[currentTrack]);
|
||||||
|
musicPlayer.startPlayingFile(trackListing[currentTrack]);
|
||||||
|
currentMode = PLAYING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start / Stop
|
||||||
|
if (!digitalRead(BUTTON_PLAY)) {
|
||||||
|
if (musicPlayer.stopped()) {
|
||||||
|
Serial.print("Start ");
|
||||||
|
Serial.print(currentTrack); Serial.print("=");
|
||||||
|
Serial.println(trackListing[currentTrack]);
|
||||||
|
musicPlayer.startPlayingFile(trackListing[currentTrack]);
|
||||||
|
currentMode = PLAYING;
|
||||||
|
} else {
|
||||||
|
Serial.println("Stopped.");
|
||||||
|
musicPlayer.stopPlaying();
|
||||||
|
currentMode = STOPPED;
|
||||||
|
}
|
||||||
|
delay(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause / Resume
|
||||||
|
if (!digitalRead(BUTTON_PAUSE)) {
|
||||||
|
if (!musicPlayer.stopped()) {
|
||||||
|
if (musicPlayer.paused()) {
|
||||||
|
Serial.println("Resumed");
|
||||||
|
musicPlayer.pausePlaying(false);
|
||||||
|
currentMode = PLAYING;
|
||||||
|
} else {
|
||||||
|
Serial.println("Paused");
|
||||||
|
musicPlayer.pausePlaying(true);
|
||||||
|
currentMode = PAUSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next
|
||||||
|
if (!digitalRead(BUTTON_NEXT)) {
|
||||||
|
if (!musicPlayer.stopped()) {
|
||||||
|
Serial.println("Stopping current playback.");
|
||||||
|
musicPlayer.stopPlaying();
|
||||||
|
}
|
||||||
|
currentTrack = ++currentTrack < totalTracks ? currentTrack : 0;
|
||||||
|
Serial.print("Next ");
|
||||||
|
Serial.print(currentTrack); Serial.print("=");
|
||||||
|
Serial.println(trackListing[currentTrack]);
|
||||||
|
musicPlayer.startPlayingFile(trackListing[currentTrack]);
|
||||||
|
currentMode = PLAYING;
|
||||||
|
delay(250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void updateVolume() {
|
||||||
|
// Read current value
|
||||||
|
currentKnob = analogRead(KNOB_VOLUME);
|
||||||
|
// Only update if it's changed
|
||||||
|
if (currentKnob != previousKnob) {
|
||||||
|
previousKnob = currentKnob;
|
||||||
|
volume = map(currentKnob, KNOB_MIN, KNOB_MAX, VOL_MIN, VOL_MAX);
|
||||||
|
musicPlayer.setVolume(volume, volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void updateStatusLED() {
|
||||||
|
if (musicPlayer.paused()) {
|
||||||
|
// Blink it like a polaroid
|
||||||
|
currentMillis = millis();
|
||||||
|
if (currentMillis - previousMillis > BLINK_RATE) {
|
||||||
|
previousMillis = currentMillis;
|
||||||
|
digitalWrite(LED_STATUS, !digitalRead(LED_STATUS));
|
||||||
|
}
|
||||||
|
} else if (!musicPlayer.stopped()) {
|
||||||
|
// It's so on again
|
||||||
|
digitalWrite(LED_STATUS, HIGH);
|
||||||
|
} else {
|
||||||
|
// It's so off again
|
||||||
|
digitalWrite(LED_STATUS, LOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void loadTracks(File dir, int level) {
|
||||||
|
while (true) {
|
||||||
|
File entry = dir.openNextFile();
|
||||||
|
if (!entry) return;
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
// Recursive call to scan next dir level
|
||||||
|
loadTracks(entry, level + 1);
|
||||||
|
} else {
|
||||||
|
// Only add files in root dir
|
||||||
|
if (level == 0) {
|
||||||
|
// And only if they have good names
|
||||||
|
if (nameCheck(entry.name())) {
|
||||||
|
strncpy(trackListing[totalTracks], entry.name(), 12);
|
||||||
|
Serial.print(totalTracks); Serial.print("=");
|
||||||
|
Serial.println(trackListing[totalTracks]);
|
||||||
|
totalTracks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.close();
|
||||||
|
// Stop scanning if we hit max
|
||||||
|
if (totalTracks >= TRACKS_MAX) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool nameCheck(char* name) {
|
||||||
|
int len = strlen(name);
|
||||||
|
// Check length
|
||||||
|
if (len <= 4) return false;
|
||||||
|
// Check extension
|
||||||
|
char* ext = strrchr(name,'.');
|
||||||
|
if (!(
|
||||||
|
strcmp(ext,".MP3") == 0 ||
|
||||||
|
strcmp(ext,".OGG") == 0
|
||||||
|
)) return false;
|
||||||
|
// Check first character
|
||||||
|
switch(name[0]) {
|
||||||
|
case '_': return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
21
Gordon_Cole_MP3_Player/LICENSE
Normal file
21
Gordon_Cole_MP3_Player/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Carter Nelson
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
2
Gordon_Cole_MP3_Player/README.md
Normal file
2
Gordon_Cole_MP3_Player/README.md
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Gordon_Cole_MP3_Player
|
||||||
|
Arduino code for simple MP3 player.
|
||||||
Loading…
Reference in a new issue