sfml/audio/
sound_stream.rs

1use {
2    crate::{
3        audio::{SoundSource, SoundStatus},
4        ffi::audio::*,
5        system::{Time, Vector3f},
6    },
7    std::{marker::PhantomData, os::raw::c_void, panic, ptr::NonNull},
8};
9
10/// Trait for streamed audio sources.
11///
12/// The implementor must be able to be sent to another thread, so it must
13/// implement [`Send`].
14pub trait SoundStream: Send {
15    /// Request a new chunk of audio samples from the stream source.
16    ///
17    /// Returns `(chunk, keep_playing)`, where `chunk` is the chunk of audio samples,
18    /// and `keep_playing` tells the streaming loop whether to keep playing or to stop.
19    fn get_data(&mut self) -> (&[i16], bool);
20    /// Change the current playing position in the stream source.
21    fn seek(&mut self, offset: Time);
22    /// Return the number of channels of the stream.
23    fn channel_count(&self) -> u32;
24    /// Get the stream sample rate of the stream.
25    fn sample_rate(&self) -> u32;
26}
27
28/// Player for custom streamed audio sources. See [`SoundStream`].
29#[derive(Debug)]
30pub struct SoundStreamPlayer<'a, S: SoundStream + 'a> {
31    handle: NonNull<sfCustomSoundStream>,
32    /// We need to hold a raw pointer instead of a reference, since
33    /// we send it to another thread, violating `&mut` rules.
34    ///
35    /// Not sure if `NonNull` can be used to be honest. Not gonna risk it.
36    stream: *mut S,
37    _borrow: PhantomData<&'a mut S>,
38}
39
40unsafe extern "C" fn get_data_callback<S: SoundStream>(
41    chunk: *mut crate::ffi::audio::sfSoundStreamChunk,
42    user_data: *mut c_void,
43) -> bool {
44    let stream: *mut S = user_data.cast();
45    unsafe {
46        let (data, keep_playing) =
47            match panic::catch_unwind(panic::AssertUnwindSafe(|| (*stream).get_data())) {
48                Ok(ret) => ret,
49                Err(_) => {
50                    eprintln!("sound_stream: Stopping playback beacuse `get_data` panicked.");
51                    (&[][..], false)
52                }
53            };
54        (*chunk).samples = data.as_ptr();
55        (*chunk).sample_count = data.len();
56        keep_playing
57    }
58}
59
60unsafe extern "C" fn seek_callback<S: SoundStream>(
61    offset: crate::ffi::system::sfTime,
62    user_data: *mut c_void,
63) {
64    let stream: *mut S = user_data.cast();
65    let result = unsafe {
66        panic::catch_unwind(panic::AssertUnwindSafe(|| {
67            (*stream).seek(Time::from_raw(offset))
68        }))
69    };
70    if result.is_err() {
71        eprintln!("sound_stream: Failed to seek because `seek` panicked.");
72    }
73}
74
75impl<'a, S: SoundStream> SoundStreamPlayer<'a, S> {
76    /// Create a new `SoundStreamPlayer` with the specified [`SoundStream`].
77    ///
78    /// # Panics
79    ///
80    /// Panics if for some reason a `SoundStreamPlayer` can't be created.
81    pub fn new(sound_stream: &'a mut S) -> Self {
82        let channel_count = sound_stream.channel_count();
83        let sample_rate = sound_stream.sample_rate();
84        let sound_stream: *mut S = sound_stream;
85        Self {
86            handle: unsafe {
87                NonNull::new(sfCustomSoundStream_new(
88                    Some(get_data_callback::<S>),
89                    Some(seek_callback::<S>),
90                    channel_count,
91                    sample_rate,
92                    sound_stream.cast(),
93                ))
94                .expect("Failed to create SoundStreamPlayer")
95            },
96            stream: sound_stream,
97            _borrow: PhantomData,
98        }
99    }
100    /// Start or resume playing the audio stream.
101    pub fn play(&mut self) {
102        unsafe {
103            sfCustomSoundStream_play(self.handle.as_ptr());
104        }
105    }
106    /// Pause the audio stream.
107    ///
108    /// This function pauses the stream if it was playing,
109    /// otherwise (stream already paused or stopped) it has no effect.
110    pub fn pause(&mut self) {
111        unsafe {
112            sfCustomSoundStream_pause(self.handle.as_ptr());
113        }
114    }
115    /// Get the current status of the stream (stopped, paused, playing)
116    #[must_use]
117    pub fn status(&self) -> SoundStatus {
118        unsafe { SoundStatus(sfCustomSoundStream_getStatus(self.handle.as_ptr())) }
119    }
120    /// Stop playing, lending out the underlying [`SoundStream`].
121    ///
122    /// This function stops the stream if it was playing or paused, and does nothing if it was
123    /// already stopped. It also resets the playing position (unlike [`pause`]).
124    ///
125    /// [`pause`]: SoundStreamPlayer::pause
126    ///
127    /// It lends out the underlying `SoundStream`, allowing it to be manipulated.
128    pub fn stop(&mut self) -> &mut S {
129        unsafe {
130            sfCustomSoundStream_stop(self.handle.as_ptr());
131            &mut *self.stream
132        }
133    }
134    /// Get the current playing position, from the beginning of the stream
135    #[must_use]
136    pub fn playing_offset(&self) -> Time {
137        unsafe { Time::from_raw(sfCustomSoundStream_getPlayingOffset(self.handle.as_ptr())) }
138    }
139    /// Change the current playing position of the stream.
140    ///
141    /// The playing position can be changed when the stream is either paused or playing.
142    /// Changing the playing position when the stream is stopped has no effect,
143    /// since playing the stream would reset its position.
144    pub fn set_playing_offset(&mut self, offset: Time) {
145        unsafe { sfCustomSoundStream_setPlayingOffset(self.handle.as_ptr(), offset.raw()) }
146    }
147    /// Return the number of channels of the stream.
148    ///
149    /// 1 channel means a mono sound, 2 means stereo, etc.
150    #[must_use]
151    pub fn channel_count(&self) -> u32 {
152        unsafe { sfCustomSoundStream_getChannelCount(self.handle.as_ptr()) }
153    }
154    /// Get the stream sample rate of the stream.
155    ///
156    /// The sample rate is the number of audio samples played per second.
157    /// The higher, the better the quality.
158    #[must_use]
159    pub fn sample_rate(&self) -> u32 {
160        unsafe { sfCustomSoundStream_getSampleRate(self.handle.as_ptr()) }
161    }
162    /// Tell whether or not the stream is in loop mode.
163    #[must_use]
164    pub fn is_looping(&self) -> bool {
165        unsafe { sfCustomSoundStream_getLoop(self.handle.as_ptr()) }
166    }
167    /// Set whether or not the stream should loop after reaching the end.
168    ///
169    /// If set, the stream will restart from beginning after reaching the end and so on,
170    /// until it is stopped or `set_looping(false)` is called.
171    /// The default looping state for streams is false.
172    pub fn set_looping(&mut self, looping: bool) {
173        unsafe { sfCustomSoundStream_setLoop(self.handle.as_ptr(), looping) }
174    }
175}
176
177impl<S: SoundStream> SoundSource for SoundStreamPlayer<'_, S> {
178    fn set_pitch(&mut self, pitch: f32) {
179        unsafe { sfCustomSoundStream_setPitch(self.handle.as_ptr(), pitch) }
180    }
181    fn set_volume(&mut self, volume: f32) {
182        unsafe { sfCustomSoundStream_setVolume(self.handle.as_ptr(), volume) }
183    }
184    fn set_position<P: Into<Vector3f>>(&mut self, position: P) {
185        unsafe { sfCustomSoundStream_setPosition(self.handle.as_ptr(), position.into()) }
186    }
187    fn set_relative_to_listener(&mut self, relative: bool) {
188        unsafe { sfCustomSoundStream_setRelativeToListener(self.handle.as_ptr(), relative) }
189    }
190    fn set_min_distance(&mut self, distance: f32) {
191        unsafe { sfCustomSoundStream_setMinDistance(self.handle.as_ptr(), distance) }
192    }
193    fn set_attenuation(&mut self, attenuation: f32) {
194        unsafe { sfCustomSoundStream_setAttenuation(self.handle.as_ptr(), attenuation) }
195    }
196    fn pitch(&self) -> f32 {
197        unsafe { sfCustomSoundStream_getPitch(self.handle.as_ptr()) }
198    }
199    fn volume(&self) -> f32 {
200        unsafe { sfCustomSoundStream_getVolume(self.handle.as_ptr()) }
201    }
202    fn position(&self) -> Vector3f {
203        unsafe { sfCustomSoundStream_getPosition(self.handle.as_ptr()) }
204    }
205    fn is_relative_to_listener(&self) -> bool {
206        unsafe { sfCustomSoundStream_isRelativeToListener(self.handle.as_ptr()) }
207    }
208    fn min_distance(&self) -> f32 {
209        unsafe { sfCustomSoundStream_getMinDistance(self.handle.as_ptr()) }
210    }
211    fn attenuation(&self) -> f32 {
212        unsafe { sfCustomSoundStream_getAttenuation(self.handle.as_ptr()) }
213    }
214}
215
216impl<S: SoundStream> Drop for SoundStreamPlayer<'_, S> {
217    fn drop(&mut self) {
218        unsafe {
219            // It seems there can be problems (e.g. "pure virtual method called") if the
220            // stream is not stopped before it's destroyed. So let's make sure it's stopped.
221            sfCustomSoundStream_stop(self.handle.as_ptr());
222            sfCustomSoundStream_del(self.handle.as_ptr());
223        }
224    }
225}