2

I'm attempting to create two instances of this class which will eventually play music files using Win32's mciSendString features. However to test it since this is the first time I've attempted to use std::thread, I wrote a test(void) method which outputs the class ID which I'd expect to print me a series of 1's and 2's like 12122111112212121212...

I'm getting the following error, the test(void) method indeed exists?

Error 1 error C2064: term does not evaluate to a function taking 0 arguments

#include <iostream>
#include <thread>

typedef enum MusicStatus {
    MUSIC_PLAYING = 0, 
    MUSIC_PAUSED, 
    MUSIC_STOPPED, 
    MUSIC_IDLE
} MusicStatus, *pMusicStatus;

class MusicPlayer
{
public:

    MusicPlayer(void) {
        m_bIsPlaying = false;
        m_bIsPaused = false;
    }

    bool isPaused(void) {
        return m_bIsPaused;
    }

    bool isPlaying(void) {
        return m_bIsPlaying;
    }

    MusicStatus getState(void) {
        if ( !m_bIsPlaying && !m_bIsPaused && !m_bIsStopped )
            return MUSIC_IDLE;
        if ( m_bIsPlaying )
            return MUSIC_PLAYING;
        if ( m_bIsPaused ) 
            return MUSIC_PAUSED;
        if ( m_bIsStopped )
            return MUSIC_STOPPED;
        return MUSIC_STOPPED;
    }

    void test(void) {
        for ( int m = 0; m < 100; m++ ) {
            std::cout << this->n;
        }
    }

    int n;

private:

    bool m_bIsPlaying, m_bIsPaused, m_bIsStopped;

};


int main(int argc, char* argv[])
{
    MusicPlayer A;
    MusicPlayer B;
    A.n = 1;
    B.n = 2;

    std::thread t1(A);
    std::thread t2(B);

    t1.join();
    t2.join();

    A.test();
    B.test();

    system("PAUSE");
    return 0;
}

Update: I've made some adjustment, now I'm having an issue with argument list, error: MusicPlayer::play_sound function call missing argument list

#include <iostream>

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

#include <thread>
#include <dshow.h>
#include "Lib/NSL.h"

typedef enum MusicStatus {
    MUSIC_PLAYING = 0, 
    MUSIC_PAUSED, 
    MUSIC_STOPPED, 
    MUSIC_IDLE
} MusicStatus, *pMusicStatus;

class MusicPlayer
{
public:

    MusicPlayer() {
        m_bIsPlaying = false;
        m_bIsPaused = false;
        m_bIsStopped = false;
    }

    bool isPaused() {
        return m_bIsPaused;
    }

    bool isPlaying() {
        return m_bIsPlaying;
    }

    MusicStatus getState() {
        if ( !m_bIsPlaying && !m_bIsPaused && !m_bIsStopped )
            return MUSIC_IDLE;
        if ( m_bIsPlaying )
            return MUSIC_PLAYING;
        if ( m_bIsPaused ) 
            return MUSIC_PAUSED;
        if ( m_bIsStopped )
            return MUSIC_STOPPED;
        return MUSIC_STOPPED;
    }

    void playAudio(std::string strFilePath) {
        m_strFilePath = strFilePath;
        std::thread audioThread(play_sound);
        audioThread.join();
    }

private:

    bool m_bIsPlaying, m_bIsPaused, m_bIsStopped;
    std::string m_strFilePath;

    void play_sound() {
        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;
        }

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

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

        // Build the graph. IMPORTANT: Change this string to a file on your system.
        hr = pGraph->RenderFile(s2ws(m_strFilePath).c_str(), 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.
            }
        }
        pControl->Release();
        pEvent->Release();
        pGraph->Release();
        CoUninitialize();
    }

};

int main(void)
{
    MusicPlayer A;
    A.playAudio("music.mp3");
    system("pause");
    return 0;
}
johnsonwi
  • 159
  • 3
  • 15

5 Answers5

5

You cannot run an object! What you can run is a member function on a specific object: std::thread needs to be told the entry function for the thread. It uses the first constructor argument as a function object and all other arguments as parameters for how to call the function. Since your class doesn't have a function call operator, std::thread doesn't know which function to call.

