×
单片机 > 单片机程序设计 > 详情

STM32音乐播放器,文件查找的实现

发布时间:2020-06-06 发布时间:
|

使用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的驱动以前写文章说过



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
AVR熔丝位操作时的要点和需要注意的相关事项