基于VS1003和SD卡的MP3播放器(2)
本帖最后由 dash1982 于 2013-8-24 08:18 编辑在《基于VS1003和SD卡的MP3播放器》中已经完成了MP3播放器的雏形。然而该播放器只是简单的顺序播放,无法实现随机播放/切歌/音量调节/音质调节等功能。本帖将实现这些需求。
本帖实现的MP3读取并解析从串口的命令,执行相应的工作。
主要功能如下:
to jump to next Song // 播放下一首
[+ or -] to change volume // +, 增加音量;- 减少音量
retrieve current audio information (partial list) // i 获得当前播放音频信息
to pause. //暂停,恢复
resumes play from 2s from begin of file // 重放
Resets and initializes VS10xx chip. //重启VS1003板
Toggle between Mono and Stereo Output. //切换立体声模式
Skip to a predetermined offset of ms in current track. // 越过相应时间播放
Skip a predetermined number of ms in current track. //越过特定时间
[O} turns OFF the VS10xx into low power reset. //重启VS1003
[o} turns ON the VS10xx out of low power reset. //启动VS1003
to toggle SM_DIFF between inphase and differential output // 切换DIFF模式
this help// 打印此帮助菜单
先决条件:
需要用到Sparkfun mp3 Shield的库,sparkfun的mp3板也是基于VS1003的,直接把它拿来用就是了。
介绍:
www.billporter.info/2012/01/28/sparkfun-mp3-shield-arduino-library/
库下载地址:
https://github.com/madsci1016/Sparkfun-MP3-Player-Shield-Arduino-Library
下载完后,解压缩,而后将目录下的SFEMP3Shield整个目录拷贝到你的arduino安装目录的libraries下。SFEMP3Shield包括了SdFat等,但是我们已经在第一个帖子中用到过SdFat库,就不需要重复操作了。从github下载来的档案中同时也含有vs1003的Dsp设置文件,都放置在plugin目录中,这些都不需要。
拷贝完毕后你的ardino安装目录下libraries看起来应该和下面差不多(ArchLinux为例), pwd返回当前工作目录,tree则显示出当前目录树结构:
$ pwd
/media/y/arduino/1.0.5/libraries/SFEMP3Shield
$ tree
.
|-- Examples
| |-- FilePlayer
| | `-- FilePlayer.ino
| |-- MP3Shield_Library_Demo
| | `-- MP3Shield_Library_Demo.ino
| `-- WebPlayer
| `-- WebPlayer.ino
|-- keywords.txt
|-- SFEMP3ShieldConfig.h
|-- SFEMP3Shield.cpp
|-- SFEMP3Shield.h
`-- SFEMP3Shieldmainpage.h
4 directories, 8 files
接下来需要对SFEMP3Shield.h进行一些参数上的修改,我直接定义了一个宏,用于标识自己使用的MP3模块的接线图:
修改:
#if ( SEEEDUINO == 1 )
#define MP3_XCS A3 //Control Chip Select Pin (for accessing SPI Control/Status registers)
#define MP3_XDCS A2 //Data Chip Select / BSYNC Pin
#define MP3_DREQ A1 //Data Request Pin: Player asks for more data
//#define MP3_DREQINT 0 // There is no IRQ used on Seeduino
#define MP3_RESET A0 //Reset is active low
#define SD_SEL 10 //select pin for SD card
#else // otherwise use pinout of typical Sparkfun MP3 Player Shield.
#define MP3_XCS 6 //Control Chip Select Pin (for accessing SPI Control/Status registers)
#define MP3_XDCS 7 //Data Chip Select / BSYNC Pin
#define MP3_DREQ 2 //Data Request Pin: Player asks for more data
#if defined(__AVR_ATmega32U4__)
#define MP3_DREQINT 1 //Corresponding INTx for DREQ pin
#else // swapped between Uno and Leonardo.
#define MP3_DREQINT 0 //Corresponding INTx for DREQ pin
#endif
#define MP3_RESET 8 //Reset is active low
#if ( GRAVITECH == 1 )
#define SD_SEL 4 //select pin for SD card
#else
#define SD_SEL 9 //select pin for SD card
#endif // GRAVITECH
#endif // none SEEEDUINO
为:
#if ( SEEEDUINO == 1 )
#define MP3_XCS A3 //Control Chip Select Pin (for accessing SPI Control/Status registers)
#define MP3_XDCS A2 //Data Chip Select / BSYNC Pin
#define MP3_DREQ A1 //Data Request Pin: Player asks for more data
//#define MP3_DREQINT 0 // There is no IRQ used on Seeduino
#define MP3_RESET A0 //Reset is active low
#define SD_SEL 10 //select pin for SD card
#elif (TAOBAO == 1)
#define MP3_XCS9 //Control Chip Select Pin ( For accessing SPI Control/Status registers )
#define MP3_XDCS 6 // Data Chip Select / BSYNC Pin
#define MP3_DREQ 2 // Data Request Pin: Player ask for more data
#define MP3_RESET 8 // Reset is active low
#define SD_SEL 4 // SD Card Selection
#define MP3_DREQINT 0 //Corresponding INTx for DREQ pin
#else // otherwise use pinout of typical Sparkfun MP3 Player Shield.
#define MP3_XCS 6 //Control Chip Select Pin (for accessing SPI Control/Status registers)
#define MP3_XDCS 7 //Data Chip Select / BSYNC Pin
#define MP3_DREQ 2 //Data Request Pin: Player asks for more data
#if defined(__AVR_ATmega32U4__)
#define MP3_DREQINT 1 //Corresponding INTx for DREQ pin
#else // swapped between Uno and Leonardo.
#define MP3_DREQINT 0 //Corresponding INTx for DREQ pin
#endif
#define MP3_RESET 8 //Reset is active low
#if ( GRAVITECH == 1 )
#define SD_SEL 4 //select pin for SD card
#else
#define SD_SEL 9 //select pin for SD card
#endif // GRAVITECH
#endif // none SEEEDUINO
这个改动很简单,就是单纯的加入从淘宝上买回来的MP3模块的接线而已。于是我们需要在SFEMP3Shield.h的开头定义一下TAOBAO这个变量,比如我就直接在原有文件里加了行:
#define GRAVITECH 0 // uncomment if using the Gravitech's MP3-4NANO shield
#define TAOBAO 1// uncomment if you using the shield bought from taobao.
按照《基于VS1003和SD卡的MP3播放器》里的接线图亦需要做一个改动,我们需要把MP3模块的DREQ口接到arduino nano的D2口上。因为我们修改了如下定义:
#define MP3_DREQ 2 // Data Request Pin: Player ask for more data
与上篇帖子不同的是:本帖中的实现方案,使用arduino板的中断来实时更新需要发送给VS1003的数据。查数据手册可知,arduino nano上使用了D2口为中断0, 而我们Fill vs1003数据段使用了中断0的方式,因而需要把DREQ从以前的D7换到D2。
SFEMP3Shield.cpp亦需要做少许改动:
Ln 217,
if(MP3Mode != (SM_LINE1 | SM_SDINEW)) return 4;
改为:
if(MP3Mode != (SM_SDINEW )) return 4;
再注释掉Ln 235:
if(VSLoadUserCode("patches.053")) return 6;
为:
// if(VSLoadUserCode("patches.053")) return 6;
做这些改动是因为从淘宝上购得的板子输入不是走SM_LINE1, 且不需要加载默认的patches文件给DSP即可工作。
优点
1. 中断方式喂(feed) vs1003,效率比上个版本更高,384KBPS支持无压力。
2. 串口交互实现功能控制,高阶功能均可衍生开发之。
、
局限
1. 文件尺寸可以把nano的Flash撑到25,254 bytes (of a 30,720 byte maximum), 后续需要添加功能(液晶显示,控制按钮等)就得很小心了。
2. 默认进入随机播放状态。顺序播放状态支持是不难的事,设置个全局变量记录下当前播放曲目而已,将在下个版本中加以修正。
3. 'n'可进入下一首,然而上一首功能暂时无法实现 ,因为随机播放每次产生的数值都是随机数。顺序播放时很容易记住当前曲目并前进/后退到相应曲目,随机播放则是无法预知,故暂不支持’上一首‘播放。。
代码
打开arduino IDE界面,输入以下代码,编译运行即可, 在打开的串口监视器窗口中,输入对应的命令即可实现对应的功能:
/*
* This sketch will list all files in the root directory and
* then do a recursive list of all directories on the SD card.
* Only the mp3 files will be listed in the original playlist.
* Then we use original Playlist to generate the actual Playlist.
* Then feeding VS1003 with the bytes read from the mp3 file.
*
*/
#include <SdFat.h>
#include <SdFatUtil.h>
#include <SdFile.h>
#include <SPI.h>
// MP3 Shield Library
#include <SFEMP3Shield.h>
// SD FileSystem Object
SdFat sd;
SdFile file; // General Operating file
/* Object instancing the SFEMP3Shield library
* object for handling all the atrributes, members and functions of the library .
*/
SFEMP3Shield MP3player;
short songnumbers = 0; // songs in SD Card
short open_count = 0; // open_count is for holding the opencounts in genOrigListFile()
short totalsongs = 0; // totalsongs in playlist.
// Variable for holding the latest timestamp
uint16_t latestWriteTime = 0;
uint16_t latestWriteDate = 0;
///////////////////////////////////////////////////////////////////////////
// SD Operations
///////////////////////////////////////////////////////////////////////////
/* This function is for generating the original mp3 list */
/* mp3 list file will use the latest timestamp in SD Card */
short genOrigListFile(const char *f, SdBaseFile * dir, int numTabs) {
// entry is for recording the file information
SdFile entry;
// length is 8(filename)+1(.)+3(suffix)+1(NUL) == 13
char filename;
/* Create file from "f" for recording the result */
open_count++;
if(!file.isOpen())
{
/* Create one */
if(!file.open(f, O_CREAT | O_TRUNC | O_WRITE))
{
/* Create failed will return 3 */
return 3;
}
}
// Set the file's current position to zero
dir->rewind();
// open Next item, until the end of the world.
while(entry.openNext(dir, O_READ)) {
// If this entry is a sub-directory, Print itself and sub-entries
if(entry.isSubDir())
{
// Use spaces for indicating the directory layers.
for(uint8_t i=0; i < numTabs; i++) {
//Serial.print("");
file.write("");
}
memset(filename, '\0', 13);
entry.getFilename(filename);
uint8_t lastchar = strlen(filename);
filename='/';
file.write(filename);
file.write("\r\n");
dir_t dir_time;
entry.dirEntry(&dir_time);
if((dir_time.lastWriteDate > latestWriteDate))
{
latestWriteDate = dir_time.lastWriteDate;
latestWriteTime = dir_time.lastWriteTime;
}
if((dir_time.lastWriteDate == latestWriteDate))
{
if(dir_time.lastWriteTime > latestWriteTime)
{
latestWriteTime = dir_time.lastWriteTime;
}
}
// Get the current index, use this index for recursively print the sub-directories
uint16_t index = dir->curPosition()/32 -1;
SdBaseFile s;
if(s.open(dir, index, O_READ))
{
if(numTabs <=2 )
{
genOrigListFile(f, &s, numTabs + 1);
}
}
entry.close();
}
// Common files will directly be displayed.
else
{
// Calculate how many MP3 exists in SD card.
memset(filename, '\0', 13);
entry.getFilename(filename);
if((filename == 'M') &&
(filename == 'P') &&
(filename == '3'))
{
// Use spaces for indicating the directory layers.
for(uint8_t i=0; i < numTabs; i++) {
file.write("");
}
file.write(filename);
file.write("\r\n");
dir_t dir_time;
entry.dirEntry(&dir_time);
if((dir_time.lastWriteDate > latestWriteDate))
{
latestWriteDate = dir_time.lastWriteDate;
latestWriteTime = dir_time.lastWriteTime;
}
if((dir_time.lastWriteDate == latestWriteDate))
{
if(dir_time.lastWriteTime > latestWriteTime)
{
latestWriteTime = dir_time.lastWriteTime;
}
}
}
entry.close();
}
}
// open_count will decrease for avoiding open it several times.
open_count--;
// Close opened file at the end of all routine, failed if we received 4.
if(open_count == 0)
{
// update the timestamp.
dir_t dir_play;
file.dirEntry(&dir_play);
// Update the timestamp for generated file. later we will use this timestamp for updating.
if (!file.timestamp(T_WRITE, (1980+(latestWriteDate>>9)), ((latestWriteDate>>5)&0XF), (latestWriteDate & 0X1F), (latestWriteTime >> 11), ((latestWriteTime >> 5) & 0X3F), ((latestWriteTime & 0X1F)*2)))
{
//Serial.println("set Write time Failed");
}
file.sync();
if(!file.close())
{
return 4;
}
}
return 0;
}
/* This function will get raw file list from Original file
* then output with the full name to the dest file */
short genPlayList(const char *src, const char *dest)
{
char *str = NULL;
short n=0;
short SpaceCounts, i, j, k;
char *dirname = {
0};
// 10 layers means (8+1)*10layer+13(file)
char *prefix = NULL; //(char *)malloc(9*4 + 13);
file.open(src, O_READ);
if(!file.isOpen())
{
return 1;
}
// pl == PlayList file
SdFile plfile(dest, O_CREAT | O_TRUNC | O_WRITE);
if(!plfile.isOpen())
{
return 2;
}
for( j = 0; j < 4; j++)
{
// malloc size is 8(dir name) + 1("/") + 1(NULL) = 10
dirname = (char *)malloc(10);
}
str = (char *)malloc(100);
prefix = (char *)malloc(9*4 + 13);
while((n = file.fgets(str, sizeof(str))) > 0)
{
// Change the '\r' to the NUL, means the end of the string.
short len = strlen(str) -1;
if(str == '\n')
{
str = 0;
}
if(str == '\r')
{
str = 0;
}
SpaceCounts = 0;
for( i = 0; i < strlen(str); i++)
{
if(str == ' ')
{
SpaceCounts++;
}
}
// Directly print the MP3 files under the root directory
if((!strncmp(str+strlen(str)-3, "MP3", 4)) && (SpaceCounts == 0))
{
plfile.write(str);
plfile.write("\r\n");
}
// Processing directory entries
if(str == '/')
{
// Remove the SpaceCounts and copy the rest of the name into string array dirname[]
strcpy(dirname[(SpaceCounts/2)], (char *)(str+SpaceCounts));
}
// Print the MP3 which locates in sub-directories
if((SpaceCounts >= 2) && (str != '/'))
{
memset(prefix, '\0', 9*4 + 13);
for( k = 0; k <= (SpaceCounts/2); k++ )
{
strcat(prefix, dirname);
}
strcat(prefix, str+SpaceCounts);
plfile.write(prefix, strlen(prefix));
plfile.write("\r\n");
delay(10);
}
} // end of while()
for(j = 0; j < 4; j++)
{
free(dirname);
}
free(str);
free(prefix);
// Generate the real player file.
if(!file.close())
{
return 5;
}
if(!plfile.close())
{
return 6;
}
return 0;
}
/* This function is for scanning all of the directories and sub-items
* to fetch the latest timestamp, we will use the latest timestamp for
* judging update playlist or not
*/
void getLatestStamp(SdBaseFile * dir, short numTabs) {
// entry is for recording the file information
SdFile entry;
// length is 8(filename)+1(.)+3(suffix)+1(NUL) == 13
char filename;
// open Next item, until the end of the world.
while(entry.openNext(dir, O_READ)) {
// If this entry is a sub-directory, Print itself and sub-entries
if(entry.isSubDir())
{
// Get the latest timestamp of the directory.
dir_t dir_time;
entry.dirEntry(&dir_time);
if((dir_time.lastWriteDate > latestWriteDate))
{
latestWriteDate = dir_time.lastWriteDate;
latestWriteTime = dir_time.lastWriteTime;
}
if((dir_time.lastWriteDate == latestWriteDate))
{
if(dir_time.lastWriteTime > latestWriteTime)
{
latestWriteTime = dir_time.lastWriteTime;
}
}
// Get the current index, use this index for recursively print the sub-directories
uint16_t index = dir->curPosition()/32 -1;
SdBaseFile s;
if(s.open(dir, index, O_READ))
{
if(numTabs <=2) getLatestStamp(&s, numTabs + 1);
}
entry.close();
}
// Common files goes here.
else
{
// Calculate how many MP3 exists in SD card.
entry.getFilename(filename);
if((filename == 'M') &&
(filename == 'P') &&
(filename == '3'))
{
// Get the latest timestamp of the directory.
dir_t dir_time;
entry.dirEntry(&dir_time);
if((dir_time.lastWriteDate > latestWriteDate))
{
latestWriteDate = dir_time.lastWriteDate;
latestWriteTime = dir_time.lastWriteTime;
}
if((dir_time.lastWriteDate == latestWriteDate))
{
if(dir_time.lastWriteTime > latestWriteTime)
{
latestWriteTime = dir_time.lastWriteTime;
}
}
}
entry.close();
}
}
}
/* This function will get the file's timestamp, we want to
* fetch the orginal playlist's timestamp, use it to judging update
* playlist or not.
*/
short getfileStamp(const char *f, uint16_t *date, uint16_t *time)
{
SdFile localfile(f, O_READ);
if(!localfile.isOpen())
{
//Serial.println("Open Local File Failed!");
return 2;
}
// Get the latest timestamp of the directory.
dir_t dir_time;
localfile.dirEntry(&dir_time);
*date = dir_time.lastWriteDate;
*time = dir_time.lastWriteTime;
if(!localfile.close())
{
return 4;
}
return 0;
}
/* This function is for getting the numbers in PlayList */
short getListLength(char *playlistfile)
{
short tmpnum = 0;
char line;
int n;
delay(100);
// Read Items from the Playlist file.
// open PlayList file
SdFile rdfile(playlistfile, O_READ);
if (!rdfile.isOpen())
{
//Serial.println("Open PlayList Failed!");
/* Actually 9999 is not good */
return 9999;
}
// read lines from the file
while ((n = rdfile.fgets(line, sizeof(line))) > 0)
{
if (line == '\n')
{
tmpnum++;
}
else
{
;
}
}
rdfile.close();
return tmpnum;
}
void selectSong(char *playlistfile, char *song, short select_number)
{
short tmpnum = select_number;
char line;
int n;
delay(100);
// Read Items from the Playlist file.
// open PlayList file
SdFile rdfile(playlistfile, O_READ);
if (!rdfile.isOpen())
{
Serial.println("Open PlayList Failed!");
return;
}
// read lines from the file
while ((n = rdfile.fgets(line, sizeof(line))) > 0)
{
if (line == '\n')
{
tmpnum--;
//songs_in_list++;
}
else
{
;
}
if(tmpnum == 0)
{
strncpy(song, line, strlen(line)-1);
song='\0';
}
}
rdfile.close();
}
//------------------------------------------------------------------------------
/**
* \brief Decode the Menu.
*
* Parses through the characters of the users input, executing corresponding
* MP3player library functions and features then displaying a brief menu and
* prompting for next input command.
*/
void parse_menu(byte key_command) {
uint8_t result; // result code from some function as to be tested at later time.
// Note these buffer may be desired to exist globably.
// but do take much space if only needed temporarily, hence they are here.
char title; // buffer to contain the extract the Title from the current filehandles
char artist; // buffer to contain the extract the artist name from the current filehandles
char album; // buffer to contain the extract the album name from the current filehandles
Serial.print(F("Received command: "));
Serial.write(key_command);
Serial.println(F(" "));
if(key_command == 'n')
{
/* Since random, this next won't go back to previous playlist */
// First stop the current playing.
Serial.println(F("Stopping"));
MP3player.stopTrack();
// Then select a new song.
char song;
//randomSeed(analogRead(4));
selectSong("PLT.TXT", song, random(1, totalsongs));
Serial.println(song);
// Play the selected song.
MP3player.playMP3(song, 0);
}
else if((key_command == '-') || (key_command == '+')) {
union twobyte mp3_vol; // create key_command existing variable that can be both word and double byte of left and right.
mp3_vol.word = MP3player.getVolume(); // returns a double uint8_t of Left and Right packed into int16_t
if(key_command == '-') { // note dB is negative
// assume equal balance and use byte for math
if(mp3_vol.byte >= 254) { // range check
mp3_vol.byte = 254;
}
else {
mp3_vol.byte += 2; // keep it simpler with whole dB's
}
}
else {
if(mp3_vol.byte <= 2) { // range check
mp3_vol.byte = 2;
}
else {
mp3_vol.byte -= 2;
}
}
// push byte into both left and right assuming equal balance.
MP3player.setVolume(mp3_vol.byte, mp3_vol.byte); // commit new volume
Serial.print(F("Volume changed to -"));
Serial.print(mp3_vol.byte>>1, 1);
Serial.println(F(""));
}
else if(key_command == 'i') {
MP3player.getAudioInfo();
}
else if(key_command == 'p') {
if( MP3player.getState() == playback) {
MP3player.pauseMusic();
Serial.println(F("Pausing"));
}
else if( MP3player.getState() == paused_playback) {
MP3player.resumeMusic();
Serial.println(F("Resuming"));
}
else {
Serial.println(F("Not Playing!"));
}
}
else if(key_command == 'R') {
MP3player.stopTrack();
MP3player.vs_init();
Serial.println(F("Reseting VS10xx chip"));
}
else if(key_command == 'M') {
uint16_t monostate = MP3player.getMonoMode();
Serial.print(F("Mono Mode "));
if(monostate == 0) {
MP3player.setMonoMode(1);
Serial.println(F("Enabled."));
}
else {
MP3player.setMonoMode(0);
Serial.println(F("Disabled."));
}
}
else if(key_command == 'g') {
int32_t offset_ms = 20000; // Note this is just an example, try your own number.
Serial.print(F("jumping to "));
Serial.print(offset_ms, DEC);
Serial.println(F(""));
result = MP3player.skipTo(offset_ms);
if(result != 0) {
Serial.print(F("Error code: "));
Serial.print(result);
Serial.println(F(" when trying to skip track"));
}
}
else if(key_command == 'k') {
int32_t offset_ms = -1000; // Note this is just an example, try your own number.
Serial.print(F("moving = "));
Serial.print(offset_ms, DEC);
Serial.println(F(""));
result = MP3player.skip(offset_ms);
if(result != 0) {
Serial.print(F("Error code: "));
Serial.print(result);
Serial.println(F(" when trying to skip track"));
}
}
else if(key_command == 'O') {
MP3player.end();
Serial.println(F("VS10xx placed into low power reset mode."));
}
else if(key_command == 'o') {
MP3player.begin();
Serial.println(F("VS10xx restored from low power reset mode."));
}
else if(key_command == 'D') {
uint16_t diff_state = MP3player.getDifferentialOutput();
Serial.print(F("Differential Mode "));
if(diff_state == 0) {
MP3player.setDifferentialOutput(1);
Serial.println(F("Enabled."));
}
else {
MP3player.setDifferentialOutput(0);
Serial.println(F("Disabled."));
}
}
else if(key_command == 'h') {
help();
}
// print prompt after key stroke has been processed.
Serial.println(F("Enter n,+,-,i,p,r,R,M,g,k,h,O,o,D,S :"));
}
//------------------------------------------------------------------------------
/**
* \brief Print Help Menu.
*
* Prints a full menu of the commands available along with descriptions.
*/
void help() {
Serial.println(F("Arduino SFEMP3Shield Library Example:"));
Serial.println(F(" courtesy of Bill Porter & Michael P. Flaga"));
Serial.println(F("COMMANDS:"));
Serial.println(F(" to jump to next Song"));
Serial.println(F(" [+ or -] to change volume"));
Serial.println(F(" retrieve current audio information (partial list)"));
Serial.println(F(" to pause."));
Serial.println(F(" resumes play from 2s from begin of file"));
Serial.println(F(" Resets and initializes VS10xx chip."));
Serial.println(F(" Toggle between Mono and Stereo Output."));
Serial.println(F(" Skip to a predetermined offset of ms in current track."));
Serial.println(F(" Skip a predetermined number of ms in current track."));
Serial.println(F(" [O} turns OFF the VS10xx into low power reset."));
Serial.println(F(" [o} turns ON the VS10xx out of low power reset."));
Serial.println(F(" to toggle SM_DIFF between inphase and differential output"));
Serial.println(F(" this help"));
}
void setup()
{
bool updateflag = 0;
delay(10);
Serial.begin(9600);
// See Free RAM
PgmPrint("Free RAM: ");
Serial.println(FreeRam());
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards.use SPI_FULL_SPEED for better performance.
digitalWrite(9,HIGH);
delay(2000);
if (!sd.begin(4, SPI_HALF_SPEED)) sd.initErrorHalt();
if(!sd.chdir("/")) sd.errorHalt("sd.chdir");
//Initialize the MP3 Player Shield
uint8_t result = MP3player.begin();
//check result, see readme for error codes.
if(result != 0) {
Serial.print(F("Error code: "));
Serial.print(result);
Serial.println(F(" when trying to start MP3 player"));
if( result == 6 ) {
Serial.println(F("Warning: patch file not found, skipping.")); // can be removed for space, if needed.
Serial.println(F("Use the \"d\" command to verify SdCard can be read")); // can be removed for space, if needed.
}
}
// Start at beginning of the root directory.
// rewind is used for set current position to zero.
sd.vwd()->rewind();
// 1. If no playlist file exists, scan the whole SD to generate one
// 2. Else Check the timestamp of the playlist to see if update is needed
// 3. If update is needed, generate playlist again.
sd.vwd()->rewind();
if(!sd.exists("PL_OK.TXT")) // Condition 1
{
Serial.println("No PL_OK.TXT found!");
updateflag = 1;
}
else // Condition 2
{
Serial.println("PL_OK has been created, need to check its timestamp!");
// Set global Variables to 0 for avoiding potential mis-using
latestWriteTime = 0;
latestWriteDate = 0;
sd.vwd()->rewind();
getLatestStamp(sd.vwd(), 0);
// Get the PlayList File's timestamp, for comparing.
uint16_t ldate = 0;
uint16_t ltime = 0;
short rvalue = getfileStamp("PL_OK.TXT", &ldate,<ime);
if( (ldate == latestWriteDate) && ( ltime == latestWriteTime) )
{
Serial.println("PlayList is the newest version, update not needed!");
}
else
{
Serial.println("Need Update PlayList!");
updateflag = 1;
}
}
sd.vwd()->rewind();
if(updateflag) // Condition 3, update playlist or not
{
// Call genOrigListFile() to fetch all of the MP3 and directory layer information
short error_reason = genOrigListFile("PL_OK.TXT", sd.vwd(), 0);
if(error_reason != 0)
{
Serial.print("genOrigListFile error number is ");
Serial.println(error_reason);
}
// Call genPlayList() to generate the actual PlayList file
error_reason = genPlayList("PL_OK.TXT", "PLT.TXT");
if(error_reason != 0)
{
Serial.print("genPlayList error number is ");
Serial.println(error_reason);
}
Serial.println("Generate OrigiList and PlayList Done!");
}
else
{
Serial.println("No Update!");
}
/* For saving space */
totalsongs = getListLength("PLT.TXT");
/* Generate random seed , a0 will be a random variable*/
randomSeed(analogRead(0));
Serial.println("Done setup()");
}
void loop() {
//Serial.println(MP3player.getState());
if((MP3player.getState() == initialized) | (MP3player.getState() == ready))
{
char song;
selectSong("PLT.TXT", song, random(1, totalsongs));
Serial.println(song);
MP3player.playMP3(song, 0);
Serial.println(F("Enter n,+,-,i,p,r,R,M,g,k,h,O,o,D,S :"));
}
else
{
if(Serial.available()) {
parse_menu(Serial.read()); // get command from serial input
}
delay(100);
}
}
本帖最后由 edmondhuang 于 2013-12-26 19:03 编辑
lz灰常厉害耶{:soso_e128:},百分百是个linux程序猿,鉴定完毕 偶然发现这句-〉 This allows the Arduino to perform other tasks while music is playing 赞一个,正在试着跟着你的做
页:
[1]