这是一款基于easyx图形库和C/C++编写的音乐播放器,参考了网上许多大佬友情分享的项目设计,结合自己的理解加以设计和制作,给有同样需求的朋友一点参考。文中放上了个人感觉很有帮助的文章,建议可以参考阅读。
基于windows和easyx的一款音乐播放器
mciSendString函数是这款音乐播放器能够实现功能的底层函数,其余所有的代码不过是在锦上添花和增加扩展功能,这个是一定要掌握的。(如果能力足够,使用QT这类框架功能更加强大)
easyx是项目所需的图形库,在界面设计上有多很强大的功能和方便的函数。如果想做图形界面可以参考使用。
学习了解mciSendString函数:
采取mciSendString这个windows上的API,有关参数详情请参见文档
快速访问==>WindowsAPImciSendString文档传送门
API设计参考xiaoxiao涛的博客-CSDN博客_c++播放音乐
项目结构参考Cybercalf/HwcPlayer: 四川大学2021年暑期实训项目-音乐播放器
有关链表的增删改查操作、文件读写操作、UI的设计、MFC库easyx库
基于Windows控制台C程序的音乐播放器其实是通过调用Windows上的一些API实现的,那就只需要通过函数对mciSendString传输字符串参数为命令控制Windows程序播放MP3文件。
基于这个想法,可以对音乐播放器的各个功能模块在原生API的基础上再加工,封装成一个个小demo,然后再做一个简陋的界面去操控。
那么一切合理了起来:也就是说相当于把各种本来要手打的命令行变成按钮或者键盘输入字符,按一下就往这里面输出一些命令,也就能实现音乐播放器了。但是
(显然)由题可知,我们可以使用链表作为这种数据结构(数据量不大),方便完成各种操作。那链表存储可以实现了,文件又怎么读取呢?
那当然是采用直接呼出文件资源管理器的方式去读取文件的路径,这个的实现我们之后再说,这样也更加友好和人性化,有图形界面的操作手感
所以,需求里难弄的的东西来了。图形界面,用C做图形界面,完全陌生的领域。摆在面前的有几种选择:
这些都是在C环境下做UI的好点子。但是MFC太拉了,UI奇丑,代码可读性差的离谱。人家微软自己都放弃了我还用吗(其实是太难不会)。QT可以说是功能强大齐全,但是学习成本高,或者说一个小播放器还不至于用QT,有点大材小用。WPF是基于C#的。
那么综上所述,我们组采取Easyx.h图形库方案,UI这种东西全靠审美。审美上去了拿什么做都好看,也就是说只要你会PS,界面这种东西不成问题,靠队友的时候到了。
那么这时根据需求,我们所有的技术选择都已经齐全,可以开始着手写代码了。
一些基础的知识不再赘述,以下是一些踩过的坑(本项目),具体代码在文末链接中可以自行下载
MCIERROR mciSendString(
LPCTSTR lpszCommand, //MCI命令字符串
LPTSTR lpszReturnString, //存放反馈信息的缓冲区
UINT cchReturn, //缓冲区的长度
HANDLE hwndCallback //回调窗口的句柄,一般为NULL
); //若成功则返回0,否则返回错误码。
命令 | 解释 |
---|---|
open | 打开设备 |
close | 关闭设备 |
play | 开始设备播放 |
stop | 停止设备的播放或记录 |
record | 开始记录 |
save | 保存设备内容 |
pause | 暂停设备的播放或记录 |
resume | 恢复暂停播放或记录的设备 |
seek | 改变媒体的当前位置 |
status | 查询设备状态信息 |
capacility | 查询设备能力 |
info | 查询设备的信息 |
strcpy: char *strcpy(char* dest, const char *src);
//把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间
//src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串
//返回指向dest的 指针。
strcat: extern char *strcat(char *dest, const char *src);
//把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')
void add()
{
TCHAR szBuffer[MAX_PATH] = { 0 };
OPENFILENAME file = { 0 };
file.hwndOwner = NULL;
file.lStructSize = sizeof(file);
file.lpstrFilter = "*文件(*.*)\0*.*\0";//要选择的文件后缀
file.lpstrFile = szBuffer;//存放文件的缓冲区
file.nMaxFile = sizeof(szBuffer) / sizeof(*szBuffer);
file.nFilterIndex = 0;
file.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER;//标志如果是多选要加上OFN_ALLOWMULTISELECT
BOOL bSel = GetOpenFileName(&file);
Media* music = createMedia(file.lpstrFile);//创建的链表
if (music != NULL) {
headInsert(list, *music);
}
}
命令 | 解释 |
---|---|
“status movie position” | 播放位置 |
“status movie length” | 播放总长度 |
“status movie mode” | 播放状态 |
"seek movie to " | 指定位置 |
“seek movie to start” | 定位到开头位置 |
“seek movie to end” | 定位到最后位置 |
为长命令设置别名更有助于写代码
由于对MCI的操作实际上就是命令操作,所以可以通过设置别名来进行操作mcisendstring函数
@doskey ls=dir /b $*
@doskey l=dir /od/p/q/tw $*
//命令 alias 别名
@REM notepad++工具设置别名为:npp
@doskey npp="C:\Program Files1\Notepad++\notepad++.exe" $
//消除cls频闪函数
void cls() {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
COORD coordScreen = { 0, 0 }; // home for the cursor
SetConsoleCursorPosition(hConsole, coordScreen);
}
int openMusic(const char* url)
{
if (url != NULL)
{
char cmd[500] = "";
strcpy_s(cmd, "open \"");//引号需要被转义
strcat_s(cmd, url);
strcat_s(cmd, "\"");//引号需要被转义
strcat_s(cmd, " alias MyMusic");
if (mciSendStringUtil(cmd, NULL) == 0)
{
return 0;
}
}
}
void add() {
TCHAR szBuffer[MAX_PATH] = { 0 };
OPENFILENAME file = { 0 };
file.hwndOwner = NULL;
file.lStructSize = sizeof(file);
file.lpstrFilter = "*文件(*.*)\0*.*\0";//要选择的文件后缀
file.lpstrFile = szBuffer;//存放文件的缓冲区
file.nMaxFile = sizeof(szBuffer) / sizeof(*szBuffer);
file.nFilterIndex = 0;
file.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER;//标志如果是多选要加上OFN_ALLOWMULTISELECT
BOOL bSel = GetOpenFileName(&file);
Media* music = createMedia(file.lpstrFile);
if (music != NULL) {
headInsert(list, *music);
}
}
HWND hwnd = initgraph(720, 960, 0);
//或者屏幕刷新等类似函数
loadimage(&play2, ".\\*.png", 81, 81);
loadimage(&pause, ".\\*.png", 81, 81);
loadimage(&play2, "D:\\visual studio project\\Project_List\\Project_List\\picture\\*.png", 81, 81);
loadimage(&pause, "D:\\visual studio project\\Project_List\\Project_List\\picture\\*.png", 81, 81);
void transimg(IMAGE* dstimg, int x, int y, IMAGE* srcimg)
{
HDC dstDC = GetImageHDC(dstimg);
HDC srcDC = GetImageHDC(srcimg);
int w = srcimg->getwidth();
int h = srcimg->getheight();
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
//半透明位图
AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}
transimg(NULL, 334, 652, &IMAGE);//一种颜色的按钮
Sleep(100);
transimg(NULL, 334, 652, &IMAGE);//另一种颜色的按钮
//进度条
while(1)
{
t = getMusicLength();
t1 = getMusicPosition();
if (t != 0)
{
c = t1 / t;
}
progress_length = c * 523 + 90;
fillroundrect(90, 830, progress_length, 825, 10, 10);
}