Welcome, Guest. Please login or register. Did you miss your activation email?

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Stepland

Pages: [1]
1
Audio / Re: Music getPlayingOffset() more accurate?
« on: February 28, 2022, 10:45:37 pm »
Alright here's what I came up with so far !

(click to show/hide)
(click to show/hide)

I just measure the initial lag once per play call and shift by that amount.

Apparently the reinterpret_cast of a void pointer to a function pointer is something very non-standard that could very possibly break at any given time, but it worked for me ! (gcc (Ubuntu 9.3.0-17ubuntu1~20.04))

If a more knowledgeable person could step in and help us find a better way to handle this that would be awesome.

The resulting value occasionally jumps a little, but overall it's pretty clean :

(click to show/hide)
(click to show/hide)
(click to show/hide)

It even seems to work when changing the playback position or the pitch while the song plays, so I don't think I'll bother fixing this any further. It seems to suit my needs for now, and I hope it'll be enough for you as well

2
Audio / Re: Music getPlayingOffset() more accurate?
« on: February 19, 2022, 02:36:11 am »
Well, I completely overlooked that I could just do

getPlayingOffset() - latency

This is almost perfect :

(click to show/hide)
(click to show/hide)

3
Audio / Re: Music getPlayingOffset() more accurate?
« on: February 19, 2022, 01:26:44 am »
I am getting very close to a solution :



Subtracting lag from the offset reported by OpenAL gives a very smooth result, only two thing to fix :

  • It moves back to zero every time sf::SoundStream clears unsed buffers
  • it's a bit late compared to the reported offset

I already have ideas to overcome both of these. I'm getting there !

4
Audio / Re: Music getPlayingOffset() more accurate?
« on: February 18, 2022, 01:29:40 am »
Just found out about AL_SEC_OFFSET_LATENCY_SOFT (It's described here) and figured it might help.

There might be a way to use it to make a better Method #2

I tried it but I couldn't quite make sense of the values it was returning. I'm very new to OpenAL so I'm still struggling a bit ...

5
Audio / Re: Music getPlayingOffset() more accurate?
« on: March 22, 2020, 07:49:36 pm »
If one were able to measure time using the audio hardware clock you could have a simplified Method #1 where you just check for the first step and then measure time from there since there should be no drift ? Or maybe other things I don't know about would come into play, I'm unsure.

Looks like it was in the works at some point for OpenAL ? http://openal.org/pipermail/openal/2017-December/000670.html

6
Audio / Re: Music getPlayingOffset() more accurate?
« on: March 22, 2020, 05:49:31 pm »
I was looking for something like this for my own project so I searched how other rhythm games were handling this, turns out it's a really common problem yet I did not find much regarding SFML on that matter.

My goals for a solution were :
  • Having a linear output, avoiding both the step function shape and jitter
  • Being at most a few ms off regardless of playback time
  • Not hogging the CPU

Here's a lengthy guide through a few methods I came up with.

The first basic (and wrong) idea all the following methods are build upon is starting an sf::Clock when you call sf::Music::play() and just reading elasped time from said sf::Clock, this has two problems :
  • sf::Music::play() is not blocking, since it just instructs another thread that playback has been requested, for this reason and maybe others having to do with the underlying sound lib, there will be an unpredictable delay between the time this function returns and the time playback actually starts
  • I'm not sure why (audio hardware using its own clock ?) but sf::Clock will pretty quickly drift away from the time reported by sf::Music::getPlayingOffset() (as much as a few seconds per minute), and the drift factor itself is pretty unpredictable

Method #1 : The Watchdog Thread
  • Have a "watchdog" thread monitor changes in the value returned by sf::Music::getPlayingOffset()
  • Each time a new value is detected, reset an sf::Clock
  • Report offset as : last detected value + time elapsed on the sf::Clock

This works better because even if we are still using an sf::Clock() to keep time elapsed since the last buffer update, sf::Clock does not drift too much from the audio clock on a timespan as short as a buffer.

Here's my code as a subclass of sf::Music :
(click to show/hide)
(click to show/hide)

Here's my test code :
(click to show/hide)

And the resulting chart :
(click to show/hide)

You can clearly see how the code waits for the first step to correct the raw offset

The downside of this method is the watchdog thread, my implementation uses a 1ms loop, it's still not unreasonably fast but the impact is noticeable when you look at CPU usage. A minimal executable simply playing back an audio file using sf::Music takes about 1.5% of the CPU on my machine, bare playback with Method #1 takes 5%.

Method #2 : overriding onGetData()

Instead of restarting the clock using a busy loop in the watchdog thread, override the buffer callback, onGetData(). Since it should be called on every buffer request from the underlying sound lib, we should get similar results while reducing CPU load right ?

Here's my code :
(click to show/hide)
(click to show/hide)

And here are the results :
(click to show/hide)

You can clearly see it's way too early, my understanding is that audio data is queued/buffered way earlier than when it is actually played. We are wayyy off (still only about 10ms early) and we have weird glitches at the beggining, but at least CPU usage is back to normal.

Method #3 : Taking Lag Into Account

Another idea I had was to make a hybrid of the first two methods :
  • Measure the lag by measuring the time between :
    • the first onGetData() call
    • the first step in the raw offset given by getPlayingOffset()
  • Report offset as previously but substract lag

The first step in the raw offset is monitored by a fast busy loop in a watchdog thread as previously but since we only need to check for the first step to measure lag, the thread only runs for about one second at most.

Here's my code :
(click to show/hide)
(click to show/hide)

And here's the resulting chart :
(click to show/hide)

We are clearly overshooting on our lag correction.

Method #3.5 : Take less lag into account

Interestingly, only substracting half the measured lag gives this curve :
(click to show/hide)

I then tried adjusting the lag correction to get the same results as Method #1 but I couldn't find a non-magic value based solution, while substracting half the lag seemed like a reasonable thing, I'm not going to try and adjust it even more to get the curves looking nice since that would probably just make my code overfitted to my test file , there has to be some hardware-dependant things that will break any result that's been found by twiddling the amount of lag correction.

I'm unsure about the ideal solution, I like Method 3.5 but I think the correction we are looking for is closer to Method #1

Pages: [1]