上一章中,主要是介绍了WINDOWS消息机制和模板对话框的创建,其实学到现在感觉windows程序设计就是各种逻辑+API函数。所以MSDN很重要。由于整个代码太大了,所以下面系列文章中,我只讲思路以及一些控件的用法。源代码以后会上传。请大家多多支持...
下面先上个 最后的效果图..
这节主要总结下最核心的播放功能,如何实现的。只提供关键思路。
核心思路:
1.按“添加”按钮,将添加歌曲进入Listbox。具有数字标号+歌曲名字
这里包括文件添加的操作,listBox的插入操作,字符串函数的操作
2.双击Listbox中某个歌曲将可以播放,并且可以有时间进度条和总时间
这里包括 listbox响应双击事件,mcisendCommand和关联结构体操作
1.添加文件的操作
在开始播放前,肯定要手动添加哪些要加入到播放列表中,就是上面按钮"添加"触发的操作。这里涉及文件的打开以及多个文件同时选中的情况。关键代码如下:
void DoSelectFile(HWND hwnd) //选择文件
{
OPENFILENAME ofn; //定义一个文件结构体
TCHAR szOpenFileNames[80*MAX_PATH]; //打开的文件名字
TCHAR szPath[MAX_PATH]; //歌曲文件的路径 其实操作一个Mp3文件就是通过路径来操作的
TCHAR szFileName[80*MAX_PATH];
ZeroMemory(szOpenFileNames,sizeof(szOpenFileNames)/sizeof(TCHAR));
ZeroMemory(&ofn,sizeof(ofn));
ZeroMemory(szFileName,sizeof(szFileName));
ZeroMemory(szPath,sizeof(szPath)); //清空 各类结构
//配置ofn各字段
ofn.Flags=OFN_EXPLORER|OFN_ALLOWMULTISELECT;//指定浏览功能,和允许多选功能
ofn.hwndOwner=hwnd; //父句柄为当前对话框
ofn.lStructSize=sizeof(ofn);//整个ofn结构体的大小 (这里以前犯了个错误&ofn)
ofn.lpstrFile=szOpenFileNames;//打开的文件的全路径
ofn.nMaxFile=sizeof(szOpenFileNames);//文件大小的最大值
ofn.lpstrFile[0]='\0';//指定第一个为NULL字符
ofn.lpstrFilter=TEXT("*.mp3\0*.mp3\0*.wav\0*.wav\0All files\0*.*\0");//文件过滤器
if(GetOpenFileName(&ofn)) //成功返回非零 不成功为0
{
TCHAR withnum_song[255];//带数字列表的 插入到listbox的歌曲名
ZeroMemory(withnum_song,sizeof(withnum_song)/sizeof(TCHAR));
lstrcpyn(szPath,szOpenFileNames,ofn.nFileOffset);//获取该多个文件所在的目录 单个文件不需要加‘\’,多个文件少个\
if(szPath[lstrlen(szPath)-1]!='\\')
{
lstrcat(szPath,TEXT("\\")); //这里又要犯错误了,lstrcat是连接两个字符串,\在字符串中必须是"\\"
}
// MessageBox(hwnd,szPath,TEXT("szPath2"),MB_OK);
TCHAR *p=szOpenFileNames+ofn.nFileOffset;//get the first file name
while(*p)
{
lstrcpy(fileinfo[isong].songname,p); // 保存歌曲名 歌曲复制到 全局文件信息库
lstrcpy(szFileName,szPath);
lstrcat(szFileName,p);
lstrcpy(fileinfo[isong].szfiledir,szFileName); //保存歌曲完整路径
TCHAR puresn[255]; //纯的songname=sn
ZeroMemory(puresn,sizeof(puresn)/sizeof(TCHAR));
TCHAR *pos=strrchr(p,'.');//从后往前查找'.' .mp3的.
lstrcpyn(puresn,p,pos-p+1);
ZeroMemory(withnum_song,sizeof(withnum_song)/sizeof(TCHAR));
wsprintf(withnum_song,TEXT("%02d %s"),isong+1,puresn);
ListBox_InsertString(hlistbox,-1,withnum_song);
p=p+lstrlen(p)+1; //移动到下一个文件名
ZeroMemory(szFileName,sizeof(szFileName));
isong++; //歌曲自加1
}
}
}
上面就是文件的操作了,包括歌曲的添加和 往ListBox中插入歌曲名字。其实全部都是文件路径的操作,我们要得到歌曲的路径,存于全局变量中 我这里是存到一个全局结构体中,用来存放歌曲名字和路径 。
typedef struct //defined a information struct for song
{
TCHAR songname[50]; //注意这个值过小 可能导致 输入的信息有误,要保证有绝对空间 查了半天的错
TCHAR szfiledir[100];
}FileInfo;
FileInfo fileinfo[255]; //文件信息 只能存255首歌曲
歌曲的名字,我们希望有标号,并且希望歌曲名字不带.mp3的字样,这里就是截取的方法,利用字符串函数的操作,来获取我们想要的文件名字。当我们同时选取了多个文件时,实际上在内存中存在的形式就是该文件的目录+各个文件名字,每个文件名字以一个'\0'隔开。在调用完(GetOpenFileName(&ofn)这个函数成功后,会将你选择的文件名全部存于该 ofn.lpstrFile=szOpenFileNames.也就是存于szOpenFileNames的数组中。
所以剩下要做两件事: 1.获取每首歌曲完整路径 2.拼写出每个歌曲的名字
将获取的名字 格式化 加上数字,就插入到Listbox中即可。具体的参见工程中的代码。
2.播放音乐
在上面我们大概了解了这么添加文件和修改文件名,剩下的就是要播放了,其实要播放一首歌曲很简单,只需要条用mciSendCommand().函数即可,比如下面代码就可以播放一首歌曲
mciSendCommand(DeviceId,MCI_PLAY,MCI_FROM|MCI_NOTIFY,(DWORD)(LPVOID)&mciplay);
参数1:是要播放的设备号 参数2:是要播放的命令,参数3:是从哪里播放和播放完发送给主窗口一个通知
参数4:是结构体。每个mcisendcommand 都要关联一个结构体,这要关联播放mciplay
MCIDEVICEID DeviceId=0; //打开的设备ID号
MCI_OPEN_PARMS mciopen; //打开结构体
MCI_PLAY_PARMS mciplay; //播放结构体
MCI_SET_PARMS mciset; //设置时间格式
MCI_SEEK_PARMS mciseek; // 设置跳转到
MCI_STATUS_PARMS mcistatus; //播放状态 获得歌曲总长度
具体的意思 和怎么使用查找 MSDN手册。
思路:就是先配置 mciopen。指定打开的路径(就是音乐文件的路径)成功打开后,得到一个设备号,然后用该设备号来操作,比如播放,暂停,关闭..附加的功能 比如获取 歌曲状态,得到歌曲的总时间。
下面代码演示了怎么打开设备和获取歌曲总时间长度
BOOL DoPlay(HWND hwnd,INT index)
{
mciopen.lpstrElementName=fileinfo[index].szfiledir; //给打开设备指定路径
mciSendCommand(DeviceId,MCI_STOP,0,0);
mciSendCommand(DeviceId,MCI_CLOSE,0,0); //先关闭全部变量的设备
if(0==mciSendCommand(NULL,MCI_OPEN,MCI_OPEN_ELEMENT,(DWORD)(LPVOID)&mciopen)) //成功返回为0
{
// MessageBox(hwnd,TEXT("MCIOPEN打开设备成功"),TEXT(""),MB_OK);
DeviceId=mciopen.wDeviceID;//成功打开后将ID号保留下来。
mciset.dwTimeFormat=MCI_FORMAT_MILLISECONDS; //config mciset time format
mciSendCommand(DeviceId,MCI_SET,MCI_WAIT|MCI_FORMAT_MILLISECONDS,(DWORD)(LPMCI_SET_PARMS)&mciset);//设置歌曲的长度以Ms度量
mcistatus.dwItem=MCI_STATUS_LENGTH;
mciSendCommand(DeviceId,MCI_STATUS,MCI_WAIT|MCI_STATUS_ITEM,(DWORD)(LPMCI_STATUS_PARMS)&mcistatus);
DWORD dwsonglen=mcistatus.dwReturn/1000; //返回歌曲的总长度 ms级别的 dwsonglen单位是秒
SendDlgItemMessage(hwnd,IDC_SLIDER_SONG,TBM_SETRANGE,TRUE,MAKELPARAM(0,dwsonglen));//设置Silder的两端范围
mciplay.dwFrom=0;//从0开始播放
mciplay.dwCallback=(DWORD)hwnd;//播放完毕后,通该窗口处理函数
mciSendCommand(DeviceId,MCI_PLAY,MCI_FROM|MCI_NOTIFY,(DWORD)(LPVOID)&mciplay);//开始播放
ListBox_SetCurSel(hlistbox,iIndex); //设置当前选中宏
return TRUE;
}
else
MessageBox(hwnd,TEXT("打开歌曲错误"),TEXT("errror!"),MB_OK);
return FALSE;
}
下个模块继续...