There are a ways you can fix the problem:

  1. You provide a function call operator()() for your MusicPlayer type as the entry function for the threads.
  2. You use a member function as the first argument together with the actual object as a parameter being passed, e.g., std::thread t1(&MusicPlayer::test, &A).
  3. You use a readily bound function object as the parameter to std::thread, e.g., std::thread t1(std::bind(&MusicPlayer::test, std::ref(A)).
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I've been using Qt for a while, I was hoping for some functionality where I could extend the thread and have some functionality like exec() until exit() is called. – johnsonwi Dec 25 '12 at 04:23
  • I assume I will have to write some boilerplate while loop – johnsonwi Dec 25 '12 at 04:24
  • @johnsonwi: I recommend you take a look at the following two links: 1. http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/ 2. http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ – bjoernz Dec 25 '12 at 04:44
2

Okay I've solved my issues, its appears std::thread is ideal to playback mp3 files in the background thanks to std::thread. Note: audioThread(&MusicPlayer::play_sound, this);

#include <iostream>

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

#include <thread>
#include <dshow.h>
#include "Lib/NSL.h"

typedef enum MusicStatus {
    MUSIC_PLAYING = 0, 
    MUSIC_PAUSED, 
    MUSIC_STOPPED, 
    MUSIC_IDLE
} MusicStatus, *pMusicStatus;

class MusicPlayer
{
public:

    MusicPlayer() {

        m_bIsPlaying = false;
        m_bIsPaused = false;
        m_bIsStopped = false;
        m_pControl = NULL;
        m_pEvent = NULL;
        m_pGraph = NULL;
        m_pEventEx = NULL;
        m_pBasicAudio = NULL;
        m_pMediaSeeking = NULL;

        // Initialize the COM library
        m_hr = CoInitialize(NULL);
        if (FAILED(m_hr)) { // Could not initialize COM library");
            return;
        }

        // Create the filter graph manager and query for interfaces.
        m_hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGraph);

        if (FAILED(m_hr)) { // Could not create the Filter Graph Manager
            return;
        }

        m_hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **)&m_pControl);
        m_hr = m_pGraph->QueryInterface(IID_IMediaEvent, (void **)&m_pEvent);
        m_hr = m_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&m_pEventEx);
        m_hr = m_pGraph->QueryInterface(IID_IBasicAudio, (void**)&m_pBasicAudio);
        m_hr = m_pGraph->QueryInterface(IID_IMediaSeeking, (void**)&m_pMediaSeeking);
    }

    ~MusicPlayer() {
        m_pControl->Release();
        m_pEvent->Release();
        m_pEventEx->Release();
        m_pGraph->Release();
        m_pBasicAudio->Release();
        m_pMediaSeeking->Release();
        CoUninitialize();
    }

    bool isPaused() {
        return m_bIsPaused;
    }

    bool isPlaying() {
        return m_bIsPlaying;
    }

    MusicStatus getState() {
        if ( !m_bIsPlaying && !m_bIsPaused && !m_bIsStopped )
            return MUSIC_IDLE;
        if ( m_bIsPlaying )
            return MUSIC_PLAYING;
        if ( m_bIsPaused ) 
            return MUSIC_PAUSED;
        if ( m_bIsStopped )
            return MUSIC_STOPPED;
        return MUSIC_STOPPED;
    }

    void playAudio(std::string strFilePath) {
        m_strFilePath = strFilePath;
        set_state(MUSIC_PLAYING);
        std::thread audioThread(&MusicPlayer::play_sound, this);
        audioThread.join();
    }

    bool stopAudio() {
        if ( getState() == MUSIC_PLAYING && m_pControl ) {
            m_hr = m_pControl->Stop();
            if ( SUCCEEDED(m_hr) ) {
                set_state(MUSIC_STOPPED);
                return true;
            }
        }
        return false;
    }

    bool pauseAudio() {
        if ( getState() == MUSIC_PLAYING && m_pControl ) {
            return SUCCEEDED(m_pControl->Pause());
        }
        return false;
    }

    long volume() {
        if ( m_bIsPlaying && m_pBasicAudio ) {
            long lVolume = -1;
            m_hr = m_pBasicAudio->get_Volume(&lVolume);
            if ( SUCCEEDED(m_hr) )
                return lVolume;
        }
        return -1;
    }

    bool setVolume(long lVolume) {
        if ( m_bIsPlaying && m_pBasicAudio ) {
            m_hr = m_pBasicAudio->put_Volume(lVolume);
            return SUCCEEDED(m_hr);
        }
        return false;
    }

    long durationInSeconds() {
        return m_ulTrackDuration / 10000000;
    }

    __int64 currentPosition() {
        if ( getState() == MUSIC_PLAYING && m_pMediaSeeking ) {
            __int64 curPosition = -1;
            m_hr = m_pMediaSeeking->GetCurrentPosition(&curPosition);
            if ( SUCCEEDED(m_hr) )
                return curPosition;
        }
        return -1;
    }

    bool setPosition(__int64* pCurrentPos, __int64* pStop, bool bAbsolutePositioning) {
        if ( getState() == MUSIC_PLAYING && m_pMediaSeeking ) {
            DWORD flags = 0;
            if ( bAbsolutePositioning )
                flags = AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame;
            else
                flags = AM_SEEKING_RelativePositioning | AM_SEEKING_SeekToKeyFrame;
            m_hr = m_pMediaSeeking->SetPositions(pCurrentPos, flags, pStop, flags);
            if ( SUCCEEDED(m_hr) )
                return true;
        }
        return false;
    }

