+5 votes

Hi, I am making a rhythm game (or at least trying to). I have some sound effects that must play in the very precise moment a "circle" on the screen is hit. The main problem is that the sound effect is a few milliseconds late - which are crucial for the game experience. Here is a footage in which you can see that it is indeed noticeable. This is the sound effect.

func _ready():
    # circle preparation would be here
    set_fixed_process(true)
    set_process_input(true)

func _input(event):
    # object_click = keyboard x, keyboard z, mouse left, mouse right
    if event.is_action_pressed("object_click"):
        _object_click()

func _object_click():
    var mousepos = get_global_mouse_pos()
    var bestel = null
    # found what element the mouse is hovering, and if there are multiple elements being overed find the "oldest created" one
    for i in get_tree().get_nodes_in_group("circles"):
        if i.is_part_of(mousepos):
            if bestel == null or bestel.id > i.id:
                bestel = i
    if bestel == null: return
    ## SOUND EFFECT
    get_node("sound_effects").play("normal-hitnormal")
    print("clicked ", bestel.id)
    bestel.fade()
    bestel.remove_from_group("circles")

EDIT: This is the sound effect in audacity. You can see that it starts at the very beginning of the file, without any offset. This is the full waveform

in Engine by (58 points)
edited by

I have been working on build rhythm game based on bms format.
in my case, Linkpy's solution reduced latency alot.
btw, if you are still working on this project, I just want to keep in touch with you

@eaglecat I'm not currently working on it. You probably have guessed that it's a sort of osu! clone prototype. I stopped working of it because the workload of other personal projects is quite high :P

3 Answers

+2 votes

From my own experience (sound and music related applications mainly) Godot is not ready yet for intensive audio works especially if you expect precise syncing and not much latency. Your best bet will be to use rather short samples with SamplePlayer that in my tests was giving best results.
SamplePlayer2D is for other tasks, when you need your sound to be located in the space, so unless it's what your need try to use SamplePlayer.

The audio part of the engine is going to be rewritten in the near future and hopefully it will work much better.

But I also have some thought about what I see on your screen when you play.
I see that you run the scene from Godot and I have an experience thatit can delay some things and run slower than normally it would run from binary file. Have you try to export the release version of the scene and check the delay than? It should make it work slightly better.

Also, I see setfixedprocess(true) in the fragment of your code. Do you use the physics in your game and things that would need to count the precise physics every frame in your game? I ask because counting precise physics might slow things down if not used right.

by (717 points)
edited by

Changing to a simple SamplePlayer noticeably reduced latency - perhaps running it in an executable did, too. But still - the delay is quite long and annoying.
The sample used to test is very small (106ms - 25kb), so I don't think that can be easily solved :/
fixed process isn't actually doing anything at the moment. Removing set_fixed_process didn't show any particular improvements.

+3 votes

After analysing the C++ code of a SamplePlayer and trimming all the fat from the code, I got down to the following function:

func play_sound(sample):
    AudioServer.voice_play(AudioServer.voice_create(), sample)

with sample being a sample loaded from a sample library through the following method chain:

var sample = get_node("soundplayer").get_sample_library().get_sample("sample_name").get_rid()

Using this method, I found the delay to be hugely reduced, and though still perceptible, much less of a bother to the player.

by (58 points)

Interesting find. I was told once on irc channel when trying to fight sound latency in Godot , that using AudioServer will not change anything for me because it's just a
"way to do things with just only more control, that can be done much easier with UI and nodes automation".
I guess someone was wrong and my fault I did not pursue to check the info myself.
So thank you very much for sharing.

+1 vote

Another solution is to do this :
enter image description here

With my try, an output latency to 1-2 will glitch the sound, 3 is the minimum I think. And I think that the buffering option help for the stream. For this, you can also start the music before the real start in the game (in this case, 100ms before the game start).

by (31 points)

I would use this settings with caution. The settings probably might work on one hardware but cause a glitch by buffer underrun on another one.
As for now Godot's delay between the interface input and sound output are probably not a hardware latency settings related most of the time, but the the engine design (audio part of it), that is going to be rewritten soon.

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.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.