STM32音乐播放器,文件查找的实现
使用FATFS只是完成了一个基本的文件读写,有时候我们需要扩展一些功能,比如MP3实验,需要上一曲下一曲的切换,扩展的代码如下
//显示目录下所有文件 u8 ShowFileList(u8* dirPath) { u8 *pname; //带路径的文件名,最终生成的文件名 char *fn; //文件名(不带目录名称) u16 fileNameLength = 0; //文件长度 u16 showPos = 0; //当前显示的坐标 FILINFO* fileinfo = malloc(sizeof(FILINFO));//文件信息 DIR* dir = malloc(sizeof(DIR)); #if _USE_LFN//使能长文件名 fileinfo->lfsize = _MAX_LFN * 2 + 1; fileinfo->lfname = malloc(fileinfo->lfsize); #endif pname = malloc(fileinfo->lfsize);//申请动态内存 //检测动态内存是否申请成功,任何一个失败都不能继续 if((pname == NULL)||(dir == NULL)||(fileinfo == NULL)||(fileinfo->lfname == NULL) ) { free(fileinfo); free(dir); free(fileinfo->lfname); free(pname); return 1;//失败 } sdCardFsResult = f_opendir(dir,(const TCHAR*)dirPath); //打开一个目录 if(sdCardFsResult == FR_OK) { while(1)//循环查找文件 { sdCardFsResult = f_readdir(dir, fileinfo); //读取目录下的一个文件 if (sdCardFsResult != FR_OK || fileinfo->fname[0] == 0) break; //错误了/到末尾了,退出 #if _USE_LFN //根据是否使用长文件名来选择一个文件 fn = *fileinfo->lfname ? fileinfo->lfname : fileinfo->fname; #else fn = fileinfo.fname; #endif strcpy((char*)pname,(const TCHAR*)dirPath); //复制路径(目录) strcat((char*)pname,(const char*)"\\"); //将文件名接在后面 strcat((char*)pname,(const char*)fn); //将文件名接在后面 fileNameLength = strlen((char*)pname); Show_Str(0,showPos,LCD_X_SIZE,LCD_Y_SIZE,pname,12,0,LCD_BLACK); showPos += 12*(((fileNameLength*6)/234)+1);//每次显示完成看还能加多少,最多就能显示一页 if(showPos >= 320)break;//超过最大长度,结束循环 } //关闭文件夹 f_closedir(dir); //结束的时候要释放内存 free(fileinfo); free(dir); free(fileinfo->lfname); free(pname); return 0; } else { free(fileinfo); free(dir); free(fileinfo->lfname); free(pname); return 1;//目录打开失败 } } //显示目录下所有文件,从指定的起始位置开始显示 //文件起始从1开始 u8 ShowFileListStart(u8* dirPath,u8 start) { u8 *pname; //带路径的文件名,最终生成的文件名 char *fn; //文件名(不带目录名称) u16 fileNameLength = 0; //文件长度 u8 count = 0; //遍历文件计数器 u16 showPos = 0; //当前显示的坐标 FILINFO* fileinfo = malloc(sizeof(FILINFO));//文件信息 DIR* dir = malloc(sizeof(DIR)); #if _USE_LFN//使能长文件名 fileinfo->lfsize = _MAX_LFN * 2 + 1; fileinfo->lfname = malloc(fileinfo->lfsize); #endif pname = malloc(fileinfo->lfsize);//申请动态内存 //检测动态内存是否申请成功,任何一个失败都不能继续 if((pname == NULL)||(dir == NULL)||(fileinfo == NULL)||(fileinfo->lfname == NULL) ) { free(fileinfo); free(dir); free(fileinfo->lfname); free(pname); return 1;//失败 } sdCardFsResult = f_opendir(dir,(const TCHAR*)dirPath); //打开一个目录 if(sdCardFsResult == FR_OK) { while(1)//循环查找文件 { sdCardFsResult = f_readdir(dir, fileinfo); //读取目录下的一个文件 if (sdCardFsResult != FR_OK || fileinfo->fname[0] == 0) break; //错误了/到末尾了,退出 #if _USE_LFN //根据是否使用长文件名来选择一个文件 fn = *fileinfo->lfname ? fileinfo->lfname : fileinfo->fname; #else fn = fileinfo.fname; #endif count++; if(count >= start) { strcpy((char*)pname,(const TCHAR*)dirPath); //复制路径(目录) strcat((char*)pname,(const char*)"\\"); //将文件名接在后面 strcat((char*)pname,(const char*)fn); //将文件名接在后面 fileNameLength = strlen((char*)pname); Show_Str(0,showPos,LCD_X_SIZE,LCD_Y_SIZE,pname,12,0,LCD_BLACK); showPos += 12*(((fileNameLength*6)/234)+1);//每次显示完成看还能加多少,最多就能显示一页 if(showPos >= 320)break;//超过最大长度,结束循环 } } //关闭文件夹 f_closedir(dir); //结束的时候要释放内存 free(fileinfo); free(dir); free(fileinfo->lfname); free(pname); return 0; } else { free(fileinfo); free(dir); free(fileinfo->lfname); free(pname); return 1;//目录打开失败 } } //从文件夹中获取特定文件的文件名(包含完整路径) //索引index从1开始 u8* GetFileNameFormDir(u8* dirPath,u8 index) { u8 *pname; //带路径的文件名,最终生成的文件名 char *fn; //文件名(不带目录名称) u8 count = 0; //遍历文件计数器 u16 showPos = 0; //当前显示的坐标 FILINFO* fileinfo = malloc(sizeof(FILINFO));//文件信息 DIR* dir = malloc(sizeof(DIR)); #if _USE_LFN//使能长文件名 fileinfo->lfsize = _MAX_LFN * 2 + 1; fileinfo->lfname = malloc(fileinfo->lfsize); #endif pname = malloc(fileinfo->lfsize);//申请动态内存 //检测动态内存是否申请成功,任何一个失败都不能继续 if((pname == NULL)||(dir == NULL)||(fileinfo == NULL)||(fileinfo->lfname == NULL) ) { free(fileinfo); free(dir); free(fileinfo->lfname); free(pname); return NULL;//失败 } sdCardFsResult = f_opendir(dir,(const TCHAR*)dirPath); //打开一个目录 if(sdCardFsResult == FR_OK) { while(1)//循环查找文件 { sdCardFsResult = f_readdir(dir, fileinfo); //读取目录下的一个文件 if (sdCardFsResult != FR_OK || fileinfo->fname[0] == 0) break; //错误了/到末尾了,退出 #if _USE_LFN //根据是否使用长文件名来选择一个文件 fn = *fileinfo->lfname ? fileinfo->lfname : fileinfo->fname; #else fn = fileinfo.fname; #endif count++; if(count == index) { strcpy((char*)pname,(const TCHAR*)dirPath); //复制路径(目录) strcat((char*)pname,(const char*)"\\"); //将文件名接在后面 strcat((char*)pname,(const char*)fn); //将文件名接在后面 //找到了,返回文件名,那就有一个指针没有释放,要注意后期释放 //关闭文件夹 f_closedir(dir); free(fileinfo); free(dir); free(fileinfo->lfname); return pname; } } //关闭文件夹 f_closedir(dir); //结束的时候要释放内存 free(fileinfo); free(dir); free(fileinfo->lfname); free(pname); return NULL; } else { free(fileinfo); free(dir); free(fileinfo->lfname); free(pname); return NULL;//目录打开失败 } } //返回文件数量 u16 GetFileCount(u8* dirPath) { u16 fileCount = 0; FILINFO* fileinfo = malloc(sizeof(FILINFO));//文件信息 DIR* dir = malloc(sizeof(DIR)); #if _USE_LFN//使能长文件名 fileinfo->lfsize = _MAX_LFN * 2 + 1; fileinfo->lfname = malloc(fileinfo->lfsize); #endif //检测动态内存是否申请成功,任何一个失败都不能继续 if((dir == NULL)||(fileinfo == NULL)) { free(fileinfo); free(dir); #if _USE_LFN//使能长文件名 free(fileinfo->lfname); #endif return 0;//失败 } sdCardFsResult = f_opendir(dir,(const TCHAR*)dirPath); //打开一个目录 if(sdCardFsResult == FR_OK) { while(1)//循环查找文件 { sdCardFsResult = f_readdir(dir, fileinfo); //读取目录下的一个文件 if (sdCardFsResult != FR_OK || fileinfo->fname[0] == 0) break; //错误了/到末尾了,退出 fileCount++; } //关闭文件夹 f_closedir(dir); //结束的时候要释放内存 free(fileinfo); free(dir); #if _USE_LFN//使能长文件名 free(fileinfo->lfname); #endif return fileCount; } else { free(fileinfo); free(dir); #if _USE_LFN//使能长文件名 free(fileinfo->lfname); #endif return 0;//目录打开失败 } }
有了这一套API就可以组合实现音乐播放器了如下
FIL* currentMusic; //当前播放音乐文件指针 u8* musicDataBuffer; //音乐文件缓存数组 u8 playState = 0; //当前音乐状态,为0标识暂停 为1标识正在播放 u16 sendMusicCount; //当前缓冲区已经发送的音乐文件计数 UINT readCount; //每次读取文件的字符数量 UINT fileReadLength; //文件已经读取的数据总量 u16 currentMusicIndex; //当前音乐文件在音乐文件夹中的位置 u16 musicCount; //当前文件夹中音乐文件的总数 //音乐初始化 //返回0成功 返回1失败 u8 Music_Init(void) { u8* name = NULL; FRESULT result; //默认初始化选中第一个文件,并且播放暂停 musicCount = GetFileCount("0:音乐");//初始化文件总数 if(musicCount == 0)return 1;//初始化失败 else { currentMusicIndex = 1;//当前处于第一首歌曲 name = GetFileNameFormDir("0:音乐",currentMusicIndex);//获取当前歌曲名 fileReadLength = 0; readCount = 0; sendMusicCount = 0; playState = 1; LCD_ShowString(0,250,LCD_X_SIZE,LCD_Y_SIZE,(u8*)" ",LCD_BLACK); LCD_ShowString(0,250,LCD_X_SIZE,LCD_Y_SIZE,(u8*)"MP3 STATUS: PALYING ",LCD_BLACK); LCD_ShowString(0,278,LCD_X_SIZE,LCD_Y_SIZE,(u8*)" ",LCD_BLACK); LCD_ShowNum(50,278,currentMusicIndex,3,LCD_BLACK); LCD_ShowChar(50+18,278,'/',0,LCD_BLACK); LCD_ShowNum(50+24,278,musicCount,3,LCD_BLACK); musicDataBuffer = NULL; currentMusic = malloc(sizeof(FIL)); //打开第一首歌曲 result = f_open(currentMusic,(TCHAR*)name,FA_READ); Show_Str(0,266,LCD_X_SIZE,LCD_Y_SIZE," ",12,0,LCD_BLACK); Show_Str(0,266,LCD_X_SIZE,LCD_Y_SIZE,name,12,0,LCD_BLACK); //打开之后要把名称缓存关闭 if(name != NULL) { free(name); } if(result == FR_OK) { VS_Soft_Reset(); musicDataBuffer = malloc(512);//申请512字节缓存 if(musicDataBuffer == NULL)return 1;//初始化失败 //打开成功 VS_SPI_SpeedHigh(); return 0; } else { //打开失败 return 1; } } } //播放当前音乐,正常循环 void Player_Current_Music(void) { u16 playtime=0;//播放时间 u16 time=0;// 总时间 u16 f_kbps=0;//歌曲文件位率 if(playState == 1) { //持续播放,首先要检查有没有文件打开 if(currentMusic == NULL) { return;//没有文件打开 } else { if(fileReadLength == currentMusic->fsize)//已经读取的长度为文件总长度 { //不在读取,发送完了事 if(sendMusicCount >= readCount) { //已经发送完了,不再发送数据 // free(musicDataBuffer);//释放缓存数组 //调用函数,自动切换下一首歌 Switch_Next_Music(); return; } else { //还需要发送 if(VS_Send_MusicData(musicDataBuffer+sendMusicCount)) { } else { //发送成功,数据增长 sendMusicCount+=32; } } } else { if((fileReadLength%(1024*10)) == 0) { //显示解码时间和当前播放时间以及位率 f_kbps=VS_Get_HeadInfo(); //获得比特率 playtime=VS_Get_DecodeTime(); //得到解码时间 if(f_kbps)time=(currentMusic->fsize/f_kbps)/125;//得到秒钟数 (文件长度(字节)/(1000/8)/比特率=持续秒钟数 if(f_kbps) { //显示当前播放时间 LCD_ShowNum(100,278,playtime/60,2,LCD_BLACK); //分钟 LCD_ShowChar(100+12,278,':',0,LCD_BLACK); LCD_ShowNum(100+18,278,playtime%60,2,LCD_BLACK); //秒钟 LCD_ShowChar(100+30,278,'/',0,LCD_BLACK); //显示总时间 LCD_ShowNum(100+36,278,time/60,2,LCD_BLACK); //分钟 LCD_ShowChar(100+48,278,':',0,LCD_BLACK); LCD_ShowNum(100+54,278,time%60,2,LCD_BLACK); //秒钟 //显示码率 LCD_ShowNum(100+78,278,f_kbps,3,LCD_BLACK); //显示位率 LCD_ShowString(100+96,278,LCD_X_SIZE,LCD_Y_SIZE,(u8*)"Kbps",LCD_BLACK); } } //还有读取机会 if(sendMusicCount < readCount) { //发送数据 if(VS_Send_MusicData(musicDataBuffer+sendMusicCount)) { //发送失败,等待下一次发送 } else { //发送成功,数据增长 sendMusicCount+=32; } } else { //读取下一次数据 f_read(currentMusic,musicDataBuffer,512,&readCount); fileReadLength += readCount; sendMusicCount = 0; if(readCount > 0) { //发送数据 if(VS_Send_MusicData(musicDataBuffer+sendMusicCount)) { //发送失败,等待下一次发送 } else { //发送成功,数据增长 sendMusicCount+=32; } } } } } } else { //音乐暂停,啥都不干 } } //切换下一个音乐,按键down键 void Switch_Next_Music(void) { u8* name = NULL; FRESULT result; //第一步是切歌曲 VS_Restart_Play(); //第二步,关闭之前的文件,释放之前使用的缓存 if(currentMusic != NULL) { f_close(currentMusic); //关闭文件 free(currentMusic); //释放文件缓存 } //将文件相关的变量都初始化 if(currentMusicIndex == musicCount)currentMusicIndex = 1;//到开头之后转到末尾 else currentMusicIndex++; fileReadLength = 0; readCount = 0; sendMusicCount = 0; if(musicDataBuffer != NULL)free(musicDataBuffer);//释放缓存 //第三步,重新打开新的文件 name = GetFileNameFormDir("0:音乐",currentMusicIndex);//获取当前歌曲名 currentMusic = malloc(sizeof(FIL)); //打开第一首歌曲 result = f_open(currentMusic,(TCHAR*)name,FA_READ); Show_Str(0,266,LCD_X_SIZE,LCD_Y_SIZE," ",12,0,LCD_BLACK); Show_Str(0,266,LCD_X_SIZE,LCD_Y_SIZE,name,12,0,LCD_BLACK); if(name != NULL) { free(name); } if(result == FR_OK) { musicDataBuffer = malloc(512);//申请512字节缓存 //读取第一段 f_read(currentMusic,musicDataBuffer,512,&readCount); fileReadLength+= readCount; } VS_Set_All(); //设置音量等信息 VS_Reset_DecodeTime(); //复位解码时间 VS_SPI_SpeedHigh(); //等待传输数据 LCD_ShowString(0,278,LCD_X_SIZE,LCD_Y_SIZE,(u8*)" ",LCD_BLACK); LCD_ShowNum(50,278,currentMusicIndex,3,LCD_BLACK); LCD_ShowChar(50+18,278,'/',0,LCD_BLACK); LCD_ShowNum(50+24,278,musicCount,3,LCD_BLACK); } //切换上一个音乐,按键up键 void Switch_Prev_Music(void) { u8* name = NULL; FRESULT result; //第一步是切歌曲 VS_Restart_Play(); //第二步,关闭之前的文件,释放之前使用的缓存 if(currentMusic != NULL) { f_close(currentMusic); //关闭文件 free(currentMusic); //释放文件缓存 } //将文件相关的变量都初始化 if(currentMusicIndex == 1)currentMusicIndex = musicCount;//到末尾之后转到开头 else currentMusicIndex--; fileReadLength = 0; readCount = 0; sendMusicCount = 0; if(musicDataBuffer != NULL)free(musicDataBuffer);//释放缓存 //第三步,重新打开新的文件 while(name == NULL) { name = GetFileNameFormDir("0:音乐",currentMusicIndex);//获取当前歌曲名 Delay_Ms(10); } currentMusic = malloc(sizeof(FIL)); //打开第一首歌曲 result = f_open(currentMusic,(TCHAR*)name,FA_READ); Show_Str(0,266,LCD_X_SIZE,LCD_Y_SIZE," ",12,0,LCD_BLACK); Show_Str(0,266,LCD_X_SIZE,LCD_Y_SIZE,name,12,0,LCD_BLACK); if(name != NULL) { free(name); } if(result == FR_OK) { musicDataBuffer = malloc(512);//申请512字节缓存 //读取第一段 f_read(currentMusic,musicDataBuffer,512,&readCount); fileReadLength+= readCount; } VS_Set_All(); //设置音量等信息 VS_Reset_DecodeTime(); //复位解码时间 VS_SPI_SpeedHigh(); //等待传输数据 LCD_ShowString(0,278,LCD_X_SIZE,LCD_Y_SIZE,(u8*)" ",LCD_BLACK); LCD_ShowNum(50,278,currentMusicIndex,3,LCD_BLACK); LCD_ShowChar(50+18,278,'/',0,LCD_BLACK); LCD_ShowNum(50+24,278,musicCount,3,LCD_BLACK); } //音乐暂停,按键left键 void Pause_Mucis(void) { playState = 0; LCD_ShowString(0,250,LCD_X_SIZE,LCD_Y_SIZE,(u8*)" ",LCD_BLACK); LCD_ShowString(0,250,LCD_X_SIZE,LCD_Y_SIZE,(u8*)"MP3 STATUS: PAUSE ",LCD_BLACK); } //音乐继续 void Continue_Music(void) { playState = 1; //加入是一首歌放完了自动进入的暂停状态就要重新启动这首歌 LCD_ShowString(0,250,LCD_X_SIZE,LCD_Y_SIZE,(u8*)" ",LCD_BLACK); LCD_ShowString(0,250,LCD_X_SIZE,LCD_Y_SIZE,(u8*)"MP3 STATUS: PLAYING ",LCD_BLACK); }
核心就是这两段代码,剩下的可以看工程代码,VS003的驱动以前写文章说过
http://download.csdn.net/detail/dengrengong/8542891