c++c++-chronofmod

How to display current music position using FMOD and C++?


I want to display the time that has elapsed as music plays in real-time. FMOD's Core API provides Channel::getPosition() function to obtain the current position in milliseconds. I want to update the position every second.

I am a beginner and have no knowledge of multithreaded programming.

I call Channel::getPosition() in a loop and use std::this_thread::sleep_for() to delay the loop for one second before the next iteration.

Here is the code:

unsigned int position = 0;
std::chrono::milliseconds timespan(1000);
while(true) {
    channel -> getPosition(&position, FMOD_TIMEUNIT_MS);
    std::cout << postion / 1000 << "\n"; //Display seconds
    std::this_thread::sleep_for(timespan);
}

However, I get some buggy output :

0
1
...
13
13
14
16
...

13 appears twice and 15 does not even appear. In another case, 5 appears twice.

I am thinking of rounding up or rounding down the number I obtain from Channel::getPosition() to correct the output.

How can I fix this?

Note: Error checking is omitted for simplicity


Solution

    1. Use <chrono> even for trivial timing functions.

    2. Use the C++17 round function for truncating milliseconds to seconds for this example. If you don't have C++17, steal round from here.

    3. Use sleep_until instead of sleep_for in order to keep a more accurate "timespan" for each iteration of the loop.

    Putting that all together:

    #include <chrono>
    #include <iostream>
    #include <memory>
    #include <thread>
    
    enum unit{FMOD_TIMEUNIT_MS};
    
    struct Channel
    {
        void getPosition(unsigned int* position, unit)
        {
            using namespace std::chrono;
            static auto start = steady_clock::now();
            *position = duration_cast<milliseconds>(steady_clock::now()-start).count();
        } 
    };
    
    int
    main()
    {
        using namespace std::chrono;
        auto channel = std::make_unique<Channel>();
        auto constexpr timespan = 1s;
        auto next_start = system_clock::now() + timespan;
        while (true)
        {
            unsigned int position_as_integral;
            channel->getPosition(&position_as_integral, FMOD_TIMEUNIT_MS);
            milliseconds position{position_as_integral};
            std::cout << round<seconds>(position).count() << '\n';
            std::this_thread::sleep_until(next_start);
            next_start += timespan;
        }
    }