极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 15991|回复: 3

基于VS1003和SD卡的MP3播放器(2)

[复制链接]
发表于 2013-8-23 22:50:28 | 显示全部楼层 |阅读模式
本帖最后由 dash1982 于 2013-8-24 08:18 编辑

     在《基于VS1003和SD卡的MP3播放器》中已经完成了MP3播放器的雏形。然而该播放器只是简单的顺序播放,无法实现随机播放/切歌/音量调节/音质调节等功能。本帖将实现这些需求。

    本帖实现的MP3读取并解析从串口的命令,执行相应的工作。
     主要功能如下:

[n] to jump to next Song                // 播放下一首
[+ or -] to change volume             // +, 增加音量;- 减少音量
retrieve current audio information (partial list)    // i 获得当前播放音频信息
[p] to pause.     //暂停,恢复
[r] resumes play from 2s from begin of file    // 重放
[R] Resets and initializes VS10xx chip.    //重启VS1003板
[M] Toggle between Mono and Stereo Output.    //切换立体声模式
[g] Skip to a predetermined offset of ms in current track.    // 越过相应时间播放
[k] 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
[D] to toggle SM_DIFF between inphase and differential output    // 切换DIFF模式
[h] 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模块的接线图:
修改:

  1. #if ( SEEEDUINO == 1 )
  2.   #define MP3_XCS             A3 //Control Chip Select Pin (for accessing SPI Control/Status registers)
  3.   #define MP3_XDCS            A2 //Data Chip Select / BSYNC Pin
  4.   #define MP3_DREQ            A1 //Data Request Pin: Player asks for more data
  5.   //#define MP3_DREQINT        0 // There is no IRQ used on Seeduino
  6.   #define MP3_RESET           A0 //Reset is active low
  7.   #define SD_SEL              10 //select pin for SD card
  8. #else // otherwise use pinout of typical Sparkfun MP3 Player Shield.
  9.   #define MP3_XCS              6 //Control Chip Select Pin (for accessing SPI Control/Status registers)
  10.   #define MP3_XDCS             7 //Data Chip Select / BSYNC Pin
  11.   #define MP3_DREQ             2 //Data Request Pin: Player asks for more data
  12.   #if defined(__AVR_ATmega32U4__)
  13.     #define MP3_DREQINT          1 //Corresponding INTx for DREQ pin
  14.   #else // swapped between Uno and Leonardo.
  15.     #define MP3_DREQINT          0 //Corresponding INTx for DREQ pin
  16.   #endif
  17.   #define MP3_RESET            8 //Reset is active low
  18.   #if ( GRAVITECH == 1 )
  19.     #define SD_SEL               4 //select pin for SD card
  20.   #else
  21.     #define SD_SEL               9 //select pin for SD card
  22.   #endif // GRAVITECH
  23. #endif // none SEEEDUINO
复制代码

为:
  1. #if ( SEEEDUINO == 1 )
  2.   #define MP3_XCS             A3 //Control Chip Select Pin (for accessing SPI Control/Status registers)
  3.   #define MP3_XDCS            A2 //Data Chip Select / BSYNC Pin
  4.   #define MP3_DREQ            A1 //Data Request Pin: Player asks for more data
  5.   //#define MP3_DREQINT        0 // There is no IRQ used on Seeduino
  6.   #define MP3_RESET           A0 //Reset is active low
  7.   #define SD_SEL              10 //select pin for SD card
  8. #elif (TAOBAO == 1)
  9.   #define MP3_XCS  9        //Control Chip Select Pin ( For accessing SPI Control/Status registers )
  10.   #define MP3_XDCS 6        // Data Chip Select / BSYNC Pin
  11.   #define MP3_DREQ 2        // Data Request Pin: Player ask for more data
  12.   #define MP3_RESET 8        // Reset is active low
  13.   #define SD_SEL 4         // SD Card Selection
  14.   #define MP3_DREQINT          0 //Corresponding INTx for DREQ pin
  15. #else // otherwise use pinout of typical Sparkfun MP3 Player Shield.
  16.   #define MP3_XCS              6 //Control Chip Select Pin (for accessing SPI Control/Status registers)
  17.   #define MP3_XDCS             7 //Data Chip Select / BSYNC Pin
  18.   #define MP3_DREQ             2 //Data Request Pin: Player asks for more data
  19.   #if defined(__AVR_ATmega32U4__)
  20.     #define MP3_DREQINT          1 //Corresponding INTx for DREQ pin
  21.   #else // swapped between Uno and Leonardo.
  22.     #define MP3_DREQINT          0 //Corresponding INTx for DREQ pin
  23.   #endif
  24.   #define MP3_RESET            8 //Reset is active low
  25.   #if ( GRAVITECH == 1 )
  26.     #define SD_SEL               4 //select pin for SD card
  27.   #else
  28.     #define SD_SEL               9 //select pin for SD card
  29.   #endif // GRAVITECH
  30. #endif // none SEEEDUINO
