dash1982 发表于 2013-8-23 22:50:28

基于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,&ltime);
    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 18:59:56

本帖最后由 edmondhuang 于 2013-12-26 19:03 编辑

lz灰常厉害耶{:soso_e128:},百分百是个linux程序猿,鉴定完毕

edmondhuang 发表于 2013-12-27 13:32:13

偶然发现这句-〉 This allows the Arduino to perform other tasks while music is playing

3Dmaker 发表于 2014-12-19 14:51:25

赞一个,正在试着跟着你的做
页: [1]
查看完整版本: 基于VS1003和SD卡的MP3播放器(2)