godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.78k stars 20.13k forks source link

AudioStreamPlayer2D starts playback with a noticeable delay #95528

Open Theome opened 4 weeks ago

Theome commented 4 weeks ago

Tested versions

Reproducible in: v4.2.2.stable.mono.official [15073afe3]

System information

Godot v4.2.2.stable.mono - macOS 14.3.0 - Vulkan (Forward+) - integrated Apple M3 Max - Apple M3 Max (14 Threads)

Issue description

When I use AudioStreamPlayer2D to play a sound, the sound can be heard around 110ms after the keypress. This is a noticeable delay, and a player action such as firing a gun or pressing a button doesn't quite feel "immediate".

The sound sample I'm using does not have any silence at the beginning.

To measure the delay, I recorded an audio track with Audacity. I can clearly hear both the click of my keyboard and the sound being played, and also see the waveforms of both events in the audio track, so the delay is easy to calculate in Audacity's UI.

I measured this delay on three systems:

I measured a delay of around 110ms on both macs, and a delay of 160ms on the PC.

Is this something that can be improved, either in the engine, or in my game? Is the delay caused by my systems? Or is such a delay expected in Godot?

To get a few reference points, I did some research and an "Input lag" commonly refers to the latency between the physical input and the display reacting to that input. 60ms is generally considered pretty good, but this includes the response time from the display hardware itself.

In my small Godot test project, I not only play a sound but also toggle the visibility of a sprite on screen. I recorded a video at 60 fps showing how I press the button on a keyboard, and the sprite appears on the screen after 4 frames on each of the three systems, that is within 67ms.

I also wrote a small command line tool that uses Apple's AVFoundation framework to play the same sound file when I press a key. The delay in that tool is around 70ms. This feels more "immediate" than a delay of 110ms or even 160ms.

I don't know much about the signal chain and at which points in the chain various delays occur and their typical magnitudes, so I'm wondering if it's reasonable or not to expect faster response times. My naive expectation would be:

So around 60ms in total.

As per danluu's keyboard latency test (https://danluu.com/keyboard-latency/), Apple's keyboards are pretty fast. I'm using an Apple USB keyboard on my gaming PC and the builtin keyboard on my MacBook, so that should probably not be an issue.

Steps to reproduce

This is how I play the sound in GDScript:

extends Node2D

var sound = preload("res://sound.wav")
var player
@export var sprite: Sprite2D

func _ready():
    player = AudioStreamPlayer2D.new()
    player.stream = sound
    add_child(player)

func _physics_process(delta):
    # Assigned to 'M' key
    if Input.is_action_just_pressed("tap"):
        player.play()
        sprite.visible = !sprite.visible

See also the attached example project.

Minimal reproduction project (MRP)

InputLatencyTest.zip

Calinou commented 3 weeks ago

Before doing any audio latency tests: make sure you have some kind of background audio playing (such as music playback), so that your headphones don't sleep to save battery or similar. The wake-up time can otherwise mislead you into thinking the latency is much higher than it really is. This is common with USB-based setups in my experience.

Also, consider doing tests with V-Sync disabled in the project settings and with the input check in _input() or _process(), so you can reach the highest possible framerate (and therefore the lowest possible audio latency, as audio queries can only be started at each rendered frame). Doing the playback in _physics_process() means that you're bound by the physics tick rate instead, which is 60 Hz by default. This will add between 0 and ~16.67 ms of latency depending on when you pressed the key. This is one of the reasons why rythm games favor framerates as high as possible (even if it's not the only reason, as these generally support DSP time sample playback).

From my testing, audio latency is pretty low on both Windows and Linux on my PC (e.g. try shooting in the 2D platformer or 3D platformer demos). I don't have objective measurements at hand, but it's not any higher than in most games I play at least. I'm using an Audeze Maxwell in 2.4 GHz wireless:

PC specifications - **CPU:** Intel Core i9-13900K - **GPU:** NVIDIA GeForce RTX 4090 - **RAM:** 64 GB (2×32 GB DDR5-5800 C30) - **SSD:** Solidigm P44 Pro 2 TB - **OS:** Linux (Fedora 40, PipeWire)

Some more suggestions to improve latency:

I also wrote a small command line tool that uses Apple's AVFoundation framework to play the same sound file when I press a key. The delay in that tool is around 70ms. This feels more "immediate" than a delay of 110ms or even 160ms.

Could you upload this tool somewhere? Thanks in advance 🙂

Theome commented 2 weeks ago

This turned out to be quite a rabbit hole I fell in :). Over the past days I did several tests and a long series of measurements to get a better understanding of this issue.

The most important result for me is that with the Godot test script I managed to get an input-to-sound latency as low as 70 ms on Windows, which is very good.

Other takeaways:

For the sake of completeness, here are the specs of my Gaming PC:

I personally find that while 110ms is an acceptable delay for action games, 70 ms feels very different. With such a short delay, I have this visceral feeling as if I'm physically causing the sound with my finger press. This is not the case for the 110 ms delay, where the effect feels more "detached" from the action. It would be great to find a way to get the delays always that short for any input in Godot, on both macOS and Windows.

As the delay is already at 70 ms for mouse input on Windows, it looks to me like larger latencies (for keyboard input or on macOS) aren't related to Godot's audio engine. They're probably caused by something earlier in the signal chain.

Here's the small command line tool that I wrote for macOS and which plays back sounds after a key press with a 60-70 ms delay. When running the tool, the sound file sound.wav is expected to be in the same directory as the tool. To build the tool, create a new Command Line Tool in Xcode and paste the code into main.swift.

TestToolMac.zip