最近一个小项目要用到音乐播放模块,于是翻了一下资料,发现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();
}