最近一个小项目要用到音乐播放模块,于是翻了一下资料,发现windows环境下播放音频文件有MCI、DirectShow等几种方式,当然也可以使用底层的api WaveOut播放。其中第一种方式最为简单,程序仅需通过发送MCI命令即可控制媒体设备,实现音频文件播放、暂停、停止等操作。其他两种方式就比较繁琐了,首先搭建DirectShow开发环境就比较周折,而使用WaveOut播放音频,需要先解码音频以获取pcm数据,然后才能播放。对于不涉及音频数据处理、仅需播放音频的简单使用场景,推荐使用MCI方式播放音频文件。MCI的用法思路可以概括为:先获取设备id,然后再向该设备发送各种媒体控制命令,以实现媒体播放操作。下面为本人简单封装的MCI使用接口,头、源文件分别如下所示:
#ifndef _MUSIC_PLAYING_MANAGER_H #define _MUSIC_PLAYING_MANAGER_H #include <MMSystem.h> #pragma comment(lib, "winmm.lib") #define RESULT_ERROR -999 class MusicPlayingManager { public: MusicPlayingManager(DWORD dwCallback); ~MusicPlayingManager(void); private: DWORD m_dwCallback; MCIDEVICEID m_mciDeviceId; WCHAR m_szFileName[MAX_PATH]; public: // 打开音乐文件 void OpenMusicFile(); // 播放/暂停/停止音乐 void PlayMusic(); void PauseMusic(); void StopMusic(); // 关闭音频设备 void CloseDevice(); }; #endif
#include "StdAfx.h" #include "MusicPlayingManager.h" MusicPlayingManager::MusicPlayingManager(DWORD dwCallback) { m_mciDeviceId = RESULT_ERROR; m_dwCallback = dwCallback; } MusicPlayingManager::~MusicPlayingManager(void) { CloseDevice(); } // 关闭设备 void MusicPlayingManager::CloseDevice() { if (m_mciDeviceId != RESULT_ERROR) { mciSendCommand(m_mciDeviceId, MCI_CLOSE, NULL, NULL); m_mciDeviceId = RESULT_ERROR; } } // 打开音乐文件 void MusicPlayingManager::OpenMusicFile() { // 选择待播放的文件 CFileDialog dlg( TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST, L"音乐(*.mp3) | *.mp3||", NULL); dlg.m_ofn.lpstrTitle = L"请选择歌曲文件"; if (dlg.DoModal() == IDOK) { CString srcFileName = dlg.GetPathName(); wcscpy_s(m_szFileName, srcFileName.GetBuffer(srcFileName.GetLength())); srcFileName.ReleaseBuffer(); } // 先关闭设备 CloseDevice(); // 再打开设备 MCIERROR mciERR = ERROR_SUCCESS; MCI_OPEN_PARMS mciParams; ZeroMemory(&mciParams, sizeof(mciParams)); mciParams.lpstrDeviceType = NULL; mciParams.lpstrElementName = m_szFileName; mciParams.dwCallback = m_dwCallback; mciERR = mciSendCommand(NULL, MCI_OPEN, MCI_NOTIFY | MCI_OPEN_ELEMENT, (DWORD_PTR)&mciParams); if (mciERR == ERROR_SUCCESS) { m_mciDeviceId = mciParams.wDeviceID; MCI_SET_PARMS mciParams; ZeroMemory(&mciParams, sizeof(mciParams)); mciParams.dwCallback = m_dwCallback; mciParams.dwTimeFormat = MCI_FORMAT_MILLISECONDS; mciSendCommand(m_mciDeviceId, MCI_SET, MCI_NOTIFY | MCI_SET_TIME_FORMAT, (DWORD_PTR)&mciParams); } else { m_mciDeviceId = RESULT_ERROR; AfxMessageBox(L"设备打开失败"); } } // 播放音乐 void MusicPlayingManager::PlayMusic() { if (m_mciDeviceId != RESULT_ERROR) { MCI_PLAY_PARMS mciParams; ZeroMemory(&mciParams, sizeof(mciParams)); mciParams.dwCallback = m_dwCallback; mciSendCommand(m_mciDeviceId, MCI_PLAY, MCI_NOTIFY, (DWORD_PTR)&mciParams); } } // 暂停音乐 void MusicPlayingManager::PauseMusic() { if (m_mciDeviceId != RESULT_ERROR) { MCI_GENERIC_PARMS mciParams; ZeroMemory(&mciParams, sizeof(mciParams)); mciParams.dwCallback = m_dwCallback; mciSendCommand(m_mciDeviceId, MCI_PAUSE, MCI_NOTIFY, (DWORD_PTR)&mciParams); } } // 停止音乐 void MusicPlayingManager::StopMusic() { if (m_mciDeviceId != RESULT_ERROR) { MCI_GENERIC_PARMS mciParams; ZeroMemory(&mciParams, sizeof(mciParams)); mciParams.dwCallback = m_dwCallback; mciSendCommand(m_mciDeviceId, MCI_STOP, MCI_NOTIFY, (DWORD_PTR)&mciParams); mciSendCommand(m_mciDeviceId, MCI_SEEK, MCI_SEEK_TO_START, (DWORD_PTR)&mciParams); } }用法很简单,如下所示:
// 打开文件 void CTestMusicPlayingDlg::OnBnClickedButtonOpenMusic() { m_pMusicPlayingMgr->OpenMusicFile(); m_pMusicPlayingMgr->PlayMusic(); } // 播放音乐 void CTestMusicPlayingDlg::OnBnClickedButtonPlayMusic() { m_pMusicPlayingMgr->PlayMusic(); } // 暂停音乐 void CTestMusicPlayingDlg::OnBnClickedButtonPauseMusic() { m_pMusicPlayingMgr->PauseMusic(); } // 停止音乐 void CTestMusicPlayingDlg::OnBnClickedButtonStopMusic() { m_pMusicPlayingMgr->StopMusic(); }