音乐播放器之源码解析四

来源:互联网 发布:简单的数据分析小工具 编辑:程序博客网 时间:2024/06/06 00:45

音乐播放器播放列表实现解析

playlist.h头文件

#ifndef PLAYLIST_H#define PLAYLIST_Hint PlayListInit();int PlayListDestroy();char *GetItemFromDefaultPlaylist(int pos);int GetDefaultPlaylistTotalItem();int DefaultPlaylistAddItem(char *itempath);int DefaultPlaylistDeleteItem(int pos);int DefaultPlaylistSave();#endif

默认播放列表为default.m3u,位于可执行文件相同目录,歌曲条目用单链表存储,默认播放列表为g_playlisthead头及指向末尾项的g_playlisttail两个指针控制。

#include <windows.h>#include <stdio.h>#include <string.h>#define DEFAULT_PLAYLIST "default.m3u"typedef struct listNode listNode;struct listNode{char val[MAX_PATH];listNode *next;};listNode *g_playlisthead = NULL;listNode *g_playlisttail = NULL;int bChange = 0;int g_playlistitemcount = 0;char g_defaultlistpath[MAX_PATH] = {0};


PlayListInit函数

创建播放列表

int PlayListInit(){return MakePlayList();}int MakePlayList(){FILE *fp;char defaultlistpath[MAX_PATH] = {0};char *pplpath;char buf[MAX_PATH] = {0};GetModuleFileName(NULL, defaultlistpath, MAX_PATH);pplpath = strrchr(defaultlistpath, '\\');pplpath[1] = '\0';strcpy(g_defaultlistpath, defaultlistpath);sprintf(defaultlistpath, "%s%s", defaultlistpath, DEFAULT_PLAYLIST);fp = fopen(defaultlistpath, "r");if (fp == NULL){printf("open %s error\n", DEFAULT_PLAYLIST);return -1;}do {char *p;listNode *lnode;p = fgets(buf, MAX_PATH, fp);if (p == NULL){break;}g_playlistitemcount++;lnode = malloc(sizeof(listNode));memset(lnode->val, 0, MAX_PATH);strncpy(lnode->val, buf, strlen(buf) - 1); //不读取\nlnode->next = NULL;if (g_playlisthead ==  NULL){g_playlisthead = lnode;g_playlisttail = g_playlisthead;}else{g_playlisttail->next = lnode;g_playlisttail = lnode;}} while (1);return 0;}


打开default.m3u文件并循环获取每一行,每一行表示一首歌曲,分别放入播放链表g_playlisthead末尾。

PlayListDestroy函数

销毁默认播放列表

int PlayListDestroy(){return DestroyDefaultPlayList();}int DestroyDefaultPlayList(){listNode *lnode;listNode *lnodenext;if (g_playlisthead == NULL){return 0;}lnode = g_playlisthead;while (lnode){lnodenext = lnode->next;free(lnode);lnode = lnodenext;}return 0;}


释放播放链表每一项内容。

GetItemFromDefaultPlaylist函数

通过pos获取歌曲路径,pos0开始表示第一首歌

char *GetItemFromDefaultPlaylist(int pos){int i = 0;listNode *lnode;if (g_playlisthead == NULL){return NULL;}if (pos >= g_playlistitemcount){return NULL;}lnode = g_playlisthead;while (i < pos && lnode != NULL){i++;lnode = lnode->next;}if (lnode == NULL){return NULL;}return lnode->val;}


GetDefaultPlaylistTotalItem函数

获取默认播放列表所有条目个数,g_playlistitemcount记录条目总数,在添加歌曲时递增,删除歌曲时递减。

int GetDefaultPlaylistTotalItem(){return g_playlistitemcount;}


DefaultPlaylistAddItem函数

添加歌曲,传入歌曲的绝对路径

// 添加一项,itempath绝对路径int DefaultPlaylistAddItem(char *itempath){WIN32_FIND_DATA finddata;HANDLE handle;handle = FindFirstFile(itempath, &finddata);if (INVALID_HANDLE_VALUE == handle){return -1;}if (finddata.cFileName){listNode *lnode;lnode = malloc(sizeof(listNode));memset(lnode->val, 0, MAX_PATH);strcpy(lnode->val, itempath);lnode->next = NULL;if (g_playlisthead ==  NULL){g_playlisthead = lnode;g_playlisttail = g_playlisthead;}else{g_playlisttail->next = lnode;g_playlisttail = lnode;}g_playlistitemcount++;bChange++;}return 0;}


 

DefaultPlaylistDeleteItem函数

通过pos位置删除歌曲,pos0开始表示第一项

// 删除一项,pos从0开始int DefaultPlaylistDeleteItem(int pos){listNode *lnode;listNode *lnodeprev;int i = 0;if (pos >= g_playlistitemcount || g_playlisthead == NULL){return 0;}bChange++;// 只有一项if (g_playlistitemcount == 1){free(g_playlisthead);g_playlisthead = NULL;g_playlisttail = NULL;g_playlistitemcount--;return 0;}// 删除第一项if (pos == 0){lnode = g_playlisthead;g_playlisthead = lnode->next;free(lnode);lnode = NULL;return 0;}// 删除最后一项if (pos == g_playlistitemcount -1){lnode = g_playlisthead;while (lnode->next->next){lnode = lnode->next;}g_playlisttail = lnode;lnode = lnode->next;g_playlisttail->next = NULL;free(lnode);lnode = NULL;g_playlistitemcount--;return 0;}// 删除中间一项lnodeprev = g_playlisthead;while (i < pos - 1){lnodeprev = lnodeprev->next;i++;}lnode = lnodeprev->next;free(lnode);lnode = NULL;lnodeprev->next = NULL;g_playlistitemcount--;return 0;}


 

DefaultPlaylistSave函数

保存修改过的默认播放列表

int DefaultPlaylistSave(){FILE *fp;listNode *lnode;char plpath[MAX_PATH] = {0};if (bChange == 0 || g_playlistitemcount == 0 || g_playlisthead == NULL){return 0;}sprintf(plpath, "%s%s", g_defaultlistpath, DEFAULT_PLAYLIST);fp = fopen(plpath, "w+");if (fp == NULL){printf("open %s error\n", DEFAULT_PLAYLIST);return -1;}lnode = g_playlisthead;while (lnode){fputs(lnode->val, fp);//fwrite(lnode->val, 1, strlen(lnode->val), fp);fputs("\n", fp);lnode = lnode->next;}fflush(fp);fclose(fp);return 0;}

 

至此播放列表的实现已经完毕,其中包括初始化播放链表,通过歌曲绝对路径添加歌曲,删除歌曲及保存播放列表功能。接下来实现为显示播放时间进度及歌词。

 

BeginTimeEvent函数

创建定时器线程,该线程TimeEvenProc中包含显示播放时间进度、一首歌结束后默认播放下一曲、同步显示歌词功能。

void TimeEventProc(UINT wTimerID, UINT msg,DWORD dwUser,DWORD dwl,DWORD dw2){long long curpos = 0;long long stoppos = 0;int curtime = 0;int stoptime = 0;char *p = NULL;char *szLryic = NULL;char szTime[1024] = {0};double rate = 0.0;mpxGetPositions(&curpos, &stoppos);if (curpos == stoppos){// 播放下一曲WCHAR songpath[MAX_PATH] = {0};char lyricpath[MAX_PATH] = {0};char *p;playIndex++;if (playIndex > GetDefaultPlaylistTotalItem()){playIndex = 0;}p = GetItemFromDefaultPlaylist(playIndex);if (p == NULL){return;}MakeLyricPathFromSongPath(p, lyricpath);LyricDestroy();LyricInit(lyricpath);MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, p, strlen(p), songpath, MAX_PATH);mpxPlayFile(songpath);status = C_BTN_PLAY;SetWindowText(btnPlay, "pause");SetWindowText(g_hWnd, p);}curtime = (int)(curpos / (double)10000000);stoptime = (int)(stoppos / (double)10000000);sprintf(szTime, "%02d:%02d->%02d:%02d", curtime/60, curtime%60, stoptime/60, stoptime%60);SendMessage(timeStatic, WM_SETTEXT, 0, szTime);szLryic = GetLyricByStartTime(curtime);if (szLryic != NULL){SendMessage(lyricStatic, WM_SETTEXT, 0, szLryic);}}void BeginTimeEvent(){if((g_timeId = timeSetEvent(500, 1,(LPTIMECALLBACK)TimeEventProc,(DWORD_PTR)0,TIME_PERIODIC)) == 0){printf("time set event error!\n");}}


mpxGetPositions获取播放的当前位置和停止位置,当播放结束后有可能获取不到值,此时两个变量的值为初始值0,此时也表示播放已结束,此时playIndex递增并表示播放下一曲。

curposstoppos获取到的值需要除以10000000的结果才表示秒,然后调用SendMessage(timeStatic, WM_SETTEXT, 0, szTime)将当前播放的时间和结束时间显示在timeStatic组件上表示播放歌曲的时间进度。

通过curtime单位为1/100秒,传入GetLyricByStartTime(curtime)函数获取当前时间对应的歌词,找的对应的歌词则将歌词显示在lyricStatic组件上,实现同步显示歌词功能。

EndTimeEvent函数

销毁定时器

void EndTimeEvent(){if (g_timeId != 0){timeKillEvent(g_timeId);g_timeId = 0;}}


下一篇音乐播放器同步歌词源码解析

原创粉丝点击