From 2affecdb0701270e7c6a5de64f37600e06a49910 Mon Sep 17 00:00:00 2001 From: Taj Holliday <207123560+taj-holliday@users.noreply.github.com> Date: Sat, 3 May 2025 11:29:38 +0000 Subject: [PATCH] Audio sink seek adopted (#18971) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopted #13869 # Objective Fixes #9076 ## Solution Using `rodio`'s `try_seek` ## Testing @ivanstepanovftw added a `seek` system using `AudioSink` to the `audio_control.rs` example. I got it working with .mp3 files, but rodio doesn't support seeking for .ogg and .flac files, so I removed it from the commit (since the assets folder only has .ogg files). Another thing to note is that `try_seek` fails when using `PlaybackMode::Loop`, as `rodio::source::buffered::Buffered` doesn't support `try_seek`. I haven't tested `SpatialAudioSink`. ## Notes I copied the docs for `try_seek` verbatim from `rodio`, and re-exported `rodio::source::SeekError`. I'm not completely confident in those decisions, please let me know if I'm doing anything wrong. --------- Co-authored-by: Ivan Stepanov Co-authored-by: François Mockers Co-authored-by: Jan Hohenheim Co-authored-by: François Mockers --- crates/bevy_audio/src/sinks.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/crates/bevy_audio/src/sinks.rs b/crates/bevy_audio/src/sinks.rs index b0c77456e1..ed51754f86 100644 --- a/crates/bevy_audio/src/sinks.rs +++ b/crates/bevy_audio/src/sinks.rs @@ -1,10 +1,11 @@ +use crate::Volume; use bevy_ecs::component::Component; use bevy_math::Vec3; use bevy_transform::prelude::Transform; +use core::time::Duration; +pub use rodio::source::SeekError; use rodio::{Sink, SpatialSink}; -use crate::Volume; - /// Common interactions with an audio sink. pub trait AudioSinkPlayback { /// Gets the volume of the sound as a [`Volume`]. @@ -41,6 +42,26 @@ pub trait AudioSinkPlayback { /// No effect if not paused. fn play(&self); + /// Attempts to seek to a given position in the current source. + /// + /// This blocks between 0 and ~5 milliseconds. + /// + /// As long as the duration of the source is known, seek is guaranteed to saturate + /// at the end of the source. For example given a source that reports a total duration + /// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to + /// 42 seconds. + /// + /// # Errors + /// This function will return [`SeekError::NotSupported`] if one of the underlying + /// sources does not support seeking. + /// + /// It will return an error if an implementation ran + /// into one during the seek. + /// + /// When seeking beyond the end of a source, this + /// function might return an error if the duration of the source is not known. + fn try_seek(&self, pos: Duration) -> Result<(), SeekError>; + /// Pauses playback of this sink. /// /// No effect if already paused. @@ -160,6 +181,10 @@ impl AudioSinkPlayback for AudioSink { self.sink.play(); } + fn try_seek(&self, pos: Duration) -> Result<(), SeekError> { + self.sink.try_seek(pos) + } + fn pause(&self) { self.sink.pause(); } @@ -256,6 +281,10 @@ impl AudioSinkPlayback for SpatialAudioSink { self.sink.play(); } + fn try_seek(&self, pos: Duration) -> Result<(), SeekError> { + self.sink.try_seek(pos) + } + fn pause(&self) { self.sink.pause(); }