MFC程式設計總結,音樂播放器製作
其實看著還是有那麼回事的。。恩,其實除了波形和線上下載的話,功能和酷狗,以及千千靜聽等播放器的功能是一樣的。
音樂播放器的關鍵是播放音樂,所以第一步是要設計一個類,這個類擁有各種各樣的播放音樂的能力。
我設計的類名字叫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::Read(CString str){
m_dID=NULL;
m_open.lpstrElementName=str;
mciSendCommand(NULL,MCI_OPEN,MCI_OPEN_ELEMENT,(DWORD)(LPVOID)&m_open);//MCI開啟檔案
m_dID=m_open.wDeviceID;
}
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<m_list.GetItemCount())
{
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前面那兒風騷的強制轉換了一把,(我也是到處抄的。。)感覺操作略微有點飄逸。後面還是很中規中矩的停止,播放~
不過,後面刪除可不能這樣寫,要分開成兩個函式
好啦,差不多就寫這麼多吧~其實寫完了才發現,犀利的操作並不多嘛(就當是以後的函式手冊了)