简单的C++ MP3播放类
malong 发布于 2023-12-19

翻译: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上。

 

malong
关注 私信
文章
35
关注
0
粉丝
0