其实看着还是有那么回事的。。恩,其实除了波形和在线下载的话,功能和酷狗,以及千千静听等播放器的功能是一样的。
音乐播放器的关键是播放音乐,所以第一步是要设计一个类,这个类拥有各种各样的播放音乐的能力。
我设计的类名字叫Player
class Player
{
public:
Player();
virtual ~Player();
void Play();//播放函数
void Pause();//暂停函数
void Stop();//停止函数
void Jump(DWORD pos);//这个函数是用来通过直接拖动进度条,然后播放从歌曲的相应位置播放的函数
void Read(CString str);//这个函数必须在所有函数的前面调用,他有一个传入参数,是指MP3文件的路径
void SetVolume(DWORD vol);//音量大小的设定。这个不难
MCI_STATUS_PARMS GetMode();//这个比较关键,是获得当前的播放状态(一首歌是否播放完了就是靠他)
MCI_STATUS_PARMS GetNow();//当前是播放的歌曲第几分第几秒?可以用来改变时间栏和进度条
MCI_STATUS_PARMS GetLenth();//获得歌曲的长度。(时间)
private:
MCI_PLAY_PARMS m_play;
MCI_OPEN_PARMS m_open;
MCIDEVICEID m_dID;
HWND m_parent;
MCI_STATUS_PARMS m_status;
MCI_SEEK_PARMS m_seek;
MCI_STATUS_PARMS m_mode;
};
各个函数(可以作为以后MCI函数的查阅手册)
实现方法是:
void Player::Play()
{
m_play.dwCallback=(DWORD)m_parent;
mciSendCommand(m_dID,MCI_PLAY,MCI_NOTIFY,(DWORD)(LPVOID)&m_play);//播放.....下面就不写注释了
}
void Player::Pause()
{
mciSendCommand(m_dID,MCI_PAUSE,NULL,NULL);
}
void Player::Stop()
{
mciSendCommand(m_dID,MCI_CLOSE,NULL,NULL);
m_status.dwReturn=0;
}
MCI_STATUS_PARMS Player::GetLenth()
{
m_status.dwItem=MCI_STATUS_LENGTH;
mciSendCommand(m_dID,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&m_status);
return m_status;
}
MCI_STATUS_PARMS Player::GetNow()
{
m_status.dwItem=MCI_STATUS_POSITION;
mciSendCommand(m_dID,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&m_status);
return m_status;
}
MCI_STATUS_PARMS Player::GetMode()
{
m_mode.dwItem=MCI_STATUS_MODE;
mciSendCommand(m_dID,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&m_mode);
return m_mode;
}
void Player::Jump(DWORD pos)
{
m_seek.dwTo=pos;
mciSendCommand(m_dID,MCI_SEEK,MCI_TO|MCI_WAIT,(DWORD)(LPVOID)&m_seek);
mciSendCommand(m_dID,MCI_PLAY,MCI_NOTIFY,(DWORD)(LPVOID)&m_play);
}
void Player::SetVolume(DWORD vol)
{
MCI_DGV_SETAUDIO_PARMS mcivol;
mcivol.dwCallback=(DWORD)m_parent;
mcivol.dwItem=MCI_DGV_SETAUDIO_VOLUME;
mcivol.dwValue=vol;
mciSendCommand(m_dID,MCI_SETAUDIO,MCI_DGV_SETAUDIO_VALUE|MCI_DGV_SETAUDIO_ITEM,(DWORD)(LPVOID)&mcivol);
}
基本上都是运用mciSendCommand这个函数,传入不同的参数,就可以达到不同的效果。
Player类设计完成后,在Dlg这个类当中去产生一个对象:MP3
这样就可以通过调用MP3的各个成员来播放声音了。
-------------------------------------------以上的大部分技术在大一的时候就有所了解----------------------------------------
后面的文件操作,都是以前不知道的了
首先我是在对话框上画了个列表控件。当然了,所有的歌曲数据都是写在它上面的,最最重要的就是列表的第四栏,文件地址
只要有文件地址,前三项都可以计算出来,但是为了优化电脑运算速度,可以事先把这些东西写在文件里面,这样显示的时候就不用算了
注意看:对话框初始化工作
BOOL CMusicDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_list.InsertColumn(0,"序列",LVCFMT_LEFT,40);//这里就要记住列表控件是如何让头顶上的字按要求划分的
m_list.InsertColumn(1,"歌曲名",LVCFMT_LEFT,200);
m_list.InsertColumn(2,"时长",LVCFMT_LEFT,50);
m_list.InsertColumn(3,"歌曲路径",LVCFMT_LEFT,172);
m_songsum=0;
m_vol.SetRangeMax(1000);//滑块控件最大值设定(这里是音量最大设置)
m_vol.SetPos(500);//初始化把音量设置为500,注意这里并没有控制音量,而只是把滑块放到中间而已
//----------------------------------------------
m_nid.hWnd=this->m_hWnd;//这下面的都是我以前不知道的,功能是让程序在后台运行。
m_nid.uID=IDR_MAINFRAME;//其实我也看不大懂每一句的意思,只能了解个大概,把他们当做工具吧
m_nid.cbSize=(DWORD)sizeof(NOTIFYICONDATA);
m_nid.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP;
m_nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
m_nid.uCallbackMessage=WM_MUSICHIDE;
m_mode=3;//默认的播放模式是全部歌曲循环播放
CFile file("musiclist.txt",CFile::modeRead);//这里是打开文件列表,所以即使播放器关掉了,再次打开,还是有上次加载的歌曲,不用再次 //加载了。
char *ch;
CString tempstr="";
CString ss;
DWORD len;
int i,r;
len=file.GetLength();//这后面的都是文件操作了,主要是把歌曲的路径以及其他信息读到列表空间上面去
ch=new char[len+1];
ch[len]=0;
file.Read(ch,len);
file.Close();
for(i=0;i<(int)len;i++)
{
if(ch[i]=='!')
{
ss.Format("%03d",m_songsum);
r=m_list.InsertItem(m_songsum,ss);
m_songsum++;
m_list.SetItemText(r,1,tempstr);
tempstr="";
continue;
}
if(ch[i]=='@')
{
m_list.SetItemText(r,2,tempstr);
tempstr="";
continue;
}
if(ch[i]=='\n')
{
m_list.SetItemText(r,3,tempstr);
tempstr="";
continue;
}
tempstr+=ch[i];
}
return TRUE; // return TRUE unless you set the focus to a control
}
发一张隐藏后的图:
其实效果还是和那些播放器很像的。。~
接下来收录一个文件夹导入的函数(太多要记忆的。。)
void CMusicDlg::OnBtnOpenbrose()
{
// TODO: Add your control notification handler code here
BROWSEINFO bi;
::ZeroMemory(&bi,sizeof(bi));
MCI_STATUS_PARMS status;
LPITEMIDLIST target=SHBrowseForFolder(&bi);
if(target!=NULL)
{
char targetpath[MAX_PATH];
SHGetPathFromIDList(target,targetpath);
CString tm_filepath;
CString tm_title;
CString tm_time;
CString tm_p;
CString ss;
tm_filepath.Format("%s\\*.*",targetpath);
CFileFind folder;
folder.FindFile(tm_filepath);
int flag=folder.FindNextFile();
while(flag)
{
flag=folder.FindNextFile();
tm_title=folder.GetFileName();
tm_filepath=folder.GetFilePath();
tm_p=tm_title.Right(3);
if(tm_p=="mp3")
{
ss.Format("%03d",m_songsum);
int r=m_list.InsertItem(m_songsum,ss);
m_songsum++;
m_list.SetItemText(r,0,ss);
m_list.SetItemText(r,1,tm_title);
m_list.SetItemText(r,3,tm_filepath);
mp3.Read(tm_filepath);
status=mp3.GetLenth();
int min,sec;
CString str;
min=(status.dwReturn/1000)/60;
sec=(status.dwReturn/1000)%60;
CString sstr;
sstr.Format("%02d:%02d",min,sec);
m_list.SetItemText(r,2,sstr);
mp3.Stop();
}
}
}
}
然后其他的感觉都不是很难了。定时器这些东西虽然以前不知道,不过后来看了一下也很快就明白了
哦对了,说一下怎么实现删除单一歌曲的吧
先截个图:
实现过程在孙鑫的那本书上面有。就是自己创建一个弹出菜单,不过要自己去写消息映射~
当然,删除歌曲之后前面的序号要变一下,这个很容易理解。
再来一个可能比较容易忘的:
双击列表中的歌曲就播放该歌曲
void CMusicDlg::OnDblclkList1(NMHDR* pNMHDR, LRESULT* pResult)
{
// TODO: Add your control notification handler code here
NMLISTVIEW* pNMLISTVIEW=(NMLISTVIEW*)pNMHDR;
int nItem;
nItem=pNMLISTVIEW->iItem;
if(nItem>=0&&nItem
int now_play=nItem;
m_filepath=m_list.GetItemText(now_play,3);
m_nowid=now_play;
mp3.Stop();
mp3.Read(m_filepath);
OnBtnPlay();
}
*pResult = 0;
}
主要是nItem前面那儿风骚的强制转换了一把,(我也是到处抄的。。)感觉操作略微有点飘逸。后面还是很中规中矩的停止,播放~
不过,后面删除可不能这样写,要分开成两个函数
好啦,差不多就写这么多吧~其实写完了才发现,犀利的操作并不多嘛(就当是以后的函数手册了)