0 votes

Process in settings Timer node set on Fixed. But starting timer in project along with the metronome playing in FL Studio (or musical composition in a player) I find a discrepancy. Sometimes next kick it sounds a bit later or earlier than necessary.

My script looks like this:

onready var sounds = get_node("SamplePlayer")
onready var timer = get_node("Timer")
var BPM = 160

func _ready():
    set_fixed_process(true)
    timer.start()

func _fixed_process( delta ):
    # I tried to move following line in ready() function without delta
    timer.set_wait_time((60/BPM)*(60*delta))

func _on_Timer_timeout():
    sounds.play("kick", false)

How can I automatically adjust timer as perfectly aligned metronome?

asked Oct 19, 2016 in Engine by toby3d (19 points)
edited Oct 19, 2016 by toby3d

1 Answer

0 votes

If your frame rate is 60, then a 60 * delta in the fixed_process is going to be 1. Anything times 1 is the same. So I don't think you need that.

Also if you have the timer's wait time being set in the fixed_process isn't it getting reset faster than it can expire? Fixed process at 60FPS is going to execute once every 16.6 milliseconds.

I would think that you want to set the wait time out in the ready function, and then again at the end of the timer time out.

If you want to used fixed_process you should want to just drop the timer entirely, and keep a counting variable and use an if statement to track the intervals.

answered Oct 19, 2016 by avencherus (4,596 points)

So how can I combine the processes, I do not care which one I'm going to use the Timer. But, it seems to me, fixed_process() used just to achieve the same results on different machines that 30FPS, that 60FPS. This is what I seek - the same work metronome on different machines, which I synchronize everything else.

I tried to put the function in _on_Timer_timeout() with delta from process, but the results are the same. At some point it becomes audible Timer lost his rhythm, and delay is accumulated.

Maybe there is another way to create an accurate metronome without node Timer? For example through while or for?

It doesn't sound as if you're familiar with game loops and delta time.

The timer offers you a node that can fire off an event after the amount of time you issue for it. I believe the options for fixed and idle determine when the timer updates itself internally, and you need not do anything with the fixed_process function. Fixed ensures a consistent timestep, idle is variable, but they both strive to maintain the target frame rate. So I don't think there will be an issue either way, unless you can detect some sort of millisecond of delay.

If the timer is not set to one shot, it will continually fire it's event. Place the code there, and if it's set to fixed, it should remain synchronized to the fixed time step loop, at whatever framerate you decide on.

There are many ways of achieving the same result.

If you want to do it manually, that is also fine. The loops are doing their best to maintain their timing, and the delta time is really only used for creating frame by frame changes to things like object velocity and such. You're dealing with something at a one second interval, so there are 59 other iterations there where nothing should happen.

Might look something like this:

var time = 0.0

func _ready():
    set_fixed_process(true)

func _fixed_process(delta):

    time += delta

    if(time >= 1):
        time -= 1
        play_my_sound()

Just in case, I'll show you what the problem is clearly (Just Listen for the sounds): YouTube

And this difference is audible in the following cases:

  • Fixed Timer && _fixed_process
  • Idle Timer && _fixed_process
  • Fixed Timer && _process
  • Idle Timer && _process

I tried to put the autocorrect Timer duration string in _process(), and when the Timer expires. The results are almost identical.

I don't think it's possible to play a sound in perfect rythm from a 60 fps loop, it's not accurate enough. With avencherus solution you can play a sound at the same rate in average, but it will always be a bit off.

What you need is a higher rate of update (more than 60fps), but a better bet is ahead-of-time control over the audio buffer that is sent to the sound card, because that one will be fed at always the same speed in time (44,100Hz). However I don't think Godot supports that in GDScript right now.

Oh my it is quite noticeable. Sorry, wish I knew more about audio.

Realtime I don't think will be possible, maybe you can lax the perfection in your design.

Of course. The trick is to reach an "acceptable" latency. And usually it's done by ahead-of-time programming techniques (buffering, scheduling, latency compensation...).
In fact, there is already latency for graphics. So I guess synchronizing the two is the goal :)

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.