diff --git a/crates/bevy_audio/src/audio.rs b/crates/bevy_audio/src/audio.rs index 349cf6b6a4..8a4a406acc 100644 --- a/crates/bevy_audio/src/audio.rs +++ b/crates/bevy_audio/src/audio.rs @@ -57,6 +57,16 @@ pub struct PlaybackSettings { /// Optional scale factor applied to the positions of this audio source and the listener, /// overriding the default value configured on [`AudioPlugin::default_spatial_scale`](crate::AudioPlugin::default_spatial_scale). pub spatial_scale: Option, + /// The point in time in the audio clip where playback should start. If set to `None`, it will + /// play from the beginning of the clip. + /// + /// If the playback mode is set to `Loop`, each loop will start from this position. + pub start_position: Option, + /// How long the audio should play before stopping. If set, the clip will play for at most + /// the specified duration. If set to `None`, it will play for as long as it can. + /// + /// If the playback mode is set to `Loop`, each loop will last for this duration. + pub duration: Option, } impl Default for PlaybackSettings { @@ -81,6 +91,8 @@ impl PlaybackSettings { muted: false, spatial: false, spatial_scale: None, + start_position: None, + duration: None, }; /// Will play the associated audio source in a loop. @@ -136,6 +148,18 @@ impl PlaybackSettings { self.spatial_scale = Some(spatial_scale); self } + + /// Helper to use a custom playback start position. + pub const fn with_start_position(mut self, start_position: core::time::Duration) -> Self { + self.start_position = Some(start_position); + self + } + + /// Helper to use a custom playback duration. + pub const fn with_duration(mut self, duration: core::time::Duration) -> Self { + self.duration = Some(duration); + self + } } /// Settings for the listener for spatial audio sources. diff --git a/crates/bevy_audio/src/audio_output.rs b/crates/bevy_audio/src/audio_output.rs index 1869fb4755..9fc757af44 100644 --- a/crates/bevy_audio/src/audio_output.rs +++ b/crates/bevy_audio/src/audio_output.rs @@ -156,12 +156,49 @@ pub(crate) fn play_queued_audio_system( } }; + let decoder = audio_source.decoder(); + match settings.mode { - PlaybackMode::Loop => sink.append(audio_source.decoder().repeat_infinite()), + PlaybackMode::Loop => match (settings.start_position, settings.duration) { + // custom start position and duration + (Some(start_position), Some(duration)) => sink.append( + decoder + .skip_duration(start_position) + .take_duration(duration) + .repeat_infinite(), + ), + + // custom start position + (Some(start_position), None) => { + sink.append(decoder.skip_duration(start_position).repeat_infinite()); + } + + // custom duration + (None, Some(duration)) => { + sink.append(decoder.take_duration(duration).repeat_infinite()); + } + + // full clip + (None, None) => sink.append(decoder.repeat_infinite()), + }, PlaybackMode::Once | PlaybackMode::Despawn | PlaybackMode::Remove => { - sink.append(audio_source.decoder()); + match (settings.start_position, settings.duration) { + (Some(start_position), Some(duration)) => sink.append( + decoder + .skip_duration(start_position) + .take_duration(duration), + ), + + (Some(start_position), None) => { + sink.append(decoder.skip_duration(start_position)); + } + + (None, Some(duration)) => sink.append(decoder.take_duration(duration)), + + (None, None) => sink.append(decoder), + } } - }; + } let mut sink = SpatialAudioSink::new(sink); @@ -196,12 +233,49 @@ pub(crate) fn play_queued_audio_system( } }; + let decoder = audio_source.decoder(); + match settings.mode { - PlaybackMode::Loop => sink.append(audio_source.decoder().repeat_infinite()), + PlaybackMode::Loop => match (settings.start_position, settings.duration) { + // custom start position and duration + (Some(start_position), Some(duration)) => sink.append( + decoder + .skip_duration(start_position) + .take_duration(duration) + .repeat_infinite(), + ), + + // custom start position + (Some(start_position), None) => { + sink.append(decoder.skip_duration(start_position).repeat_infinite()); + } + + // custom duration + (None, Some(duration)) => { + sink.append(decoder.take_duration(duration).repeat_infinite()); + } + + // full clip + (None, None) => sink.append(decoder.repeat_infinite()), + }, PlaybackMode::Once | PlaybackMode::Despawn | PlaybackMode::Remove => { - sink.append(audio_source.decoder()); + match (settings.start_position, settings.duration) { + (Some(start_position), Some(duration)) => sink.append( + decoder + .skip_duration(start_position) + .take_duration(duration), + ), + + (Some(start_position), None) => { + sink.append(decoder.skip_duration(start_position)); + } + + (None, Some(duration)) => sink.append(decoder.take_duration(duration)), + + (None, None) => sink.append(decoder), + } } - }; + } let mut sink = AudioSink::new(sink);