private:

    bool m_bIsPlaying, m_bIsPaused, m_bIsStopped;
    std::string m_strFilePath;

    HRESULT m_hr;
    IGraphBuilder *m_pGraph;
    IMediaControl *m_pControl;
    IMediaEvent   *m_pEvent;
    IMediaEventEx *m_pEventEx;
    IBasicAudio   *m_pBasicAudio;
    IMediaSeeking *m_pMediaSeeking;

    // 10,000,000 per second
    __int64 m_ulTrackDuration;

    void set_state(MusicStatus m) {
        switch(m) {
        case MUSIC_STOPPED:
            m_bIsStopped = true;
            m_bIsPlaying = m_bIsPaused = false;
            break;
        case MUSIC_PAUSED:
            m_bIsPaused = true;
            m_bIsPlaying = m_bIsStopped = false;
            break;
        case MUSIC_PLAYING:
            m_bIsPlaying = true;
            m_bIsPaused = m_bIsStopped = false;
            break;
        case MUSIC_IDLE:
            m_bIsPaused = m_bIsPlaying = m_bIsStopped = false;
            break;
        }
    }

    void play_sound() {
        m_hr = m_pGraph->RenderFile(s2ws(m_strFilePath).c_str(), NULL);
        if (SUCCEEDED(m_hr))
        {
            m_hr = m_pControl->Run();
            if (SUCCEEDED(m_hr)) {
                if ( m_pMediaSeeking ) {
                    m_pMediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
                    m_pMediaSeeking->GetDuration(&m_ulTrackDuration);
                }
            }
        }

    }

};

int main(void)
{
    MusicPlayer A;
    A.playAudio("music.mp3");
    std::cout << A.durationInSeconds();
    system("pause");
    return 0;
}
johnsonwi
  • 159
  • 3
  • 15
  • Did you get the reference to the function `play_sound` of the `MusicPlayer` and the reference of the current object and has sent it to a thread ? Did you know how exactly this executes ? –  May 12 '19 at 20:22
1

Use std::thread as member of your MusicPlayer class, and start the background thread by assigning the thread function to it as soon changing to the state MUSIC_PLAYING.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
1

Minor problem:

MusicPlayer(void)
bool isPaused(void)
bool isPlaying(void)

Functions that have zero parameters are defined as:

MusicPlayer()
bool isPaused()
bool isPlaying()

Major problem.

A thread object constructor takes a functor. Because you do not pass any arguments the functor must also take zero arguments. This means the object you pass to the thread must be callable like this:

MusicPlayer A;
std::thread t1(A);

// This means that the object A is callable like a function.
A();

// For this to work the class MusicPlayer must define the opropriate function operator:

class MusicPlayer
{
    public:
        void operator()() { /* Code run inside thread */ }
};
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Minor problem? I was informed using void to signify no parameters speeds up compile time. Perhaps this is a myth. – johnsonwi Dec 25 '12 at 19:38
  • 1
    @johnsonwi: No parameters is the C++ way of doing it. Using a void in there is solely for compatibility with C http://stackoverflow.com/a/7412299/14065 – Martin York Dec 27 '12 at 03:02
0

You can try something like

std::thread audioThread(([this](){MusicPlayer::play_sound();};
Mark Kahn
  • 1,130
  • 10
  • 8