1. 程式人生 > >MFC程式設計總結,音樂播放器製作

MFC程式設計總結,音樂播放器製作

  自從退出ACM集訓隊之後,就很少寫關於技術的文章了。。以前在QQ空間寫的很多關於ACM的解題報告也覺得寫的太幼稚而刪掉了。現在寫的這篇總結應該在比較長的一段時間裡,都應該會用到吧。其實QQ空間的日誌是很好的記錄技術細節的東西了,因為他和QQ連在一起,所以很方便的就可以進入檢視。常用而且不好記的東西,寫在QQ空間裡面真的是不錯的選擇~     言歸正傳,音樂播放器其實我在大一的時候就用非常生硬的語法寫出過一個非常簡陋的播放器。這次寫的這個是以前那個的加強版。雖然說還是有很多bug,而且我至今不知道那些BUG是什麼地方的問題。。     先給個截圖吧,播放器的介面:

圖片

其實看著還是有那麼回事的。。恩,其實除了波形和線上下載的話,功能和酷狗,以及千千靜聽等播放器的功能是一樣的。

音樂播放器的關鍵是播放音樂,所以第一步是要設計一個類,這個類擁有各種各樣的播放音樂的能力。

我設計的類名字叫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前面那兒風騷的強制轉換了一把,(我也是到處抄的。。)感覺操作略微有點飄逸。後面還是很中規中矩的停止,播放~

不過,後面刪除可不能這樣寫,要分開成兩個函式

好啦,差不多就寫這麼多吧~其實寫完了才發現,犀利的操作並不多嘛(就當是以後的函式手冊了)