7

How do I from C or C++ use the MP3 decoder supposedly built in with Windows since Windows Media Player 6.1?

I want to play an mp3 file without having to depend on any other third party library such as for instance LAME.DLL.

I updated the question to better fit the answers I got, since I liked them a lot. Related question.

Community
  • 1
  • 1
Prof. Falken
  • 24,226
  • 19
  • 100
  • 173
  • 1
    It's going to involve COM programming of DirectShow filters - building a filter graph. I don't have any code, so I don't have an answer. But hopefully you can have something to google for now: How to decode an mp3 with DirectShow? – Ian Boyd Nov 14 '11 at 12:32
  • That would have been a good answer in its own right. – Prof. Falken Nov 14 '11 at 12:51
  • Nah, unless an answer includes code (or pseudo-code), it's not an acceptable answer. i only know that you *can* construct a filter-graph with DirectShow COM objects to be able to access the data. i know that it is more complicated than "*just letting Windows play the MP3*", since you want the decoded waveform data. But it is possible (tools like VirtualDub and AviSynth do it). – Ian Boyd Nov 14 '11 at 15:51
  • http://code4k.blogspot.se/2010/05/playing-mp3-in-c-using-plain-windows.html – Prof. Falken May 10 '13 at 12:49

2 Answers2

11

Sure. Like lots of other things in the Windows API, there's more than one way to go about playing .mp3 files. The "easiest" way to do this programmatically is using DirectShow. The MSDN docs even include a minimal code example on a page aptly called "How To Play a File" to get you started:

// Visual C++ example
#include <dshow.h>
#include <cstdio>
// For IID_IGraphBuilder, IID_IMediaControl, IID_IMediaEvent
#pragma comment(lib, "strmiids.lib") 

// Obviously change this to point to a valid mp3 file.
const wchar_t* filePath = L"C:/example.mp3"; 

int main()
{
    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEvent   *pEvent = NULL;

    // Initialize the COM library.
    HRESULT hr = ::CoInitialize(NULL);
    if (FAILED(hr))
    {
        ::printf("ERROR - Could not initialize COM library");
        return 0;
    }

    // Create the filter graph manager and query for interfaces.
    hr = ::CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
                        IID_IGraphBuilder, (void **)&pGraph);
    if (FAILED(hr))
    {
        ::printf("ERROR - Could not create the Filter Graph Manager.");
        return 0;
    }

    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);

    // Build the graph.
    hr = pGraph->RenderFile(filePath, NULL);
    if (SUCCEEDED(hr))
    {
        // Run the graph.
        hr = pControl->Run();
        if (SUCCEEDED(hr))
        {
            // Wait for completion.
            long evCode;
            pEvent->WaitForCompletion(INFINITE, &evCode);

            // Note: Do not use INFINITE in a real application, because it
            // can block indefinitely.
        }
    }
    // Clean up in reverse order.
    pEvent->Release();
    pControl->Release();
    pGraph->Release();
    ::CoUninitialize();
}

Make sure you read through the DirectShow documentation to get an idea of what's supposed to happen in a proper DirectShow application.


To "feed" media data into a graph, you need to implement a IAsyncReader. Fortunately, the Windows SDK includes a sample that implements an IAsyncReader called CAsyncReader. The sample reads a media file into a memory buffer then uses CAsyncReader to stream the data into the graph. This may be what you want. On my machine the sample is located in the folder C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\multimedia\directshow\filters\async.

In silico
  • 51,091
  • 10
  • 150
  • 143
  • Can you also play from a memory buffer instead of a file path? If you don't know I'll just start reading filter graph docs. Thanks again! – Prof. Falken Nov 14 '11 at 12:54
  • @AmigableClarkKant: Actually it means ["performed on computer or via computer simulation"](http://en.wikipedia.org/wiki/In_silico). :-) – In silico Nov 14 '11 at 12:55
  • Is it this? http://msdn.microsoft.com/en-us/library/windows/desktop/dd390089(v=VS.85).aspx – Prof. Falken Nov 14 '11 at 12:56
  • @AmigableClarkKant: The DirectShow API is quite comprehensive, so it's actually not as simple as just using `IGraphBuilder::Render()`. Let me see if there's a way to feed bytes from a memory buffer into a DirectShow graph. – In silico Nov 14 '11 at 12:57
  • And that code looks really proper and "programmatic", so I give it a +1 – Prof. Falken Nov 14 '11 at 13:03
  • @AmigableClarkKant: To feed data into a graph from memory you need to implement an `IAsyncReader`. Fortunately, [the Windows SDK includes a sample that implements `IAsyncReader`](http://msdn.microsoft.com/en-us/library/dd317573.aspx). You may want to start there. – In silico Nov 14 '11 at 13:09
  • Ok, very good. This looks like the more solid approach to me, but I am going to implement the MCI approach first because it looks so easy to do, but I really want avoid the file mess and go the memory buffer route eventually. Thanks again! – Prof. Falken Nov 14 '11 at 13:19
  • Maybe you can help me with this question https://stackoverflow.com/questions/64892800/how-to-get-uncompressed-byte-array-from-directshow – Sirop4ik Nov 18 '20 at 12:17
3

You can control an audio channel (in order to make it play anything, including MP3) with mciSendString http://msdn.microsoft.com/en-us/library/ms709492%28VS.85%29.aspx

Here's an example (it's in C#, but it's basically the same principle):

http://social.msdn.microsoft.com/forums/en-US/Vsexpressvcs/thread/152f0149-a62a-446d-a205-91256da7845d

Here is the same principle in C:

http://www.daniweb.com/software-development/c/code/268167

Prof. Falken
  • 24,226
  • 19
  • 100
  • 173
Fabio Ceconello
  • 15,819
  • 5
  • 38
  • 51
  • And this seems to be it in C: http://www.daniweb.com/software-development/c/code/268167 – Prof. Falken Nov 14 '11 at 12:59
  • That looked really, really simple, so I am accepting your answer, specifically the last C link. – Prof. Falken Nov 14 '11 at 13:02
  • @AmigableClarkKant Ohh, i thought you wanted to *decode* an MP3, not simply play it! – Ian Boyd Nov 14 '11 at 15:53
  • @IanBoyd, I see how that happened. And I really want to eventually, can you do that in Windows with its built in mp3 decoder? That would be really fascinating. – Prof. Falken Nov 14 '11 at 16:12
  • 1
    @AmigableClarkKant In Silco's answer (http://stackoverflow.com/questions/8121570/use-windows-built-in-mp3-decoder/8121768#8121768) is the best head-start to that. In his case he calls the `RenderFile` method, which does everything required to **"play"** a file. You need to start with the same filter graph, but then access the raw (e.g. signed integer) data stream. i don't know *how* to do that in DirectShow. i know it's possible, but i don't know how. So i'll have to leave it to someone more knowledgeable to mention what's needed to access data moving between filter elements. – Ian Boyd Nov 14 '11 at 16:33
  • Thanks! When (if) I get around to do that, I know what question to ask. Heck, I may just as well ask it now... – Prof. Falken Nov 14 '11 at 17:01
  • @IanBoyd, there: http://stackoverflow.com/questions/8125234/how-to-access-raw-decoded-audio-data-using-windows-built-in-mp3-decoder – Prof. Falken Nov 14 '11 at 17:08