翻译:Simple C++ MP3 Player Class
如果你只需要在你的应用程序中播放MP3(例如,在应用程序启动屏幕上播放一个简短的MP3),MP3类是一个简单的C++MP3/WMA DirectShow播放器类,可以满足这些简单的需求。原始代码来自Flipcode的贡献者Alan Kemp。原始代码需要进行一些调整,以包括必要的头文件和导入库,以便在VisualStudio2010中进行编译。由于此类依赖于DirectShow,您需要下载Windows SDK来构建它。如果您使用的是Visual Studio 2010,它实际上附带了Windows SDK的一个子集,其中包括DirectShow库,因此您可以在不下载任何内容的情况下构建此类。在调用加载mp3文件之前,您必须调用COM的CoInitialize来初始化COM的运行时。在调用清理之后,您还必须在应用程序结束时调用CoUninitialize。头文件Mp3.h如下所示。
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <mmsystem.h>
#include <strmif.h>
#include <control.h>
#pragma comment(lib, "strmiids.lib")
class Mp3
{
public:
Mp3();
~Mp3();
bool Load(LPCWSTR filename);
void Cleanup();
bool Play();
bool Pause();
bool Stop();
// Poll this function with msTimeout = 0, so that it return immediately.
// If the mp3 finished playing, WaitForCompletion will return true;
bool WaitForCompletion(long msTimeout, long* EvCode);
// -10000 is lowest volume and 0 is highest volume, positive value > 0 will fail
bool SetVolume(long vol);
// -10000 is lowest volume and 0 is highest volume
long GetVolume();
// Returns the duration in 1/10 millionth of a second,
// meaning 10,000,000 == 1 second
// You have to divide the result by 10,000,000
// to get the duration in seconds.
__int64 GetDuration();
// Returns the current playing position
// in 1/10 millionth of a second,
// meaning 10,000,000 == 1 second
// You have to divide the result by 10,000,000
// to get the duration in seconds.
__int64 GetCurrentPosition();
// Seek to position with pCurrent and pStop
// bAbsolutePositioning specifies absolute or relative positioning.
// If pCurrent and pStop have the same value, the player will seek to the position
// and stop playing. Note: Even if pCurrent and pStop have the same value,
// avoid putting the same pointer into both of them, meaning put different
// pointers with the same dereferenced value.
bool SetPositions(__int64* pCurrent, __int64* pStop, bool bAbsolutePositioning);
private:
IGraphBuilder * pigb;
IMediaControl * pimc;
IMediaEventEx * pimex;
IBasicAudio * piba;
IMediaSeeking * pims;
bool ready;
// Duration of the MP3.
__int64 duration;
};
原来的类只有播放、暂停和停止功能。注意:在呼叫“暂停”后,您必须呼叫“播放”才能继续播放。由于我需要循环播放音乐,我需要知道我的MP3何时结束,所以我添加了WaitForCompletion方法来定期轮询播放是否结束,并再次播放。由于原始代码总是以满音量播放,我还添加了一个方法GetVolume来获取音量,以及另一个方法SetVolume来设置音量。注:-10000是最小体积,0是最大体积。如果将任何正音量设置为大于0,则会收到一个错误。您可以调用GetDuration和GetCurrentPosition分别获取MP3的持续时间和当前播放(时间)位置。这两种方法返回的单位是千万分之一秒(1/10000秒):你必须除以10000000才能得到以秒为单位的持续时间。我之所以没有返回以秒为单位的持续时间,是因为我找到了第二个单位
#include "Mp3.h"
#include <uuids.h>
Mp3::Mp3()
{
pigb = NULL;
pimc = NULL;
pimex = NULL;
piba = NULL;
pims = NULL;
ready = false;
duration = 0;
}
Mp3::~Mp3()
{
Cleanup();
}
void Mp3::Cleanup()
{
if (pimc)
pimc->Stop();
if(pigb)
{
pigb->Release();
pigb = NULL;
}
if(pimc)
{
pimc->Release();
pimc = NULL;
}
if(pimex)
{
pimex->Release();
pimex = NULL;
}
if(piba)
{
piba->Release();
piba = NULL;
}
if(pims)
{
pims->Release();
pims = NULL;
}
ready = false;
}
bool Mp3::Load(LPCWSTR szFile)
{
Cleanup();
ready = false;
if (SUCCEEDED(CoCreateInstance( CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void **)&this->pigb)))
{
pigb->QueryInterface(IID_IMediaControl, (void **)&pimc);
pigb->QueryInterface(IID_IMediaEventEx, (void **)&pimex);
pigb->QueryInterface(IID_IBasicAudio, (void**)&piba);
pigb->QueryInterface(IID_IMediaSeeking, (void**)&pims);
HRESULT hr = pigb->RenderFile(szFile, NULL);
if (SUCCEEDED(hr))
{
ready = true;
if(pims)
{
pims->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
pims->GetDuration(&duration); // returns 10,000,000 for a second.
duration = duration;
}
}
}
return ready;
}
bool Mp3::Play()
{
if (ready&&pimc)
{
HRESULT hr = pimc->Run();
return SUCCEEDED(hr);
}
return false;
}
bool Mp3::Pause()
{
if (ready&&pimc)
{
HRESULT hr = pimc->Pause();
return SUCCEEDED(hr);
}
return false;
}
bool Mp3::Stop()
{
if (ready&&pimc)
{
HRESULT hr = pimc->Stop();
return SUCCEEDED(hr);
}
return false;
}
bool Mp3::WaitForCompletion(long msTimeout, long* EvCode)
{
if (ready&&pimex)
{
HRESULT hr = pimex->WaitForCompletion(msTimeout, EvCode);
return *EvCode > 0;
}
return false;
}
bool Mp3::SetVolume(long vol)
{
if (ready&&piba)
{
HRESULT hr = piba->put_Volume(vol);
return SUCCEEDED(hr);
}
return false;
}
long Mp3::GetVolume()
{
if (ready&&piba)
{
long vol = -1;
HRESULT hr = piba->get_Volume(&vol);
if(SUCCEEDED(hr))
return vol;
}
return -1;
}
__int64 Mp3::GetDuration()
{
return duration;
}
__int64 Mp3::GetCurrentPosition()
{
if (ready&&pims)
{
__int64 curpos = -1;
HRESULT hr = pims->GetCurrentPosition(&curpos);
if(SUCCEEDED(hr))
return curpos;
}
return -1;
}
bool Mp3::SetPositions(__int64* pCurrent, __int64* pStop, bool bAbsolutePositioning)
{
if (ready&&pims)
{
DWORD flags = 0;
if(bAbsolutePositioning)
flags = AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame;
else
flags = AM_SEEKING_RelativePositioning | AM_SEEKING_SeekToKeyFrame;
HRESULT hr = pims->SetPositions(pCurrent, flags, pStop, flags);
if(SUCCEEDED(hr))
return true;
}
return false;
}
源代码包括一个静态库项目和DLL项目,以及一个演示项目PlayMp3,它使用助手类CLibMP3DLL播放MP3,以在运行时加载LibMP3DLL.DLL。CLibMP3DLL的用法类似于Mp3类,具有额外的LoadDLL和UnloadDLL方法来加载/卸载dll。下面是CLibMP3DLL的头文件。
class CLibMP3DLL
{
public:
CLibMP3DLL(void);
~CLibMP3DLL(void);
bool LoadDLL(LPCWSTR dll);
void UnloadDLL();
bool Load(LPCWSTR filename);
bool Cleanup();
bool Play();
bool Pause();
bool Stop();
bool WaitForCompletion(long msTimeout, long* EvCode);
bool SetVolume(long vol);
long GetVolume();
__int64 GetDuration();
__int64 GetCurrentPosition();
bool SetPositions(__int64* pCurrent, __int64* pStop, bool bAbsolutePositioning);
private:
HMODULE m_Mod;
};
虽然我可能已经在Mp3类中添加了一些方法,但要使它们正确运行需要付出相当多的努力。我希望将这些节省的时间传递给其他只想播放MP3文件的开发人员,省去麻烦。此类托管在Codeplex上。