复制代码


    这个改动很简单,就是单纯的加入从淘宝上买回来的MP3模块的接线而已。于是我们需要在SFEMP3Shield.h的开头定义一下TAOBAO这个变量,比如我就直接在原有文件里加了行:
  1. #define GRAVITECH 0 // uncomment if using the Gravitech's MP3-4NANO shield

  2. #define TAOBAO 1  // uncomment if you using the shield bought from taobao.
复制代码


    按照《基于VS1003和SD卡的MP3播放器》里的接线图亦需要做一个改动,我们需要把MP3模块的DREQ口接到arduino nano的D2口上。因为我们修改了如下定义:
  1.   #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,
  1.   if(MP3Mode != (SM_LINE1 | SM_SDINEW)) return 4;
复制代码

改为:
  1.   if(MP3Mode != (SM_SDINEW )) return 4;
复制代码


再注释掉Ln 235:
  1.   if(VSLoadUserCode("patches.053")) return 6;
复制代码

为:
  1. // 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界面,输入以下代码,编译运行即可, 在打开的串口监视器窗口中,输入对应的命令即可实现对应的功能:

  1. /*
  2. * This sketch will list all files in the root directory and
  3. * then do a recursive list of all directories on the SD card.
  4. * Only the mp3 files will be listed in the original playlist.
  5. * Then we use original Playlist to generate the actual Playlist.
  6. * Then feeding VS1003 with the bytes read from the mp3 file.
  7. *
  8. */
  9. #include <SdFat.h>
  10. #include <SdFatUtil.h>
  11. #include <SdFile.h>
  12. #include <SPI.h>
  13. // MP3 Shield Library
  14. #include <SFEMP3Shield.h>

  15. // SD FileSystem Object
  16. SdFat sd;
  17. SdFile file;                // General Operating file


  18. /* Object instancing the SFEMP3Shield library
  19. * object for handling all the atrributes, members and functions of the library .
  20. */
  21. SFEMP3Shield MP3player;

  22. short songnumbers = 0;         // songs in SD Card
  23. short open_count = 0;        // open_count is for holding the opencounts in genOrigListFile()

  24. short totalsongs = 0;   // totalsongs in playlist.

  25. // Variable for holding the latest timestamp
  26. uint16_t latestWriteTime = 0;
  27. uint16_t latestWriteDate = 0;

  28. ///////////////////////////////////////////////////////////////////////////
  29. // SD Operations
  30. ///////////////////////////////////////////////////////////////////////////

  31. /* This function is for generating the original mp3 list */
  32. /* mp3 list file will use the latest timestamp in SD Card */
  33. short genOrigListFile(const char *f, SdBaseFile * dir, int numTabs) {
  34.   // entry is for recording the file information
  35.   SdFile entry;
  36.   // length is 8(filename)+1(.)+3(suffix)+1(NUL) == 13
  37.   char filename[13];

  38.   /* Create file from "f" for recording the result */
  39.   open_count++;
  40.   if(!file.isOpen())
  41.   {
  42.     /* Create one */
  43.     if(!file.open(f, O_CREAT | O_TRUNC | O_WRITE))
  44.     {
  45.       /* Create failed will return 3 */
  46.       return 3;
  47.     }
  48.   }

  49.   // Set the file's current position to zero
  50.   dir->rewind();

  51.   // open Next item, until the end of the world.
  52.   while(entry.openNext(dir, O_READ)) {   

  53.     // If this entry is a sub-directory, Print itself and sub-entries
  54.     if(entry.isSubDir())
  55.     {     
  56.       // Use spaces for indicating the directory layers.
  57.       for(uint8_t i=0; i < numTabs; i++) {
  58.         //Serial.print("  ");
  59.         file.write("  ");     
  60.       }

  61.       memset(filename, '\0', 13);
  62.       entry.getFilename(filename);

  63.       uint8_t lastchar = strlen(filename);
  64.       filename[lastchar]='/';
  65.       file.write(filename);
  66.       file.write("\r\n");

  67.       dir_t dir_time;
  68.       entry.dirEntry(&dir_time);

  69.       if((dir_time.lastWriteDate > latestWriteDate))
  70.       {
  71.         latestWriteDate = dir_time.lastWriteDate;
  72.         latestWriteTime = dir_time.lastWriteTime;
  73.       }
  74.       if((dir_time.lastWriteDate == latestWriteDate))
  75.       {
  76.         if(dir_time.lastWriteTime > latestWriteTime)
  77.         {
  78.           latestWriteTime = dir_time.lastWriteTime;
  79.         }
  80.       }

  81.       // Get the current index, use this index for recursively print the sub-directories
  82.       uint16_t index = dir->curPosition()/32 -1;
  83.       SdBaseFile s;
  84.       if(s.open(dir, index, O_READ))
  85.       {
  86.         if(numTabs <=2 )
  87.         {
  88.           genOrigListFile(f, &s, numTabs + 1);
  89.         }
  90.       }
  91.       entry.close();      
  92.     }   
  93.     // Common files will directly be displayed.
  94.     else
  95.     {      
  96.       // Calculate how many MP3 exists in SD card.
  97.       memset(filename, '\0', 13);
  98.       entry.getFilename(filename);
  99.       if((filename[strlen(filename)-3] == 'M') &&
  100.         (filename[strlen(filename)-2] == 'P') &&
  101.         (filename[strlen(filename)-1] == '3'))
  102.       {

  103.         // Use spaces for indicating the directory layers.
  104.         for(uint8_t i=0; i < numTabs; i++) {
  105.           file.write("  ");     
  106.         }
  107.         file.write(filename);
  108.         file.write("\r\n");

  109.         dir_t dir_time;
  110.         entry.dirEntry(&dir_time);

  111.         if((dir_time.lastWriteDate > latestWriteDate))
  112.         {
  113.           latestWriteDate = dir_time.lastWriteDate;
  114.           latestWriteTime = dir_time.lastWriteTime;
  115.         }
  116.         if((dir_time.lastWriteDate == latestWriteDate))
  117.         {
  118.           if(dir_time.lastWriteTime > latestWriteTime)
  119.           {
  120.             latestWriteTime = dir_time.lastWriteTime;
  121.           }
  122.         }
  123.       }         

  124.       entry.close();
  125.     }
  126.   }
  127.   // open_count will decrease for avoiding open it several times.
  128.   open_count--;
  129.   // Close opened file at the end of all routine, failed if we received 4.
  130.   if(open_count == 0)
  131.   {
  132.     // update the timestamp.
  133.     dir_t dir_play;
  134.     file.dirEntry(&dir_play);

  135.     // Update the timestamp for generated file. later we will use this timestamp for updating.     
  136.     if (!file.timestamp(T_WRITE, (1980+(latestWriteDate>>9)), ((latestWriteDate>>5)&0XF), (latestWriteDate & 0X1F), (latestWriteTime >> 11), ((latestWriteTime >> 5) & 0X3F), ((latestWriteTime & 0X1F)*2)))
  137.     {
  138.       //Serial.println("set Write time Failed");
  139.     }

  140.     file.sync();         

  141.     if(!file.close())
  142.     {
  143.       return 4;
  144.     }
  145.   }

  146.   return 0;
  147. }


  148. /* This function will get raw file list from Original file
  149. * then output with the full name to the dest file */
  150. short genPlayList(const char *src, const char *dest)
  151. {
  152.   char *str = NULL;
  153.   short n=0;
  154.   short SpaceCounts, i, j, k;
  155.   char *dirname[4] = {
  156.     0  };
  157.   // 10 layers means (8+1)*10layer+13(file)
  158.   char *prefix = NULL; //(char *)malloc(9*4 + 13);


  159.   file.open(src, O_READ);
  160.   if(!file.isOpen())
  161.   {
  162.     return 1;
  163.   }

  164.   // pl == PlayList file
  165.   SdFile plfile(dest, O_CREAT | O_TRUNC | O_WRITE);
  166.   if(!plfile.isOpen())
  167.   {
  168.     return 2;
  169.   }

  170.   for( j = 0; j < 4; j++)
  171.   {
  172.     // malloc size is 8(dir name) + 1("/") + 1(NULL) = 10
  173.     dirname[j] = (char *)malloc(10);
  174.   }

  175.   str = (char *)malloc(100);
  176.   prefix = (char *)malloc(9*4 + 13);

  177.   while((n = file.fgets(str, sizeof(str))) > 0)
  178.   {
  179.     // Change the '\r' to the NUL, means the end of the string.
  180.     short len = strlen(str) -1;

  181.     if(str[len] == '\n')
  182.     {     
  183.       str[len] = 0;
  184.     }
  185.     if(str[len -1] == '\r')
  186.     {     
  187.       str[len - 1] = 0;
  188.     }

  189.     SpaceCounts = 0;
  190.     for( i = 0; i < strlen(str); i++)
  191.     {
  192.       if(str[i] == ' ')
  193.       {
  194.         SpaceCounts++;
  195.       }
  196.     }


  197.     // Directly print the MP3 files under the root directory
  198.     if((!strncmp(str+strlen(str)-3, "MP3", 4)) && (SpaceCounts == 0))
  199.     {
  200.       plfile.write(str);
  201.       plfile.write("\r\n");
  202.     }

  203.     // Processing directory entries
  204.     if(str[strlen(str) - 1] == '/')
  205.     {
  206.       // Remove the SpaceCounts and copy the rest of the name into string array dirname[]
  207.       strcpy(dirname[(SpaceCounts/2)], (char *)(str+SpaceCounts));
  208.     }

  209.     // Print the MP3 which locates in sub-directories
  210.     if((SpaceCounts >= 2) && (str[strlen(str) - 1] != '/'))
  211.     {
  212.       memset(prefix, '\0', 9*4 + 13);
  213.       for( k = 0; k <= (SpaceCounts/2); k++ )
  214.       {
  215.         strcat(prefix, dirname[k]);
  216.       }
  217.       strcat(prefix, str+SpaceCounts);
  218.       plfile.write(prefix, strlen(prefix));
  219.       plfile.write("\r\n");
  220.       delay(10);
  221.     }

  222.   } // end of while()


  223.   for(j = 0; j < 4; j++)
  224.   {
  225.     free(dirname[j]);
  226.   }  
  227.   free(str);
  228.   free(prefix);

  229.   // Generate the real player file.
  230.   if(!file.close())
  231.   {
  232.     return 5;
  233.   }
  234.   if(!plfile.close())
  235.   {
  236.     return 6;
  237.   }

  238.   return 0;  
  239. }

  240. /* This function is for scanning all of the directories and sub-items
  241. * to fetch the latest timestamp, we will use the latest timestamp for
  242. * judging update playlist or not
  243. */
  244. void getLatestStamp(SdBaseFile * dir, short numTabs) {
  245.   // entry is for recording the file information
  246.   SdFile entry;
  247.   // length is 8(filename)+1(.)+3(suffix)+1(NUL) == 13
  248.   char filename[13];

  249.   // open Next item, until the end of the world.
  250.   while(entry.openNext(dir, O_READ)) {   
  251.     // If this entry is a sub-directory, Print itself and sub-entries
  252.     if(entry.isSubDir())
  253.     {
  254.       // Get the latest timestamp of the directory.
  255.       dir_t dir_time;
  256.       entry.dirEntry(&dir_time);

  257.       if((dir_time.lastWriteDate > latestWriteDate))
  258.       {
  259.         latestWriteDate = dir_time.lastWriteDate;
  260.         latestWriteTime = dir_time.lastWriteTime;
  261.       }
  262.       if((dir_time.lastWriteDate == latestWriteDate))
  263.       {
  264.         if(dir_time.lastWriteTime > latestWriteTime)
  265.         {
  266.           latestWriteTime = dir_time.lastWriteTime;
  267.         }
  268.       }

  269.       // Get the current index, use this index for recursively print the sub-directories
  270.       uint16_t index = dir->curPosition()/32 -1;
  271.       SdBaseFile s;
  272.       if(s.open(dir, index, O_READ))
  273.       {
  274.         if(numTabs <=2) getLatestStamp(&s, numTabs + 1);
  275.       }
  276.       entry.close();      
  277.     }

  278.     // Common files goes here.
  279.     else
  280.     {      
  281.       // Calculate how many MP3 exists in SD card.
  282.       entry.getFilename(filename);      
  283.       if((filename[strlen(filename)-3] == 'M') &&
  284.         (filename[strlen(filename)-2] == 'P') &&
  285.         (filename[strlen(filename)-1] == '3'))
  286.       {
  287.         // Get the latest timestamp of the directory.
  288.         dir_t dir_time;
  289.         entry.dirEntry(&dir_time);

  290.         if((dir_time.lastWriteDate > latestWriteDate))
  291.         {
  292.           latestWriteDate = dir_time.lastWriteDate;
  293.           latestWriteTime = dir_time.lastWriteTime;
  294.         }
  295.         if((dir_time.lastWriteDate == latestWriteDate))
  296.         {
  297.           if(dir_time.lastWriteTime > latestWriteTime)
  298.           {
  299.             latestWriteTime = dir_time.lastWriteTime;
  300.           }
  301.         }
  302.       }   
  303.       entry.close();
  304.     }
  305.   }
  306. }


  307. /* This function will get the file's timestamp, we want to
  308. * fetch the orginal playlist's timestamp, use it to judging update
  309. * playlist or not.
  310. */
  311. short getfileStamp(const char *f, uint16_t *date, uint16_t *time)
  312. {
  313.   SdFile localfile(f, O_READ);
  314.   if(!localfile.isOpen())
  315.   {
  316.     //Serial.println("Open Local File Failed!");
  317.     return 2;
  318.   }

  319.   // Get the latest timestamp of the directory.
  320.   dir_t dir_time;
  321.   localfile.dirEntry(&dir_time);

  322.   *date = dir_time.lastWriteDate;
  323.   *time = dir_time.lastWriteTime;

  324.   if(!localfile.close())
  325.   {
  326.     return 4;
  327.   }
  328.   return 0;
  329. }

  330. /* This function is for getting the numbers in PlayList */
  331. short getListLength(char *playlistfile)
  332. {
  333.   short tmpnum = 0;
  334.   char line[25];
  335.   int n;

  336.   delay(100);
  337.   // Read Items from the Playlist file.
  338.   // open PlayList file
  339.   SdFile rdfile(playlistfile, O_READ);
  340.   if (!rdfile.isOpen())
  341.   {
  342.     //Serial.println("Open PlayList Failed!");
  343.     /* Actually 9999 is not good */
  344.     return 9999;
  345.   }

  346.   // read lines from the file
  347.   while ((n = rdfile.fgets(line, sizeof(line))) > 0)
  348.   {
  349.     if (line[n - 1] == '\n')
  350.     {
  351.       tmpnum++;
  352.     }
  353.     else
  354.     {
  355.       ;
  356.     }                              
  357.   }       
  358.   rdfile.close();
  359.   return tmpnum;
  360. }


  361. void selectSong(char *playlistfile, char *song, short select_number)
  362. {
  363.   short tmpnum = select_number;
  364.   char line[50];
  365.   int n;

  366.   delay(100);
  367.   // Read Items from the Playlist file.
  368.   // open PlayList file
  369.   SdFile rdfile(playlistfile, O_READ);
  370.   if (!rdfile.isOpen())
  371.   {
  372.     Serial.println("Open PlayList Failed!");
  373.     return;
  374.   }

  375.   // read lines from the file
  376.   while ((n = rdfile.fgets(line, sizeof(line))) > 0)
  377.   {
  378.     if (line[n - 1] == '\n')
  379.     {
  380.       tmpnum--;
  381.       //songs_in_list++;
  382.     }
  383.     else
  384.     {
  385.       ;
  386.     }
  387.     if(tmpnum == 0)
  388.     {
  389.       strncpy(song, line, strlen(line)-1);
  390.       song[strlen(line)-1]='\0';
  391.     }

  392.   }       
  393.   rdfile.close();
  394. }



  395. //------------------------------------------------------------------------------
  396. /**
  397. * \brief Decode the Menu.
  398. *
  399. * Parses through the characters of the users input, executing corresponding
  400. * MP3player library functions and features then displaying a brief menu and
  401. * prompting for next input command.
  402. */
  403. void parse_menu(byte key_command) {

  404.   uint8_t result; // result code from some function as to be tested at later time.

  405.   // Note these buffer may be desired to exist globably.
  406.   // but do take much space if only needed temporarily, hence they are here.
  407.   char title[30]; // buffer to contain the extract the Title from the current filehandles
  408.   char artist[30]; // buffer to contain the extract the artist name from the current filehandles
  409.   char album[30]; // buffer to contain the extract the album name from the current filehandles

  410.   Serial.print(F("Received command: "));
  411.   Serial.write(key_command);
  412.   Serial.println(F(" "));

  413.   if(key_command == 'n')
  414.   {
  415.     /* Since random, this next won't go back to previous playlist */
  416.     // First stop the current playing.
  417.     Serial.println(F("Stopping"));
  418.     MP3player.stopTrack();

  419.     // Then select a new song.
  420.     char song[50];
  421.     //randomSeed(analogRead(4));
  422.     selectSong("PLT.TXT", song, random(1, totalsongs));
  423.     Serial.println(song);

  424.     // Play the selected song.
  425.     MP3player.playMP3(song, 0);
  426.   }
  427.   else if((key_command == '-') || (key_command == '+')) {
  428.     union twobyte mp3_vol; // create key_command existing variable that can be both word and double byte of left and right.
  429.     mp3_vol.word = MP3player.getVolume(); // returns a double uint8_t of Left and Right packed into int16_t

  430.     if(key_command == '-') { // note dB is negative
  431.       // assume equal balance and use byte[1] for math
  432.       if(mp3_vol.byte[1] >= 254) { // range check
  433.         mp3_vol.byte[1] = 254;
  434.       }
  435.       else {
  436.         mp3_vol.byte[1] += 2; // keep it simpler with whole dB's
  437.       }
  438.     }
  439.     else {
  440.       if(mp3_vol.byte[1] <= 2) { // range check
  441.         mp3_vol.byte[1] = 2;
  442.       }
  443.       else {
  444.         mp3_vol.byte[1] -= 2;
  445.       }
  446.     }
  447.     // push byte[1] into both left and right assuming equal balance.
  448.     MP3player.setVolume(mp3_vol.byte[1], mp3_vol.byte[1]); // commit new volume
  449.     Serial.print(F("Volume changed to -"));
  450.     Serial.print(mp3_vol.byte[1]>>1, 1);
  451.     Serial.println(F("[dB]"));
  452.   }
  453.   else if(key_command == 'i') {
  454.     MP3player.getAudioInfo();

  455.   }
  456.   else if(key_command == 'p') {
  457.     if( MP3player.getState() == playback) {
  458.       MP3player.pauseMusic();
  459.       Serial.println(F("Pausing"));
  460.     }
  461.     else if( MP3player.getState() == paused_playback) {
  462.       MP3player.resumeMusic();
  463.       Serial.println(F("Resuming"));
  464.     }
  465.     else {
  466.       Serial.println(F("Not Playing!"));
  467.     }

  468.   }
  469.   else if(key_command == 'R') {
  470.     MP3player.stopTrack();
  471.     MP3player.vs_init();
  472.     Serial.println(F("Reseting VS10xx chip"));

  473.   }

  474.   else if(key_command == 'M') {
  475.     uint16_t monostate = MP3player.getMonoMode();
  476.     Serial.print(F("Mono Mode "));
  477.     if(monostate == 0) {
  478.       MP3player.setMonoMode(1);
  479.       Serial.println(F("Enabled."));
  480.     }
  481.     else {
  482.       MP3player.setMonoMode(0);
  483.       Serial.println(F("Disabled."));
  484.     }

  485.   }
  486.   else if(key_command == 'g') {
  487.     int32_t offset_ms = 20000; // Note this is just an example, try your own number.
  488.     Serial.print(F("jumping to "));
  489.     Serial.print(offset_ms, DEC);
  490.     Serial.println(F("[milliseconds]"));
  491.     result = MP3player.skipTo(offset_ms);
  492.     if(result != 0) {
  493.       Serial.print(F("Error code: "));
  494.       Serial.print(result);
  495.       Serial.println(F(" when trying to skip track"));
  496.     }

  497.   }
  498.   else if(key_command == 'k') {
  499.     int32_t offset_ms = -1000; // Note this is just an example, try your own number.
  500.     Serial.print(F("moving = "));
  501.     Serial.print(offset_ms, DEC);
  502.     Serial.println(F("[milliseconds]"));
  503.     result = MP3player.skip(offset_ms);
  504.     if(result != 0) {
  505.       Serial.print(F("Error code: "));
  506.       Serial.print(result);
  507.       Serial.println(F(" when trying to skip track"));
  508.     }

  509.   }
  510.   else if(key_command == 'O') {
  511.     MP3player.end();
  512.     Serial.println(F("VS10xx placed into low power reset mode."));

  513.   }
  514.   else if(key_command == 'o') {
  515.     MP3player.begin();
  516.     Serial.println(F("VS10xx restored from low power reset mode."));

  517.   }
  518.   else if(key_command == 'D') {
  519.     uint16_t diff_state = MP3player.getDifferentialOutput();
  520.     Serial.print(F("Differential Mode "));
  521.     if(diff_state == 0) {
  522.       MP3player.setDifferentialOutput(1);
  523.       Serial.println(F("Enabled."));
  524.     }
  525.     else {
  526.       MP3player.setDifferentialOutput(0);
  527.       Serial.println(F("Disabled."));
  528.     }

  529.   }

  530.   else if(key_command == 'h') {
  531.     help();
  532.   }

  533.   // print prompt after key stroke has been processed.
  534.   Serial.println(F("Enter n,+,-,i,p,r,R,M,g,k,h,O,o,D,S :"));
  535. }

  536. //------------------------------------------------------------------------------
  537. /**
  538. * \brief Print Help Menu.
  539. *
  540. * Prints a full menu of the commands available along with descriptions.
  541. */
  542. void help() {

  543.   Serial.println(F("Arduino SFEMP3Shield Library Example:"));
  544.   Serial.println(F(" courtesy of Bill Porter & Michael P. Flaga"));
  545.   Serial.println(F("COMMANDS:"));
  546.   Serial.println(F(" [n] to jump to next Song"));
  547.   Serial.println(F(" [+ or -] to change volume"));
  548.   Serial.println(F(" [i] retrieve current audio information (partial list)"));
  549.   Serial.println(F(" [p] to pause."));
  550.   Serial.println(F(" [r] resumes play from 2s from begin of file"));
  551.   Serial.println(F(" [R] Resets and initializes VS10xx chip."));
  552.   Serial.println(F(" [M] Toggle between Mono and Stereo Output."));
  553.   Serial.println(F(" [g] Skip to a predetermined offset of ms in current track."));
  554.   Serial.println(F(" [k] Skip a predetermined number of ms in current track."));
  555.   Serial.println(F(" [O} turns OFF the VS10xx into low power reset."));
  556.   Serial.println(F(" [o} turns ON the VS10xx out of low power reset."));
  557.   Serial.println(F(" [D] to toggle SM_DIFF between inphase and differential output"));
  558.   Serial.println(F(" [h] this help"));
  559. }



  560. void setup()
  561. {
  562.   bool updateflag = 0;       

  563.   delay(10);
  564.   Serial.begin(9600);

  565.   // See Free RAM       
  566.   PgmPrint("Free RAM: ");
  567.   Serial.println(FreeRam());  


  568.   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  569.   // breadboards.  use SPI_FULL_SPEED for better performance.
  570.   digitalWrite(9,HIGH);
  571.   delay(2000);
  572.   if (!sd.begin(4, SPI_HALF_SPEED)) sd.initErrorHalt();
  573.   if(!sd.chdir("/")) sd.errorHalt("sd.chdir");

  574.   //Initialize the MP3 Player Shield
  575.   uint8_t result = MP3player.begin();
  576.   //check result, see readme for error codes.
  577.   if(result != 0) {
  578.     Serial.print(F("Error code: "));
  579.     Serial.print(result);
  580.     Serial.println(F(" when trying to start MP3 player"));
  581.     if( result == 6 ) {
  582.       Serial.println(F("Warning: patch file not found, skipping.")); // can be removed for space, if needed.
  583.       Serial.println(F("Use the "d" command to verify SdCard can be read")); // can be removed for space, if needed.
  584.     }
  585.   }

  586.   // Start at beginning of the root directory.
  587.   // rewind is used for set current position to zero.
  588.   sd.vwd()->rewind();

  589.   // 1. If no playlist file exists, scan the whole SD to generate one
  590.   // 2. Else Check the timestamp of the playlist to see if update is needed
  591.   // 3. If update is needed, generate playlist again.
  592.   sd.vwd()->rewind();
  593.   if(!sd.exists("PL_OK.TXT"))                // Condition 1
  594.   {
  595.     Serial.println("No PL_OK.TXT found!");
  596.     updateflag = 1;
  597.   }
  598.   else                                        // Condition 2
  599.   {
  600.     Serial.println("PL_OK has been created, need to check its timestamp!");
  601.     // Set global Variables to 0 for avoiding potential mis-using
  602.     latestWriteTime = 0;
  603.     latestWriteDate = 0;
  604.     sd.vwd()->rewind();
  605.     getLatestStamp(sd.vwd(), 0);
  606.     // Get the PlayList File's timestamp, for comparing.
  607.     uint16_t ldate = 0;
  608.     uint16_t ltime = 0;
  609.     short rvalue = getfileStamp("PL_OK.TXT", &ldate,&ltime);
  610.     if( (ldate == latestWriteDate) && ( ltime == latestWriteTime) )
  611.     {
  612.       Serial.println("PlayList is the newest version, update not needed!");
  613.     }
  614.     else
  615.     {
  616.       Serial.println("Need Update PlayList!");
  617.       updateflag = 1;
  618.     }
  619.   }

  620.   sd.vwd()->rewind();
  621.   if(updateflag)                                // Condition 3, update playlist or not
  622.   {
  623.     // Call genOrigListFile() to fetch all of the MP3 and directory layer information
  624.     short error_reason = genOrigListFile("PL_OK.TXT", sd.vwd(), 0);
  625.     if(error_reason != 0)
  626.     {
  627.       Serial.print("genOrigListFile error number is ");
  628.       Serial.println(error_reason);
  629.     }


  630.     // Call genPlayList() to generate the actual PlayList file
  631.     error_reason = genPlayList("PL_OK.TXT", "PLT.TXT");
  632.     if(error_reason != 0)
  633.     {
  634.       Serial.print("genPlayList error number is ");
  635.       Serial.println(error_reason);
  636.     }
  637.     Serial.println("Generate OrigiList and PlayList Done!");
  638.   }
  639.   else
  640.   {
  641.     Serial.println("No Update!");
  642.   }


  643.   /* For saving space */
  644.   totalsongs = getListLength("PLT.TXT");

  645.   /* Generate random seed , a0 will be a random variable*/
  646.   randomSeed(analogRead(0));

  647.   Serial.println("Done setup()");
  648. }


  649. void loop() {
  650.   //Serial.println(MP3player.getState());
  651.   if((MP3player.getState() == initialized) | (MP3player.getState() == ready))
  652.   {
  653.     char song[50];
  654.     selectSong("PLT.TXT", song, random(1, totalsongs));
  655.     Serial.println(song);
  656.     MP3player.playMP3(song, 0);
  657.     Serial.println(F("Enter n,+,-,i,p,r,R,M,g,k,h,O,o,D,S :"));
  658.   }
  659.   else
  660.   {
  661.     if(Serial.available()) {
  662.       parse_menu(Serial.read()); // get command from serial input
  663.     }

  664.     delay(100);
  665.   }
  666. }
复制代码

   
回复

使用道具 举报

发表于 2013-12-26 18:59:56 | 显示全部楼层
本帖最后由 edmondhuang 于 2013-12-26 19:03 编辑

lz灰常厉害耶{:soso_e128:},百分百是个linux程序猿,鉴定完毕
回复 支持 反对

使用道具 举报

发表于 2013-12-27 13:32:13 | 显示全部楼层
偶然发现这句-〉 This allows the Arduino to perform other tasks while music is playing
回复 支持 反对

使用道具 举报

发表于 2014-12-19 14:51:25 | 显示全部楼层
赞一个,正在试着跟着你的做
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-17 04:07 , Processed in 0.040840 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表