MFC制作的简单音乐播放器总结

最近刚刚看完MFC中各种控件的重绘,试着模仿QQ音乐做了个界面,不过没有播放音乐的功能。最近为了C++大作业,把功能加了上去,不过界面是临时重写的,十分粗糙,很多图片没有处理,直接放上去了,不过对于我们新手来说,一步步来,过一段时间一定能再填完整的。

好了,直接总结。

核心:播放代码(直接使用Bass库,不过只是小小的用了下,不知道的可以到http://www.un4seen.com/files/bass24.zip去下载,很强大的库)

BOOL CTestBassDlg::OnInitDialog()
{
        //初始化代码
	BASS_Init(-1,44100,0,this->m_hWnd,NULL); 
	m_volum.SetRange(0,100);
	m_volum.SetPos(20);
	//设置音量
	float pp=10/100.0f; 
	BASS_SetVolume(pp);
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}
void CTestBassDlg::OnBnClickedButton1()
{
	//打开文件
    m_play=BASS_StreamCreateFile(FALSE, "E:\\歌曲\\:北极雪 ....mp3",0,0,BASS_SAMPLE_MONO);
	//开始播放
	BASS_ChannelPlay(m_play,FALSE);
}
void CTestBassDlg::OnBnClickedButton2()
{
	// TODO: 在此添加控件通知处理程序代码
	CFileDialog read(TRUE,NULL,NULL,OFN_HIDEREADONLY|OFN_ALLOWMULTISELECT|OFN_NOCHANGEDIR,
		_T("媒体文件(*.avi,*.mp3,*.wma)|*.avi;*.mp3;*.wma||"));
	POSITION pos=read.GetStartPosition();
	if (IDOK==read.DoModal())
	{ 
		int count=m_list.GetCount();
	  while(pos!=NULL)
       {   
		   CString path;
		   path=read.GetNextPathName(pos);
		   m_list.AddString(path);
		   count++;
	   }
	}
}
void CTestBassDlg::OnLbnDblclkList1()
{
	// TODO: 在此添加控件通知处理程序代码
	int nIndex=m_list.GetCurSel();
	CString path;
	if (nIndex!=-1)
		m_list.GetText(nIndex,path); 
	//释放 
	if (m_play!=NULL) BASS_StreamFree(m_play);
	m_play=BASS_StreamCreateFile(FALSE, path,0,0,BASS_SAMPLE_MONO);
	initpro();
	//播放
	BASS_ChannelPlay(m_play,FALSE);
	//设置回调函数
	BASS_ChannelSetSync(m_play, BASS_SYNC_END, (QWORD)MAKELONG(10,0), &CTestBassDlg::MySyncProc, 0);

}
void CTestBassDlg::OnBnClickedButton3()
{
	// TODO: 在此添加控件通知处理程序代码
	//停止
	 BASS_ChannelStop(m_play);
}
void CTestBassDlg::OnBnClickedButton4()
{
	// TODO: 在此添加控件通知处理程序代码
	//播放
	if (m_play!=NULL) BASS_ChannelPlay(m_play,FALSE);
}
void CTestBassDlg::OnBnClickedButton5()
{
	// TODO: 在此添加控件通知处理程序代码
	//暂停
	BASS_ChannelPause(m_play);
}

void CTestBassDlg::OnTRBNThumbPosChangingSlider1(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMTRBTHUMBPOSCHANGING *pNMTPC = reinterpret_cast<NMTRBTHUMBPOSCHANGING *>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	int pos=m_volum.GetPos();
	BASS_SetVolume(pos/100.0f);
	*pResult = 0;
}
void CTestBassDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	int pos=m_volum.GetPos();
	BASS_SetVolume(pos/100.0f);
	pos=m_progress.GetPos();

	//获取播放的位置
	QWORD post=BASS_ChannelSeconds2Bytes(m_play,pos);
	//重新设置
	BASS_ChannelSetPosition(m_play,post,BASS_POS_BYTE);
	
	CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CTestBassDlg::initpro()
{
	double time=BASS_ChannelBytes2Seconds(m_play, BASS_ChannelGetLength(m_play, BASS_POS_BYTE));
//这里获取歌曲的相关信息 具体的结构可以去看帮助文档的内容 TAG_ID3 *pTag = (TAG_ID3 *)BASS_ChannelGetTags(m_play,BASS_TAG_ID3); CString show; show.Format(("%lf %s %s %s"),time,pTag->title,pTag->artist,pTag->album); GetDlgItem(IDC_SHOW)->SetWindowText(show); m_progress.SetRange(0,(int)time); m_progress.SetPos(0); KillTimer(1); SetTimer(1,1000,NULL); } void CTestBassDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //获取时间 以秒计算 double time=BASS_ChannelBytes2Seconds(m_play, BASS_ChannelGetPosition(m_play, BASS_POS_BYTE)); m_progress.SetPos((int)time); CDialogEx::OnTimer(nIDEvent); }
//回调函数 可以用来设置 播放的模式 void CALLBACK CTestBassDlg::MySyncProc(HSYNC handle, DWORD channel, DWORD data, void *user) { DWORD dwActive = BASS_ChannelIsActive(channel); if( dwActive == BASS_ACTIVE_STOPPED ) { AfxMessageBox("111"); } }

 上面是Bass的使用 详细的大家可以直接去http://www.cnblogs.com/del/archive/2008/08/17/1269958.html 虽然是VB的方法,但是看函数用法就行了

2.界面的绘制

先看下效果图(图怎么传不上去啊,算了)

很垃圾的界面,我只说下大概的思路(刚学的新手看看,应该会有帮助吧)

1.滚动条(重绘都是在OnPaint中,其实麻烦的是轨道区,滑块的坐标计算)

我只给出部分代码(新手写的,有错误请直接留言)

CPaintDC dc(this); 
    CRect rc;
    GetClientRect(&rc);
	CRect bkrect;
	bkrect.SetRect(rc.left,rc.top,rc.right,rc.bottom);
	CPoint tmp(0,0);
	if (!m_back.IsNull())
		m_back.StretchBlt(dc.m_hDC,bkrect,SRCCOPY);
	if (!m_tunner.IsNull())
		  m_tunner.BitBlt(dc.m_hDC,m_ctunner,tmp,SRCCOPY);
	if (isdown)
		m_dowbutton.StretchBlt(dc.m_hDC,m_cbutton,SRCCOPY);
	else
		if (ishover)
         m_hovbutton.StretchBlt(dc.m_hDC,m_cbutton,SRCCOPY);
		else
		 m_norbutton.StretchBlt(dc.m_hDC,m_cbutton,SRCCOPY);
	

 说白了,就是计算坐标,然后贴图片,整个的代码下面给出,主要用这个实现进度变化,音量设置,以及一个小小的功能(设置透明,不过我不会裁剪区,做不到像QQ音乐那样)

2.CButton的绘制(这个就简单了,就是设置自绘属性,所有的变量在DrawItem这个虚函数中都有)

CDC *pdc=CDC::FromHandle(lpDrawItemStruct->hDC);
	SetStretchBltMode(pdc->m_hDC,STRETCH_HALFTONE);
	if (!m_pic.IsNull())
	{   
	    CRect rc=lpDrawItemStruct->rcItem;
		BOOL issel=(lpDrawItemStruct->itemState&ODS_SELECTED?TRUE:FALSE);
		int nIndex=0;
		if (issel) nIndex=2;
		else
			if (ishover) nIndex=1;
		CRect picrect;
		picrect.SetRect(nIndex*GetWidth(),0,(nIndex+1)*GetWidth(),GetHeight());
		//设置去背景
		m_pic.TransDrawImage(pdc,rc.left,rc.top,rc.right,rc.bottom,picrect.left,picrect.top,RGB(255,255,255));
	}

 至于鼠标移进,离开的图片变换就是几个BOOL变量的判断,请看具体代码。

3CListBox的重绘(这个重绘不怎么成功,我想实现透明,但是一直出错,没办法只能先写个简单的了)

if (GetCount()<=0) return;
	CRect rcItem=lpDrawItemStruct->rcItem;
	CDC * pDCControl=CDC::FromHandle(lpDrawItemStruct->hDC);
 	CDC BufferDC;
 	CBitmap ImageBuffer;
 	BufferDC.CreateCompatibleDC(pDCControl);
 	ImageBuffer.CreateCompatibleBitmap(pDCControl,rcItem.Width(),rcItem.Height());
 	BufferDC.SelectObject(&ImageBuffer);
	CString strString;
	GetText(lpDrawItemStruct->itemID,strString);
	CRect rcString;
	rcString.SetRect(4,0,rcItem.Width()-8,rcItem.Height());
	COLORREF crTextColor=RGB(84,84,84);
	BufferDC.FillSolidRect(0,0,rcItem.Width(),rcItem.Height(),RGB(251,243,235));
	BOOL isselect=(lpDrawItemStruct->itemState&ODS_SELECTED?TRUE:FALSE);
	CRect tmprect(0,0,rcItem.Width(),rcItem.Height());
	if (isselect)
		{  
			m_sel.StretchBlt(BufferDC.m_hDC,tmprect,SRCCOPY);
	     }
	else
	 if ( hovenindex==lpDrawItemStruct->itemID )
	 { 
		if ( m_sel!= NULL && !m_sel.IsNull())
		{  
			m_sel.StretchBlt(BufferDC.m_hDC,tmprect,SRCCOPY);
		}
	}
    BufferDC.SetBkMode(TRANSPARENT);
	BufferDC.SetTextColor(crTextColor);
	BufferDC.DrawText(strString,&rcString,DT_VCENTER|DT_SINGLELINE);
	pDCControl->BitBlt(rcItem.left,rcItem.top,rcItem.Width(),rcItem.Height(),&BufferDC,0,0,SRCCOPY);
 	BufferDC.DeleteDC();
 	ImageBuffer.DeleteObject();

上面使用双缓冲来是为了减少闪屏(让我头痛啊,有时候还是会闪屏,看来我还是要再学学这方面啊)

4抛开重绘,主界面就是播放的代码。大家可以直接去看代码

上面可能写的简单了,不过大家可以直接去下代码,但是写的比较急(给别人交作业),代码重复的很多,不过我会加上注释,希望能对新手有所帮助,当然有错误或者不懂得的可

以给我留言,大家一起讨论,解决这些问题,一起加油!!!

http://download.csdn.net/detail/zhoupeng39/6688075

原文地址:https://www.cnblogs.com/fightfuture/p/3466678.html