sfml/audio/
sound.rs

1use {
2    crate::{
3        audio::{SoundBuffer, SoundSource, SoundStatus},
4        ffi,
5        system::{Time, Vector3f},
6    },
7    std::{marker::PhantomData, ptr::NonNull},
8};
9
10/// Regular sound that can be played in the audio environment.
11///
12/// `Sound` is the type to use to play sounds.
13///
14/// It provides:
15///
16/// - Control (play, pause, stop)
17/// - Ability to modify output parameters in real-time (pitch, volume, ...)
18/// - 3D spatial features (position, attenuation, ...).
19///
20/// `Sound` is perfect for playing short sounds that can fit in memory and require no latency,
21/// like foot steps or gun shots. For longer sounds, like background musics or long speeches,
22/// rather see [`Music`] (which is based on streaming).
23///
24/// In order to work, a sound must be given a buffer of audio data to play.
25/// Audio data (samples) is stored in [`SoundBuffer`], and attached to a sound with the
26/// [`set_buffer`] function. The buffer object attached to a sound must remain alive as long as
27/// the sound uses it. Note that multiple sounds can use the same sound buffer at the same time.
28///
29/// [`set_buffer`]: Sound::set_buffer
30///
31/// [`Music`]: crate::audio::Music
32#[derive(Debug)]
33pub struct Sound<'buf> {
34    handle: NonNull<ffi::audio::sfSound>,
35    buffer: PhantomData<&'buf SoundBuffer>,
36}
37
38// SAFETY: An `sfSound` isn't tied to a particular thread, so it can be sent between threads safely.
39unsafe impl Send for Sound<'_> {}
40
41// SAFETY: An `&Sound` only allows access to methods which read the status of the sound, which is
42// fine to do from multiple threads at once. Thus it is safe to pass `&Sound` between threads.
43unsafe impl Sync for Sound<'_> {}
44
45/// Creation
46impl<'buf> Sound<'buf> {
47    /// Create a new `Sound`
48    ///
49    /// # Panics
50    ///
51    /// Panics on allocation failure
52    #[must_use]
53    pub fn new() -> Self {
54        let s = unsafe { ffi::audio::sfSound_new() };
55        Sound {
56            handle: NonNull::new(s).expect("Failed to create Sound"),
57            buffer: PhantomData,
58        }
59    }
60
61    /// Create a new `Sound` with a buffer
62    #[must_use]
63    pub fn with_buffer(buffer: &'buf SoundBuffer) -> Self {
64        let mut s = Sound::new();
65        s.set_buffer(buffer);
66        s
67    }
68}
69
70/// Playback
71impl Sound<'_> {
72    /// Start or resume playing a sound
73    ///
74    /// This function starts the sound if it was stopped, resumes
75    /// it if it was paused, and restarts it from beginning if it
76    /// was it already playing.
77    /// This function uses its own thread so that it doesn't block
78    /// the rest of the program while the sound is played.
79    pub fn play(&mut self) {
80        unsafe { ffi::audio::sfSound_play(self.handle.as_ptr()) }
81    }
82
83    /// Pause a sound
84    ///
85    /// This function pauses the sound if it was playing,
86    /// otherwise (sound already paused or stopped) it has no effect.
87    pub fn pause(&mut self) {
88        unsafe { ffi::audio::sfSound_pause(self.handle.as_ptr()) }
89    }
90
91    /// Stop playing a sound
92    ///
93    /// This function stops the sound if it was playing or paused,
94    /// and does nothing if it was already stopped.
95    /// It also resets the playing position (unlike pause).
96    pub fn stop(&mut self) {
97        unsafe { ffi::audio::sfSound_stop(self.handle.as_ptr()) }
98    }
99}
100
101/// Query properties
102impl<'buf> Sound<'buf> {
103    /// Tell whether or not a sound is in loop mode
104    ///
105    /// Return true if the sound is looping, false otherwise
106    #[must_use]
107    pub fn is_looping(&self) -> bool {
108        unsafe { ffi::audio::sfSound_getLoop(self.handle.as_ptr()) }
109    }
110
111    /// Get the current status of a sound (stopped, paused, playing)
112    ///
113    /// Return current status
114    #[must_use]
115    pub fn status(&self) -> SoundStatus {
116        unsafe { SoundStatus(ffi::audio::sfSound_getStatus(self.handle.as_ptr())) }
117    }
118
119    /// Get the current playing position of a sound
120    ///
121    /// Return the current playing position
122    #[must_use]
123    pub fn playing_offset(&self) -> Time {
124        unsafe { Time::from_raw(ffi::audio::sfSound_getPlayingOffset(self.handle.as_ptr())) }
125    }
126    /// Get the audio buffer attached to a sound
127    ///
128    /// Return an option to Sound buffer attached to the sound or None
129    #[must_use]
130    pub fn buffer(&self) -> Option<&'buf SoundBuffer> {
131        unsafe { ffi::audio::sfSound_getBuffer(self.handle.as_ptr()).as_ref() }
132    }
133}
134
135/// Set properties
136impl<'buf> Sound<'buf> {
137    /// Sets whether this sound should loop or not.
138    pub fn set_looping(&mut self, looping: bool) {
139        unsafe { ffi::audio::sfSound_setLoop(self.handle.as_ptr(), looping) }
140    }
141
142    /// Change the current playing position of a sound
143    ///
144    /// The playing position can be changed when the sound is
145    /// either paused or playing.
146    ///
147    /// # Arguments
148    /// * timeOffset - New playing position
149    pub fn set_playing_offset(&mut self, time_offset: Time) {
150        unsafe { ffi::audio::sfSound_setPlayingOffset(self.handle.as_ptr(), time_offset.raw()) }
151    }
152
153    /// Set the source buffer containing the audio data to play
154    ///
155    /// # Arguments
156    /// * buffer - Sound buffer to attach to the sound
157    pub fn set_buffer(&mut self, buffer: &'buf SoundBuffer) {
158        unsafe { ffi::audio::sfSound_setBuffer(self.handle.as_ptr(), buffer) }
159    }
160}
161
162impl Default for Sound<'_> {
163    fn default() -> Self {
164        Self::new()
165    }
166}
167
168impl Clone for Sound<'_> {
169    fn clone(&self) -> Self {
170        let s = unsafe { ffi::audio::sfSound_cpy(self.handle.as_ptr()) };
171        Sound {
172            handle: NonNull::new(s).expect("Failed to copy Sound"),
173            buffer: self.buffer,
174        }
175    }
176}
177
178impl SoundSource for Sound<'_> {
179    fn set_pitch(&mut self, pitch: f32) {
180        unsafe { ffi::audio::sfSound_setPitch(self.handle.as_ptr(), pitch) }
181    }
182    fn set_volume(&mut self, volume: f32) {
183        unsafe { ffi::audio::sfSound_setVolume(self.handle.as_ptr(), volume) }
184    }
185    fn set_position<P: Into<Vector3f>>(&mut self, position: P) {
186        unsafe { ffi::audio::sfSound_setPosition(self.handle.as_ptr(), position.into()) }
187    }
188    fn set_relative_to_listener(&mut self, relative: bool) {
189        unsafe { ffi::audio::sfSound_setRelativeToListener(self.handle.as_ptr(), relative) }
190    }
191    fn set_min_distance(&mut self, distance: f32) {
192        unsafe { ffi::audio::sfSound_setMinDistance(self.handle.as_ptr(), distance) }
193    }
194    fn set_attenuation(&mut self, attenuation: f32) {
195        unsafe { ffi::audio::sfSound_setAttenuation(self.handle.as_ptr(), attenuation) }
196    }
197    fn pitch(&self) -> f32 {
198        unsafe { ffi::audio::sfSound_getPitch(self.handle.as_ptr()) }
199    }
200    fn volume(&self) -> f32 {
201        unsafe { ffi::audio::sfSound_getVolume(self.handle.as_ptr()) }
202    }
203    fn position(&self) -> Vector3f {
204        unsafe { ffi::audio::sfSound_getPosition(self.handle.as_ptr()) }
205    }
206    fn is_relative_to_listener(&self) -> bool {
207        unsafe { ffi::audio::sfSound_isRelativeToListener(self.handle.as_ptr()) }
208    }
209    fn min_distance(&self) -> f32 {
210        unsafe { ffi::audio::sfSound_getMinDistance(self.handle.as_ptr()) }
211    }
212    fn attenuation(&self) -> f32 {
213        unsafe { ffi::audio::sfSound_getAttenuation(self.handle.as_ptr()) }
214    }
215}
216
217impl Drop for Sound<'_> {
218    fn drop(&mut self) {
219        unsafe {
220            ffi::audio::sfSound_del(self.handle.as_ptr());
221        }
222    }
223}