• 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
  • 相关阅读:
    Python之文件操作
    document.hasFocus() & $(window).blur()
    innerHtml 会忽略里面元素的属性
    ng  命令集合
    阿里云ECS CentOs7.3下搭建LAMP环境(Apache2.4 + Mysql5.7 + PHP5.6 + Laravel5.2)
    在忘记root密码的时候,可以这样 亲测可用
    下一次装mysql 试一下这个方法
    CentOS-6.8安装Mysql-5.5.29
    阿里云服务器下安装LAMP环境(CentOS Linux 6.3)
    CentOS 7.2 配置Apache服务(httpd)--上篇
  • 原文地址:https://www.cnblogs.com/dengxiaojun/p/4374995.html
Copyright © 2020-2023  润新知