Add AudioSinkPlayback::position (#19173)

# Objective

- Allow users to get the playback position of playing audio.

## Solution

- Add a `position` method to `AudioSinkPlayback`
- Implement it for `AudioSink` and `SpatialAudioSink`

## Testing

- Updated `audio_control` example to show playback position
This commit is contained in:
Weihnachtsbaum 2025-05-26 19:50:40 +02:00 committed by GitHub
parent 645871e74e
commit 841105a993
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 1 deletions

View File

@ -42,6 +42,14 @@ pub trait AudioSinkPlayback {
/// No effect if not paused. /// No effect if not paused.
fn play(&self); fn play(&self);
/// Returns the position of the sound that's being played.
///
/// This takes into account any speedup or delay applied.
///
/// Example: if you [`set_speed(2.0)`](Self::set_speed) and [`position()`](Self::position) returns *5s*,
/// then the position in the recording is *10s* from its start.
fn position(&self) -> Duration;
/// Attempts to seek to a given position in the current source. /// Attempts to seek to a given position in the current source.
/// ///
/// This blocks between 0 and ~5 milliseconds. /// This blocks between 0 and ~5 milliseconds.
@ -181,6 +189,10 @@ impl AudioSinkPlayback for AudioSink {
self.sink.play(); self.sink.play();
} }
fn position(&self) -> Duration {
self.sink.get_pos()
}
fn try_seek(&self, pos: Duration) -> Result<(), SeekError> { fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
self.sink.try_seek(pos) self.sink.try_seek(pos)
} }
@ -281,6 +293,10 @@ impl AudioSinkPlayback for SpatialAudioSink {
self.sink.play(); self.sink.play();
} }
fn position(&self) -> Duration {
self.sink.get_pos()
}
fn try_seek(&self, pos: Duration) -> Result<(), SeekError> { fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
self.sink.try_seek(pos) self.sink.try_seek(pos)
} }

View File

@ -6,7 +6,10 @@ fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, (update_speed, pause, mute, volume)) .add_systems(
Update,
(update_progress_text, update_speed, pause, mute, volume),
)
.run(); .run();
} }
@ -16,6 +19,17 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
MyMusic, MyMusic,
)); ));
commands.spawn((
Text::new(""),
Node {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
},
ProgressText,
));
// example instructions // example instructions
commands.spawn(( commands.spawn((
Text::new("-/=: Volume Down/Up\nSpace: Toggle Playback\nM: Toggle Mute"), Text::new("-/=: Volume Down/Up\nSpace: Toggle Playback\nM: Toggle Mute"),
@ -34,10 +48,23 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
#[derive(Component)] #[derive(Component)]
struct MyMusic; struct MyMusic;
#[derive(Component)]
struct ProgressText;
fn update_progress_text(
music_controller: Single<&AudioSink, With<MyMusic>>,
mut progress_text: Single<&mut Text, With<ProgressText>>,
) {
progress_text.0 = format!("Progress: {}s", music_controller.position().as_secs_f32());
}
fn update_speed(music_controller: Query<&AudioSink, With<MyMusic>>, time: Res<Time>) { fn update_speed(music_controller: Query<&AudioSink, With<MyMusic>>, time: Res<Time>) {
let Ok(sink) = music_controller.single() else { let Ok(sink) = music_controller.single() else {
return; return;
}; };
if sink.is_paused() {
return;
}
sink.set_speed((ops::sin(time.elapsed_secs() / 5.0) + 1.0).max(0.1)); sink.set_speed((ops::sin(time.elapsed_secs() / 5.0) + 1.0).max(0.1));
} }