From 199d161bef9c43b600b2203503faaa045a3c5370 Mon Sep 17 00:00:00 2001 From: Ivan Stepanov Date: Sun, 16 Jun 2024 10:40:43 +0300 Subject: [PATCH 1/3] Audio seek --- crates/bevy_audio/src/sinks.rs | 13 +++++++++++++ examples/audio/audio_control.rs | 14 +++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/bevy_audio/src/sinks.rs b/crates/bevy_audio/src/sinks.rs index b0c77456e1db6..7c6676dc267d6 100644 --- a/crates/bevy_audio/src/sinks.rs +++ b/crates/bevy_audio/src/sinks.rs @@ -1,7 +1,9 @@ use bevy_ecs::component::Component; use bevy_math::Vec3; use bevy_transform::prelude::Transform; +use rodio::source::SeekError; use rodio::{Sink, SpatialSink}; +use std::time::Duration; use crate::Volume; @@ -94,6 +96,9 @@ pub trait AudioSinkPlayback { self.mute(); } } + + /// Seeks to a specific position in the audio. + fn try_seek(&self, pos: Duration) -> Result<(), SeekError>; } /// Used to control audio during playback. @@ -190,6 +195,10 @@ impl AudioSinkPlayback for AudioSink { self.sink.set_volume(volume.to_linear()); } } + + fn try_seek(&self, pos: Duration) -> Result<(), SeekError> { + self.sink.try_seek(pos) + } } /// Used to control spatial audio during playback. @@ -286,6 +295,10 @@ impl AudioSinkPlayback for SpatialAudioSink { self.sink.set_volume(volume.to_linear()); } } + + fn try_seek(&self, pos: Duration) -> Result<(), SeekError> { + self.sink.try_seek(pos) + } } impl SpatialAudioSink { diff --git a/examples/audio/audio_control.rs b/examples/audio/audio_control.rs index 55d60e20837bc..68849a0e53085 100644 --- a/examples/audio/audio_control.rs +++ b/examples/audio/audio_control.rs @@ -6,7 +6,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) - .add_systems(Update, (update_speed, pause, mute, volume)) + .add_systems(Update, (update_speed, pause, mute, volume, seek)) .run(); } @@ -84,3 +84,15 @@ fn volume( sink.set_volume(current_volume - Volume::Linear(0.1)); } } + +fn seek( + keyboard_input: Res>, + music_controller: Query<&AudioSink, With>, +) { + if let Ok(sink) = music_controller.single() { + if keyboard_input.just_pressed(KeyCode::Digit0) { + sink.try_seek(std::time::Duration::from_secs_f64(0.0)) + .unwrap(); + } + } +} From 697e7877949df5268e1a4b5ba579318c0bfcac62 Mon Sep 17 00:00:00 2001 From: Taj Holliday <207123560+taj-holliday@users.noreply.github.com> Date: Sat, 26 Apr 2025 15:14:41 -0400 Subject: [PATCH 2/3] Add more thorough docs from rodio, re-export rodio::source::SeekError --- crates/bevy_audio/src/sinks.rs | 46 ++++++++++++++++++++++----------- examples/audio/audio_control.rs | 14 +--------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/crates/bevy_audio/src/sinks.rs b/crates/bevy_audio/src/sinks.rs index 7c6676dc267d6..29bb9aac013f2 100644 --- a/crates/bevy_audio/src/sinks.rs +++ b/crates/bevy_audio/src/sinks.rs @@ -1,11 +1,10 @@ +use crate::Volume; use bevy_ecs::component::Component; use bevy_math::Vec3; use bevy_transform::prelude::Transform; -use rodio::source::SeekError; +use core::time::Duration; +pub use rodio::source::SeekError; use rodio::{Sink, SpatialSink}; -use std::time::Duration; - -use crate::Volume; /// Common interactions with an audio sink. pub trait AudioSinkPlayback { @@ -43,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. @@ -96,9 +115,6 @@ pub trait AudioSinkPlayback { self.mute(); } } - - /// Seeks to a specific position in the audio. - fn try_seek(&self, pos: Duration) -> Result<(), SeekError>; } /// Used to control audio during playback. @@ -165,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(); } @@ -195,10 +215,6 @@ impl AudioSinkPlayback for AudioSink { self.sink.set_volume(volume.to_linear()); } } - - fn try_seek(&self, pos: Duration) -> Result<(), SeekError> { - self.sink.try_seek(pos) - } } /// Used to control spatial audio during playback. @@ -265,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(); } @@ -295,10 +315,6 @@ impl AudioSinkPlayback for SpatialAudioSink { self.sink.set_volume(volume.to_linear()); } } - - fn try_seek(&self, pos: Duration) -> Result<(), SeekError> { - self.sink.try_seek(pos) - } } impl SpatialAudioSink { diff --git a/examples/audio/audio_control.rs b/examples/audio/audio_control.rs index 68849a0e53085..55d60e20837bc 100644 --- a/examples/audio/audio_control.rs +++ b/examples/audio/audio_control.rs @@ -6,7 +6,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) - .add_systems(Update, (update_speed, pause, mute, volume, seek)) + .add_systems(Update, (update_speed, pause, mute, volume)) .run(); } @@ -84,15 +84,3 @@ fn volume( sink.set_volume(current_volume - Volume::Linear(0.1)); } } - -fn seek( - keyboard_input: Res>, - music_controller: Query<&AudioSink, With>, -) { - if let Ok(sink) = music_controller.single() { - if keyboard_input.just_pressed(KeyCode::Digit0) { - sink.try_seek(std::time::Duration::from_secs_f64(0.0)) - .unwrap(); - } - } -} From 8b1ce681086afd9d87a9273769fd5ab676e4ecb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Sat, 3 May 2025 11:21:30 +0200 Subject: [PATCH 3/3] Update crates/bevy_audio/src/sinks.rs Co-authored-by: Jan Hohenheim --- crates/bevy_audio/src/sinks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_audio/src/sinks.rs b/crates/bevy_audio/src/sinks.rs index 29bb9aac013f2..ed51754f86541 100644 --- a/crates/bevy_audio/src/sinks.rs +++ b/crates/bevy_audio/src/sinks.rs @@ -58,7 +58,7 @@ pub trait AudioSinkPlayback { /// It will return an error if an implementation ran /// into one during the seek. /// - /// When seeking beyond the end of a source this + /// 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>;