Ralith / oddio

Lightweight game audio
Apache License 2.0
150 stars 9 forks source link

Gap-free nonlinear sequencing #62

Open Ralith opened 3 years ago

Ralith commented 3 years ago

@tesselode has described some interesting use cases where sounds are played according to complex rules with precise timing based on dynamic gameplay conditions. This is difficult to support: simply playing new sounds immediately in response to gameplay events will produce relative timing jitter in their playback; while this is fine for common gameplay sounds like gunshots, if the samples are intended to be sections of contiguous music then sample-perfect relative timing may be required.

They've solved the problem by allowing the audio thread's current time to be polled from non-realtime gameplay logic, and by allowing the playback of new sounds to be scheduled for precise times in the future. By working slightly in advance, this allows sounds to be timed precisely with respect to eachother based on near-realtime gameplay state.

This alone would be a reasonable primitive for us to provide. However, I suspect the ergonomics can be improved further with clever use of async/await: a specialized async runtime could be provided which masks the latency offset necessary to schedule work ahead of time entirely by maintaining a virtual "now" that, when the runtime is polled, iteratively completes time-based audio futures in order until it reaches the current audio time plus the fixed latency offset, polling all outstanding tasks after each completion. When a new playback command is issued from within a task, it's always relative to the virtual "now". This would allow code like handle.play(foo).await; handle.play(bar).await; to Just Work gap-free, with arbitrarily complex control flow and inspection of game logic folded in.