rg3d_sound/source/
generic.rs

1//! Generic sound source.
2//!
3//! # Overview
4//!
5//! Generic sound source is base building block for each other types of sound sources. It holds state of buffer read
6//! cursor, information about panning, pitch, looping, etc. It performs automatic resampling on the fly.
7//!
8//! # Usage
9//!
10//! Generic sound source can be constructed using GenericSourceBuilder like this:
11//!
12//! ```no_run
13//! use std::sync::{Arc, Mutex};
14//! use rg3d_sound::buffer::SoundBufferResource;
15//! use rg3d_sound::pool::Handle;
16//! use rg3d_sound::source::{SoundSource, Status};
17//! use rg3d_sound::source::generic::GenericSourceBuilder;
18//! use rg3d_sound::context::SoundContext;
19//!
20//! fn make_source(context: &mut SoundContext, buffer: SoundBufferResource) -> Handle<SoundSource> {
21//!     let source = GenericSourceBuilder::new()
22//!        .with_buffer(buffer)
23//!        .with_status(Status::Playing)
24//!        .build_source()
25//!        .unwrap();
26//!     context.state().add_source(source)
27//! }
28//!
29//! ```
30
31#![allow(clippy::float_cmp)]
32
33use crate::{
34    buffer::{streaming::StreamingBuffer, SoundBufferResource, SoundBufferState},
35    error::SoundError,
36    source::{SoundSource, Status},
37};
38use rg3d_core::{
39    inspect::{Inspect, PropertyInfo},
40    visitor::{Visit, VisitResult, Visitor},
41};
42use rg3d_resource::ResourceState;
43use std::time::Duration;
44
45/// See module info.
46#[derive(Debug, Clone, Inspect)]
47pub struct GenericSource {
48    name: String,
49    buffer: Option<SoundBufferResource>,
50    // Read position in the buffer in samples. Differs from `playback_pos` if buffer is streaming.
51    // In case of streaming buffer its maximum value will be some fixed value which is
52    // implementation defined. It can be less than zero, this happens when we are in the process
53    // of reading next block in streaming buffer (see also prev_buffer_sample).
54    #[inspect(skip)]
55    buf_read_pos: f64,
56    // Real playback position in samples.
57    #[inspect(skip)]
58    playback_pos: f64,
59    #[inspect(min_value = 0.0, step = 0.05)]
60    panning: f32,
61    #[inspect(min_value = 0.0, step = 0.05)]
62    pitch: f64,
63    #[inspect(min_value = 0.0, step = 0.05)]
64    gain: f32,
65    looping: bool,
66    // Important coefficient for runtime resampling. It is used to modify playback speed
67    // of a source in order to match output device sampling rate. PCM data can be stored
68    // in various sampling rates (22050 Hz, 44100 Hz, 88200 Hz, etc.) but output device
69    // is running at fixed sampling rate (usually 44100 Hz). For example if we we'll feed
70    // data to device with rate of 22050 Hz but device is running at 44100 Hz then we'll
71    // hear that sound will have high pitch (2.0), to fix that we'll just pre-multiply
72    // playback speed by 0.5.
73    // However such auto-resampling has poor quality, but it is fast.
74    #[inspect(read_only)]
75    resampling_multiplier: f64,
76    status: Status,
77    play_once: bool,
78    // Here we use Option because when source is just created it has no info about it
79    // previous left and right channel gains. We can't set it to 1.0 for example
80    // because it would give incorrect results: a sound would just start as loud as it
81    // can be with no respect to real distance attenuation (or what else affects channel
82    // gain). So if these are None engine will set correct values first and only then it
83    // will start interpolation of gain.
84    #[inspect(skip)]
85    pub(in crate) last_left_gain: Option<f32>,
86    #[inspect(skip)]
87    pub(in crate) last_right_gain: Option<f32>,
88    #[inspect(skip)]
89    pub(in crate) frame_samples: Vec<(f32, f32)>,
90    // This sample is used when doing linear interpolation between two blocks of streaming buffer.
91    #[inspect(skip)]
92    prev_buffer_sample: (f32, f32),
93}
94
95impl Default for GenericSource {
96    fn default() -> Self {
97        Self {
98            name: Default::default(),
99            buffer: None,
100            buf_read_pos: 0.0,
101            playback_pos: 0.0,
102            panning: 0.0,
103            pitch: 1.0,
104            gain: 1.0,
105            looping: false,
106            resampling_multiplier: 1.0,
107            status: Status::Stopped,
108            play_once: false,
109            last_left_gain: None,
110            last_right_gain: None,
111            frame_samples: Default::default(),
112            prev_buffer_sample: (0.0, 0.0),
113        }
114    }
115}
116
117impl GenericSource {
118    /// Sets new name of the sound source.
119    pub fn set_name<N: AsRef<str>>(&mut self, name: N) {
120        self.name = name.as_ref().to_owned();
121    }
122
123    /// Returns the name of the sound source.
124    pub fn name(&self) -> &str {
125        &self.name
126    }
127
128    /// Returns the name of the sound source.
129    pub fn name_owned(&self) -> String {
130        self.name.to_owned()
131    }
132
133    /// Changes buffer of source. Returns old buffer. Source will continue playing from beginning, old
134    /// position will be discarded.
135    pub fn set_buffer(
136        &mut self,
137        buffer: Option<SoundBufferResource>,
138    ) -> Result<Option<SoundBufferResource>, SoundError> {
139        self.buf_read_pos = 0.0;
140        self.playback_pos = 0.0;
141
142        // If we already have streaming buffer assigned make sure to decrease use count
143        // so it can be reused later on if needed.
144        if let Some(buffer) = self.buffer.clone() {
145            if let SoundBufferState::Streaming(ref mut streaming) = *buffer.data_ref() {
146                streaming.use_count = streaming.use_count.saturating_sub(1);
147            }
148        }
149
150        if let Some(buffer) = buffer.clone() {
151            match *buffer.state() {
152                ResourceState::LoadError { .. } => return Err(SoundError::BufferFailedToLoad),
153                ResourceState::Ok(ref mut locked_buffer) => {
154                    // Check new buffer if streaming - it must not be used by anyone else.
155                    if let SoundBufferState::Streaming(ref mut streaming) = *locked_buffer {
156                        if streaming.use_count != 0 {
157                            return Err(SoundError::StreamingBufferAlreadyInUse);
158                        }
159                        streaming.use_count += 1;
160                    }
161
162                    // Make sure to recalculate resampling multiplier, otherwise sound will play incorrectly.
163                    let device_sample_rate = f64::from(crate::context::SAMPLE_RATE);
164                    let sample_rate = locked_buffer.sample_rate() as f64;
165                    self.resampling_multiplier = sample_rate / device_sample_rate;
166                }
167                ResourceState::Pending { .. } => unreachable!(),
168            }
169        }
170
171        Ok(std::mem::replace(&mut self.buffer, buffer))
172    }
173
174    /// Returns current buffer if any.
175    pub fn buffer(&self) -> Option<SoundBufferResource> {
176        self.buffer.clone()
177    }
178
179    /// Marks buffer for single play. It will be automatically destroyed when it will finish playing.
180    ///
181    /// # Notes
182    ///
183    /// Make sure you not using handles to "play once" sounds, attempt to get reference of "play once" sound
184    /// may result in panic if source already deleted. Looping sources will never be automatically deleted
185    /// because their playback never stops.
186    pub fn set_play_once(&mut self, play_once: bool) {
187        self.play_once = play_once;
188    }
189
190    /// Returns true if this source is marked for single play, false - otherwise.
191    pub fn is_play_once(&self) -> bool {
192        self.play_once
193    }
194
195    /// Sets new gain (volume) of sound. Value should be in 0..1 range, but it is not clamped
196    /// and larger values can be used to "overdrive" sound.
197    ///
198    /// # Notes
199    ///
200    /// Physical volume has non-linear scale (logarithmic) so perception of sound at 0.25 gain
201    /// will be different if logarithmic scale was used.
202    pub fn set_gain(&mut self, gain: f32) -> &mut Self {
203        self.gain = gain;
204        self
205    }
206
207    /// Returns current gain (volume) of sound. Value is in 0..1 range.
208    pub fn gain(&self) -> f32 {
209        self.gain
210    }
211
212    /// Sets panning coefficient. Value must be in -1..+1 range. Where -1 - only left channel will be audible,
213    /// 0 - both, +1 - only right.
214    pub fn set_panning(&mut self, panning: f32) -> &mut Self {
215        self.panning = panning.max(-1.0).min(1.0);
216        self
217    }
218
219    /// Returns current panning coefficient in -1..+1 range. For more info see `set_panning`. Default value is 0.
220    pub fn panning(&self) -> f32 {
221        self.panning
222    }
223
224    /// Returns status of sound source.
225    pub fn status(&self) -> Status {
226        self.status
227    }
228
229    /// Changes status to `Playing`.
230    pub fn play(&mut self) -> &mut Self {
231        self.status = Status::Playing;
232        self
233    }
234
235    /// Changes status to `Paused`
236    pub fn pause(&mut self) -> &mut Self {
237        self.status = Status::Paused;
238        self
239    }
240
241    /// Enabled or disables sound looping. Looping sound will never stop by itself, but can be stopped or paused
242    /// by calling `stop` or `pause` methods. Useful for music, ambient sounds, etc.
243    pub fn set_looping(&mut self, looping: bool) -> &mut Self {
244        self.looping = looping;
245        self
246    }
247
248    /// Returns looping status.
249    pub fn is_looping(&self) -> bool {
250        self.looping
251    }
252
253    /// Sets sound pitch. Defines "tone" of sounds. Default value is 1.0
254    pub fn set_pitch(&mut self, pitch: f64) -> &mut Self {
255        self.pitch = pitch.abs();
256        self
257    }
258
259    /// Returns pitch of sound source.
260    pub fn pitch(&self) -> f64 {
261        self.pitch
262    }
263
264    /// Stops sound source. Automatically rewinds streaming buffers.
265    pub fn stop(&mut self) -> Result<(), SoundError> {
266        self.status = Status::Stopped;
267
268        self.buf_read_pos = 0.0;
269        self.playback_pos = 0.0;
270
271        if let Some(buffer) = self.buffer.as_ref() {
272            let mut buffer = buffer.data_ref();
273            if let SoundBufferState::Streaming(ref mut streaming) = *buffer {
274                streaming.rewind()?;
275            }
276        }
277
278        Ok(())
279    }
280
281    /// Returns playback duration.
282    pub fn playback_time(&self) -> Duration {
283        if let Some(buffer) = self.buffer.as_ref() {
284            let buffer = buffer.data_ref();
285            Duration::from_secs_f64(self.playback_pos / (buffer.sample_rate() as f64))
286        } else {
287            Duration::from_secs(0)
288        }
289    }
290
291    /// Sets playback duration.
292    pub fn set_playback_time(&mut self, time: Duration) {
293        if let Some(buffer) = self.buffer.as_ref() {
294            let mut buffer = buffer.data_ref();
295            if let SoundBufferState::Streaming(ref mut streaming) = *buffer {
296                // Make sure decoder is at right position.
297                streaming.time_seek(time);
298            }
299            // Set absolute position first.
300            self.playback_pos = time.as_secs_f64() * buffer.sample_rate as f64;
301            // Then adjust buffer read position.
302            self.buf_read_pos = match *buffer {
303                SoundBufferState::Streaming(ref mut streaming) => {
304                    // Make sure to load correct data into buffer from decoder.
305                    streaming.read_next_block();
306                    // Streaming sources has different buffer read position because
307                    // buffer contains only small portion of data.
308                    self.playback_pos % (StreamingBuffer::STREAM_SAMPLE_COUNT as f64)
309                }
310                SoundBufferState::Generic(_) => self.playback_pos,
311            };
312            assert!(
313                self.buf_read_pos * (buffer.channel_count() as f64) < buffer.samples().len() as f64
314            );
315        }
316    }
317
318    pub(in crate) fn render(&mut self, amount: usize) {
319        if self.frame_samples.capacity() < amount {
320            self.frame_samples = Vec::with_capacity(amount);
321        }
322
323        self.frame_samples.clear();
324
325        if let Some(buffer) = self.buffer.clone() {
326            let mut state = buffer.state();
327            if let ResourceState::Ok(ref mut buffer) = *state {
328                if self.status == Status::Playing && !buffer.is_empty() {
329                    self.render_playing(buffer, amount);
330                }
331            }
332        }
333        // Fill the remaining part of frame_samples.
334        self.frame_samples.resize(amount, (0.0, 0.0));
335    }
336
337    fn render_playing(&mut self, buffer: &mut SoundBufferState, amount: usize) {
338        let mut count = 0;
339        loop {
340            count += self.render_until_block_end(buffer, amount - count);
341            if count == amount {
342                break;
343            }
344
345            let channel_count = buffer.channel_count();
346            let len = buffer.samples().len();
347            let mut end_reached = true;
348            if let SoundBufferState::Streaming(streaming) = buffer {
349                // Means that this is the last available block.
350                if len != channel_count * StreamingBuffer::STREAM_SAMPLE_COUNT {
351                    let _ = streaming.rewind();
352                } else {
353                    end_reached = false;
354                }
355                self.prev_buffer_sample = get_last_sample(streaming);
356                streaming.read_next_block();
357            }
358            if end_reached {
359                if !self.looping {
360                    self.status = Status::Stopped;
361                    return;
362                }
363                self.buf_read_pos = 0.0;
364                self.playback_pos = 0.0;
365            } else {
366                self.buf_read_pos -= len as f64 / channel_count as f64;
367            }
368        }
369    }
370
371    // Renders until the end of the block or until amount samples is written and returns
372    // the number of written samples.
373    fn render_until_block_end(
374        &mut self,
375        buffer: &mut SoundBufferState,
376        mut amount: usize,
377    ) -> usize {
378        let step = self.pitch * self.resampling_multiplier;
379        if step == 1.0 {
380            if self.buf_read_pos < 0.0 {
381                // This can theoretically happen if we change pitch on the fly.
382                self.frame_samples.push(self.prev_buffer_sample);
383                self.buf_read_pos = 0.0;
384                amount -= 1;
385            }
386            // Fast-path for common case when there is no resampling and no pitch change.
387            let from = self.buf_read_pos as usize;
388            let buffer_len = buffer.samples.len() / buffer.channel_count;
389            let rendered = (buffer_len - from).min(amount);
390            if buffer.channel_count == 2 {
391                for i in from..from + rendered {
392                    self.frame_samples
393                        .push((buffer.samples[i * 2], buffer.samples[i * 2 + 1]))
394                }
395            } else {
396                for i in from..from + rendered {
397                    self.frame_samples
398                        .push((buffer.samples[i], buffer.samples[i]))
399                }
400            }
401            self.buf_read_pos += rendered as f64;
402            self.playback_pos += rendered as f64;
403            rendered
404        } else {
405            self.render_until_block_end_resample(buffer, amount, step)
406        }
407    }
408
409    // Does linear resampling while rendering until the end of the block.
410    fn render_until_block_end_resample(
411        &mut self,
412        buffer: &mut SoundBufferState,
413        amount: usize,
414        step: f64,
415    ) -> usize {
416        let mut rendered = 0;
417
418        while self.buf_read_pos < 0.0 {
419            // Interpolate between last sample of previous buffer and first sample of current
420            // buffer. This is important, otherwise there will be quiet but audible pops
421            // in the output.
422            let w = (self.buf_read_pos - self.buf_read_pos.floor()) as f32;
423            let cur_first_sample = if buffer.channel_count == 2 {
424                (buffer.samples[0], buffer.samples[1])
425            } else {
426                (buffer.samples[0], buffer.samples[0])
427            };
428            let l = self.prev_buffer_sample.0 * (1.0 - w) + cur_first_sample.0 * w;
429            let r = self.prev_buffer_sample.1 * (1.0 - w) + cur_first_sample.1 * w;
430            self.frame_samples.push((l, r));
431            self.buf_read_pos += step;
432            self.playback_pos += step;
433            rendered += 1;
434        }
435
436        // We want to keep global positions in f64, but use f32 in inner loops (this improves
437        // code generation and performance at least on some systems), so we split the buf_read_pos
438        // into integer and f32 part.
439        let buffer_base_idx = self.buf_read_pos as usize;
440        let mut buffer_rel_pos = (self.buf_read_pos - buffer_base_idx as f64) as f32;
441        let start_buffer_rel_pos = buffer_rel_pos;
442        let rel_step = step as f32;
443        // We skip one last element because the hot loop resampling between current and next
444        // element. Last elements are appended after the hot loop.
445        let buffer_last = buffer.samples.len() / buffer.channel_count - 1;
446        if buffer.channel_count == 2 {
447            while rendered < amount {
448                let (idx, w) = {
449                    let idx = buffer_rel_pos as usize;
450                    // This looks a bit complicated but fract() is quite a bit slower on x86,
451                    // because it turns into a function call on targets < SSE4.1, unlike aarch64)
452                    (idx + buffer_base_idx, buffer_rel_pos - idx as f32)
453                };
454                if idx >= buffer_last {
455                    break;
456                }
457                let l = buffer.samples[idx * 2] * (1.0 - w) + buffer.samples[idx * 2 + 2] * w;
458                let r = buffer.samples[idx * 2 + 1] * (1.0 - w) + buffer.samples[idx * 2 + 3] * w;
459                self.frame_samples.push((l, r));
460                buffer_rel_pos += rel_step;
461                rendered += 1;
462            }
463        } else {
464            while rendered < amount {
465                let (idx, w) = {
466                    let idx = buffer_rel_pos as usize;
467                    // See comment above.
468                    (idx + buffer_base_idx, buffer_rel_pos - idx as f32)
469                };
470                if idx >= buffer_last {
471                    break;
472                }
473                let v = buffer.samples[idx] * (1.0 - w) + buffer.samples[idx + 1] * w;
474                self.frame_samples.push((v, v));
475                buffer_rel_pos += rel_step;
476                rendered += 1;
477            }
478        }
479
480        self.buf_read_pos += (buffer_rel_pos - start_buffer_rel_pos) as f64;
481        self.playback_pos += (buffer_rel_pos - start_buffer_rel_pos) as f64;
482        rendered
483    }
484
485    pub(in crate) fn frame_samples(&self) -> &[(f32, f32)] {
486        &self.frame_samples
487    }
488}
489
490fn get_last_sample(buffer: &StreamingBuffer) -> (f32, f32) {
491    let len = buffer.samples.len();
492    if len == 0 {
493        return (0.0, 0.0);
494    }
495    if buffer.channel_count == 2 {
496        (buffer.samples[len - 2], buffer.samples[len - 1])
497    } else {
498        (buffer.samples[len - 1], buffer.samples[len - 1])
499    }
500}
501
502impl Drop for GenericSource {
503    fn drop(&mut self) {
504        if let Some(buffer) = self.buffer.as_ref() {
505            let mut buffer = buffer.data_ref();
506            if let SoundBufferState::Streaming(ref mut streaming) = *buffer {
507                streaming.use_count = streaming.use_count.saturating_sub(1);
508            }
509        }
510    }
511}
512
513impl Visit for GenericSource {
514    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
515        visitor.enter_region(name)?;
516
517        let _ = self.name.visit("Name", visitor);
518        self.buffer.visit("Buffer", visitor)?;
519        self.buf_read_pos.visit("BufReadPos", visitor)?;
520        self.playback_pos.visit("PlaybackPos", visitor)?;
521        self.panning.visit("Pan", visitor)?;
522        self.pitch.visit("Pitch", visitor)?;
523        self.gain.visit("Gain", visitor)?;
524        self.looping.visit("Looping", visitor)?;
525        self.resampling_multiplier
526            .visit("ResamplingMultiplier", visitor)?;
527        self.status.visit("Status", visitor)?;
528        self.play_once.visit("PlayOnce", visitor)?;
529
530        visitor.leave_region()
531    }
532}
533
534/// Allows you to construct generic sound source with desired state.
535///
536/// # Usage
537///
538/// ```no_run
539/// use std::sync::{Arc, Mutex};
540/// use rg3d_sound::buffer::SoundBufferResource;
541/// use rg3d_sound::source::generic::{GenericSource, GenericSourceBuilder};
542/// use rg3d_sound::source::{Status, SoundSource};
543///
544/// fn make_generic_source(buffer: SoundBufferResource) -> GenericSource {
545///     GenericSourceBuilder::new()
546///         .with_buffer(buffer)
547///         .with_status(Status::Playing)
548///         .with_gain(0.5)
549///         .with_looping(true)
550///         .with_pitch(1.25)
551///         .build()
552///         .unwrap()
553/// }
554///
555/// fn make_source(buffer: SoundBufferResource) -> SoundSource {
556///     GenericSourceBuilder::new()
557///         .with_buffer(buffer)
558///         .with_status(Status::Playing)
559///         .with_gain(0.5)
560///         .with_looping(true)
561///         .with_pitch(1.25)
562///         .build_source() // build_source creates SoundSource::Generic directly
563///         .unwrap()
564/// }
565/// ```
566pub struct GenericSourceBuilder {
567    buffer: Option<SoundBufferResource>,
568    gain: f32,
569    pitch: f32,
570    name: String,
571    panning: f32,
572    looping: bool,
573    status: Status,
574    play_once: bool,
575}
576
577impl Default for GenericSourceBuilder {
578    fn default() -> Self {
579        Self::new()
580    }
581}
582
583impl GenericSourceBuilder {
584    /// Creates new generic source builder with specified buffer.
585    pub fn new() -> Self {
586        Self {
587            buffer: None,
588            gain: 1.0,
589            pitch: 1.0,
590            name: Default::default(),
591            panning: 0.0,
592            looping: false,
593            status: Status::Stopped,
594            play_once: false,
595        }
596    }
597
598    /// Sets desired sound buffer to play.
599    pub fn with_buffer(mut self, buffer: SoundBufferResource) -> Self {
600        self.buffer = Some(buffer);
601        self
602    }
603
604    /// See `set_gain` of GenericSource
605    pub fn with_gain(mut self, gain: f32) -> Self {
606        self.gain = gain;
607        self
608    }
609
610    /// See `set_pitch` of GenericSource
611    pub fn with_pitch(mut self, pitch: f32) -> Self {
612        self.pitch = pitch;
613        self
614    }
615
616    /// See `set_panning` of GenericSource
617    pub fn with_panning(mut self, panning: f32) -> Self {
618        self.panning = panning;
619        self
620    }
621
622    /// See `set_looping` of GenericSource
623    pub fn with_looping(mut self, looping: bool) -> Self {
624        self.looping = looping;
625        self
626    }
627
628    /// Sets desired status of source.
629    pub fn with_status(mut self, status: Status) -> Self {
630        self.status = status;
631        self
632    }
633
634    /// See `set_play_once` of GenericSource
635    pub fn with_play_once(mut self, play_once: bool) -> Self {
636        self.play_once = play_once;
637        self
638    }
639
640    /// Sets desired name of the source.
641    pub fn with_name<N: AsRef<str>>(mut self, name: N) -> Self {
642        self.name = name.as_ref().to_owned();
643        self
644    }
645
646    /// Creates new instance of generic sound source. May fail if buffer is invalid.
647    pub fn build(self) -> Result<GenericSource, SoundError> {
648        let mut source = GenericSource {
649            buffer: self.buffer.clone(),
650            gain: self.gain,
651            pitch: self.pitch as f64,
652            play_once: self.play_once,
653            panning: self.panning,
654            status: self.status,
655            looping: self.looping,
656            name: self.name,
657            frame_samples: Default::default(),
658            ..Default::default()
659        };
660
661        source.set_buffer(self.buffer)?;
662
663        Ok(source)
664    }
665
666    /// Creates new instance of sound source of `Generic` variant.
667    pub fn build_source(self) -> Result<SoundSource, SoundError> {
668        Ok(SoundSource::Generic(self.build()?))
669    }
670}