Simple C++ MP3 Player Class

If you need to just play MP3s in your application (for example, play a short MP3 during the application splash screen), Mp3 class is a no frills C++ MP3/WMA DirectShow player class, for such simple needs. The original code is from Flipcode's contributor, Alan Kemp. The original code needs a bit of tweaking to include the necessary header files and import libraries so that it will compile in Visual Studio 2010. Since this class relies on DirectShow, you need to download the Windows SDK to build it. If you are using Visual Studio 2010, it actually comes with a subset of the Windows SDK, which includes the DirectShow libraries, so you can build this class without downloading anything. You have to call COM's CoInitialize to initialize COM's runtime before calling the Load on mp3 file. And you have to also call CoUninitialize at the end of your application, after the Cleanup is called. The header file, Mp3.h is listed below.

cppmp3player
Figure 1: cppmp3player

#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;

};

The original class only has the play, pause and stop functionality. Note: after calling Pause, you have to call Play to resume playing. Since I have a need to loop my music, I need to know when my MP3 has ended, so I added the method, WaitForCompletion to poll periodically whether the playing has ended, and to replay it again. Since the original code always played at full volume, I have also added a method, GetVolume to get volume and another method, SetVolume to set volume. Note: -10000 is the minimum volume and 0 is the maximum volume. If you set any positive volume greater than 0, you will receive an error. You can call GetDuration and GetCurrentPosition to get the duration of the MP3 and the current playing (time) position of the MP3 respectively. These 2 methods return units of 10th millionth of a second(1/10,000,000 of a second): you have to divide by 10,000,000 to get the duration in seconds. The reason I did not return the duration in seconds is because I found that second unit is too coarse grained to do seeking. The source code implementation of Mp3.cpp is listed below.

#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;
}

The source code includes a static library project and the DLL project and a demo project, PlayMp3, which plays MP3 with a helper class, CLibMP3DLL, to load the LibMP3DLL.dll at runtime. Usage of CLibMP3DLL is similar to Mp3 class, with additional LoadDLL and UnloadDLL methods to load/unload dll. Below is the header file of 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;
};

Though I may have added a few methods to the Mp3 class, it took quite a bit of effort to get them to run correctly. I hope to pass these time-savings to other developers who simply wants to play a MP3 file, minus the hassle. This class is hosted at Codeplex.



About the Author

Wong Shao Voon

I guess I'll write here what I does in my free time, than to write an accolade of skills which I currently possess. I believe the things I does in my free time, say more about me.

When I am not working, I like to watch Japanese anime. I am also writing some movie script, hoping to see my own movie on the big screen one day.

I like to jog because it makes me feel good, having done something meaningful in the morning before the day starts.

I also writes articles for CodeGuru; I have a few ideas to write about but never get around writing because of hectic schedule.

Related Articles

Downloads

Comments

  • nice one!

    Posted by bleep on 10/02/2011 08:48am

    after trying several other libs i found this at last. very simple and totally stable way to get your mp3s playing. lean and clean indeed! thx a lot for this class!

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 14, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Data protection has long been considered "overhead" by many organizations in the past, many chalking it up to an insurance policy or an extended warranty you may never use. The realities of today make data protection a must-have, as we live in a data driven society. The digital assets we create, share, and collaborate with others on must be managed and protected for many purposes. Check out this upcoming eSeminar and join eVault Chief Technology …

  • "Security" is the number one issue holding business leaders back from the cloud. But does the reality match the perception? Keeping data close to home, on premises, makes business and IT leaders feel inherently more secure. But the truth is, cloud solutions can offer companies real, tangible security advantages. Before you assume that on-site is the only way to keep data safe, it's worth taking a comprehensive approach to evaluating risks. Doing so can lead to big benefits.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds