Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

simple .wav or .mp3 playbck in Windows - where has it gone?

Tags:

winapi

audio

mp3

Is there a "modern" replacement for the old Windows sndPlaySound() function, which was a very convenient way of playing a .wav file in the background while you focused on other matters? I now find myself needing to play an .mp3 file in the background and am wondering how to accomplish the same thing in a relatively easy way that the system supports inherently. Perhaps there's a COM component to acccomplish basic .mp3 playback?

like image 486
Glenn Axworthy Avatar asked Oct 22 '22 06:10

Glenn Axworthy


2 Answers

Over years there have been a few audio and media related APIs and there are a few ways to achieve the goal.

The best in terms of absence of third party libs, best OS version coverage, feature set and simplicity is DirectShow API. 15 years old and still beats the hell out of rivals, supported in all versions of Windows that current and a few of previous versions of Visual Studio could target, except WinRT.

The code snippet below plays MP3 and WMA files. It is C++ however since it is all COM it is well portable across languages.

#include "stdafx.h"
#include <dshow.h>
#include <dshowasf.h>
#include <atlcom.h>

#pragma comment(lib, "strmiids.lib")

#define V(x) ATLVERIFY(SUCCEEDED(x))

int _tmain(int argc, _TCHAR* argv[])
{
    static LPCTSTR g_pszPath = _T("F:\\Music\\Cher - Walking In Memphis.mp3");
    V(CoInitialize(NULL));
    {
        CComPtr<IGraphBuilder> pGraphBuilder;
        V(pGraphBuilder.CoCreateInstance(CLSID_FilterGraph));
        CComPtr<IBaseFilter> pBaseFilter;
        V(pBaseFilter.CoCreateInstance(CLSID_WMAsfReader));
        CComQIPtr<IFileSourceFilter> pFileSourceFilter = pBaseFilter;
        ATLASSERT(pFileSourceFilter);
        V(pFileSourceFilter->Load(CT2COLE(g_pszPath), NULL));
        V(pGraphBuilder->AddFilter(pBaseFilter, NULL));
        CComPtr<IEnumPins> pEnumPins;
        V(pBaseFilter->EnumPins(&pEnumPins));
        CComPtr<IPin> pPin;
        ATLVERIFY(pEnumPins->Next(1, &pPin, NULL) == S_OK);
        V(pGraphBuilder->Render(pPin));
        CComQIPtr<IMediaControl> pMediaControl = pGraphBuilder;
        CComQIPtr<IMediaEvent> pMediaEvent = pGraphBuilder;
        ATLASSERT(pMediaControl && pMediaEvent);
        V(pMediaControl->Run());
        LONG nEventCode = 0;
        V(pMediaEvent->WaitForCompletion(INFINITE, &nEventCode));
    }
    CoUninitialize();
    return 0;
}

If you are playing your own files you are sure to not contain large ID3 tag sections, the code might be twice as short.

like image 95
Roman R. Avatar answered Oct 27 '22 10:10

Roman R.


A simple answer to a lot of problems like this is to simply call out to a command line program with system("play.exe soundfile.mp3") or equivalent. Just treat the command line as another API, an API that is has extensive functionality and is standard, portable, flexible, easy to debug and easy to modify. It may not be as efficient as calling a library function but that often doesn't matter, particularly if the program being called is already in the disk cache. Incidentally, avoid software complexity just because it's "modern"; often that's evidence of an architecture astronaut and poor programming practice.

like image 21
Charlie Avatar answered Oct 27 '22 09:10

Charlie