fyrox_sound/
source.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Generic sound source.
22//!
23//! # Overview
24//!
25//! Sound source is responsible for sound playback.
26//!
27//! # Usage
28//!
29//! Generic sound source can be constructed using GenericSourceBuilder like this:
30//!
31//! ```no_run
32//! use std::sync::{Arc, Mutex};
33//! use fyrox_sound::buffer::SoundBufferResource;
34//! use fyrox_sound::pool::Handle;
35//! use fyrox_sound::source::{SoundSource, Status};
36//! use fyrox_sound::source::SoundSourceBuilder;
37//! use fyrox_sound::context::SoundContext;
38//!
39//! fn make_source(context: &mut SoundContext, buffer: SoundBufferResource) -> Handle<SoundSource> {
40//!     let source = SoundSourceBuilder::new()
41//!        .with_buffer(buffer)
42//!        .with_status(Status::Playing)
43//!        .build()
44//!        .unwrap();
45//!     context.state().add_source(source)
46//! }
47//! ```
48
49#![allow(clippy::float_cmp)]
50
51use crate::{
52    buffer::{streaming::StreamingBuffer, SoundBuffer, SoundBufferResource},
53    bus::AudioBusGraph,
54    context::DistanceModel,
55    error::SoundError,
56    listener::Listener,
57};
58use fyrox_core::{
59    algebra::Vector3,
60    reflect::prelude::*,
61    uuid_provider,
62    visitor::{Visit, VisitResult, Visitor},
63};
64use std::time::Duration;
65
66/// Status (state) of sound source.
67#[derive(Eq, PartialEq, Copy, Clone, Debug, Reflect, Visit)]
68#[repr(u32)]
69pub enum Status {
70    /// Sound is stopped - it won't produces any sample and won't load mixer. This is default
71    /// state of all sound sources.
72    Stopped = 0,
73
74    /// Sound is playing.
75    Playing = 1,
76
77    /// Sound is paused, it can stay in this state any amount if time. Playback can be continued by
78    /// setting `Playing` status.
79    Paused = 2,
80}
81
82uuid_provider!(Status = "1980bded-86cd-4eff-a5db-bab729bdb3ad");
83
84/// See module info.
85#[derive(Debug, Clone, Reflect, Visit)]
86pub struct SoundSource {
87    name: String,
88    #[reflect(hidden)]
89    buffer: Option<SoundBufferResource>,
90    // Read position in the buffer in samples. Differs from `playback_pos` if buffer is streaming.
91    // In case of streaming buffer its maximum value will be some fixed value which is
92    // implementation defined. It can be less than zero, this happens when we are in the process
93    // of reading next block in streaming buffer (see also prev_buffer_sample).
94    #[reflect(hidden)]
95    buf_read_pos: f64,
96    // Real playback position in samples.
97    #[reflect(hidden)]
98    playback_pos: f64,
99    #[reflect(min_value = 0.0, step = 0.05)]
100    panning: f32,
101    #[reflect(min_value = 0.0, step = 0.05)]
102    pitch: f64,
103    #[reflect(min_value = 0.0, step = 0.05)]
104    gain: f32,
105    looping: bool,
106    #[reflect(min_value = 0.0, max_value = 1.0, step = 0.05)]
107    spatial_blend: f32,
108    // Important coefficient for runtime resampling. It is used to modify playback speed
109    // of a source in order to match output device sampling rate. PCM data can be stored
110    // in various sampling rates (22050 Hz, 44100 Hz, 88200 Hz, etc.) but output device
111    // is running at fixed sampling rate (usually 44100 Hz). For example if we we'll feed
112    // data to device with rate of 22050 Hz but device is running at 44100 Hz then we'll
113    // hear that sound will have high pitch (2.0), to fix that we'll just pre-multiply
114    // playback speed by 0.5.
115    // However such auto-resampling has poor quality, but it is fast.
116    #[reflect(read_only)]
117    resampling_multiplier: f64,
118    status: Status,
119    #[visit(optional)]
120    pub(crate) bus: String,
121    play_once: bool,
122    // Here we use Option because when source is just created it has no info about it
123    // previous left and right channel gains. We can't set it to 1.0 for example
124    // because it would give incorrect results: a sound would just start as loud as it
125    // can be with no respect to real distance attenuation (or what else affects channel
126    // gain). So if these are None engine will set correct values first and only then it
127    // will start interpolation of gain.
128    #[reflect(hidden)]
129    #[visit(skip)]
130    pub(crate) last_left_gain: Option<f32>,
131    #[reflect(hidden)]
132    #[visit(skip)]
133    pub(crate) last_right_gain: Option<f32>,
134    #[reflect(hidden)]
135    #[visit(skip)]
136    pub(crate) frame_samples: Vec<(f32, f32)>,
137    // This sample is used when doing linear interpolation between two blocks of streaming buffer.
138    #[reflect(hidden)]
139    #[visit(skip)]
140    prev_buffer_sample: (f32, f32),
141    #[reflect(min_value = 0.0, step = 0.05)]
142    radius: f32,
143    position: Vector3<f32>,
144    #[reflect(min_value = 0.0, step = 0.05)]
145    max_distance: f32,
146    #[reflect(min_value = 0.0, step = 0.05)]
147    rolloff_factor: f32,
148    // Some data that needed for iterative overlap-save convolution.
149    #[reflect(hidden)]
150    #[visit(skip)]
151    pub(crate) prev_left_samples: Vec<f32>,
152    #[reflect(hidden)]
153    #[visit(skip)]
154    pub(crate) prev_right_samples: Vec<f32>,
155    #[reflect(hidden)]
156    #[visit(skip)]
157    pub(crate) prev_sampling_vector: Vector3<f32>,
158    #[reflect(hidden)]
159    #[visit(skip)]
160    pub(crate) prev_distance_gain: Option<f32>,
161}
162
163impl Default for SoundSource {
164    fn default() -> Self {
165        Self {
166            name: Default::default(),
167            buffer: None,
168            buf_read_pos: 0.0,
169            playback_pos: 0.0,
170            panning: 0.0,
171            pitch: 1.0,
172            gain: 1.0,
173            spatial_blend: 1.0,
174            looping: false,
175            resampling_multiplier: 1.0,
176            status: Status::Stopped,
177            bus: "Master".to_string(),
178            play_once: false,
179            last_left_gain: None,
180            last_right_gain: None,
181            frame_samples: Default::default(),
182            prev_buffer_sample: (0.0, 0.0),
183            radius: 1.0,
184            position: Vector3::new(0.0, 0.0, 0.0),
185            max_distance: f32::MAX,
186            rolloff_factor: 1.0,
187            prev_left_samples: Default::default(),
188            prev_right_samples: Default::default(),
189            prev_sampling_vector: Vector3::new(0.0, 0.0, 1.0),
190            prev_distance_gain: None,
191        }
192    }
193}
194
195impl SoundSource {
196    /// Sets new name of the sound source.
197    pub fn set_name<N: AsRef<str>>(&mut self, name: N) {
198        name.as_ref().clone_into(&mut self.name);
199    }
200
201    /// Returns the name of the sound source.
202    pub fn name(&self) -> &str {
203        &self.name
204    }
205
206    /// Returns the name of the sound source.
207    pub fn name_owned(&self) -> String {
208        self.name.to_owned()
209    }
210
211    /// Sets spatial blend factor. It defines how much the source will be 2D and 3D sound at the same
212    /// time. Set it to 0.0 to make the sound fully 2D and 1.0 to make it fully 3D. Middle values
213    /// will make sound proportionally 2D and 3D at the same time.
214    pub fn set_spatial_blend(&mut self, k: f32) {
215        self.spatial_blend = k.clamp(0.0, 1.0);
216    }
217
218    /// Returns spatial blend factor.
219    pub fn spatial_blend(&self) -> f32 {
220        self.spatial_blend
221    }
222
223    /// Changes buffer of source. Returns old buffer. Source will continue playing from beginning, old
224    /// position will be discarded.
225    pub fn set_buffer(
226        &mut self,
227        buffer: Option<SoundBufferResource>,
228    ) -> Result<Option<SoundBufferResource>, SoundError> {
229        self.buf_read_pos = 0.0;
230        self.playback_pos = 0.0;
231
232        // If we already have streaming buffer assigned make sure to decrease use count
233        // so it can be reused later on if needed.
234        if let Some(buffer) = self.buffer.clone() {
235            if let Some(SoundBuffer::Streaming(streaming)) = buffer.state().data() {
236                streaming.use_count = streaming.use_count.saturating_sub(1);
237            }
238        }
239
240        if let Some(buffer) = buffer.clone() {
241            match buffer.state().data() {
242                None => return Err(SoundError::BufferFailedToLoad),
243                Some(locked_buffer) => {
244                    // Check new buffer if streaming - it must not be used by anyone else.
245                    if let SoundBuffer::Streaming(ref mut streaming) = *locked_buffer {
246                        if streaming.use_count != 0 {
247                            return Err(SoundError::StreamingBufferAlreadyInUse);
248                        }
249                        streaming.use_count += 1;
250                    }
251
252                    // Make sure to recalculate resampling multiplier, otherwise sound will play incorrectly.
253                    let device_sample_rate = f64::from(crate::context::SAMPLE_RATE);
254                    let sample_rate = locked_buffer.sample_rate() as f64;
255                    self.resampling_multiplier = sample_rate / device_sample_rate;
256                }
257            }
258        }
259
260        Ok(std::mem::replace(&mut self.buffer, buffer))
261    }
262
263    /// Returns current buffer if any.
264    pub fn buffer(&self) -> Option<SoundBufferResource> {
265        self.buffer.clone()
266    }
267
268    /// Marks buffer for single play. It will be automatically destroyed when it will finish playing.
269    ///
270    /// # Notes
271    ///
272    /// Make sure you not using handles to "play once" sounds, attempt to get reference of "play once" sound
273    /// may result in panic if source already deleted. Looping sources will never be automatically deleted
274    /// because their playback never stops.
275    pub fn set_play_once(&mut self, play_once: bool) {
276        self.play_once = play_once;
277    }
278
279    /// Returns true if this source is marked for single play, false - otherwise.
280    pub fn is_play_once(&self) -> bool {
281        self.play_once
282    }
283
284    /// Sets new gain (volume) of sound. Value should be in 0..1 range, but it is not clamped
285    /// and larger values can be used to "overdrive" sound.
286    ///
287    /// # Notes
288    ///
289    /// Physical volume has non-linear scale (logarithmic) so perception of sound at 0.25 gain
290    /// will be different if logarithmic scale was used.
291    pub fn set_gain(&mut self, gain: f32) -> &mut Self {
292        self.gain = gain;
293        self
294    }
295
296    /// Returns current gain (volume) of sound. Value is in 0..1 range.
297    pub fn gain(&self) -> f32 {
298        self.gain
299    }
300
301    /// Sets panning coefficient. Value must be in -1..+1 range. Where -1 - only left channel will be audible,
302    /// 0 - both, +1 - only right.
303    pub fn set_panning(&mut self, panning: f32) -> &mut Self {
304        self.panning = panning.clamp(-1.0, 1.0);
305        self
306    }
307
308    /// Returns current panning coefficient in -1..+1 range. For more info see `set_panning`. Default value is 0.
309    pub fn panning(&self) -> f32 {
310        self.panning
311    }
312
313    /// Returns status of sound source.
314    pub fn status(&self) -> Status {
315        self.status
316    }
317
318    /// Changes status to `Playing`.
319    pub fn play(&mut self) -> &mut Self {
320        self.status = Status::Playing;
321        self
322    }
323
324    /// Changes status to `Paused`
325    pub fn pause(&mut self) -> &mut Self {
326        self.status = Status::Paused;
327        self
328    }
329
330    /// Enabled or disables sound looping. Looping sound will never stop by itself, but can be stopped or paused
331    /// by calling `stop` or `pause` methods. Useful for music, ambient sounds, etc.
332    pub fn set_looping(&mut self, looping: bool) -> &mut Self {
333        self.looping = looping;
334        self
335    }
336
337    /// Returns looping status.
338    pub fn is_looping(&self) -> bool {
339        self.looping
340    }
341
342    /// Sets sound pitch. Defines "tone" of sounds. Default value is 1.0
343    pub fn set_pitch(&mut self, pitch: f64) -> &mut Self {
344        self.pitch = pitch.abs();
345        self
346    }
347
348    /// Returns pitch of sound source.
349    pub fn pitch(&self) -> f64 {
350        self.pitch
351    }
352
353    /// Stops sound source. Automatically rewinds streaming buffers.
354    pub fn stop(&mut self) -> Result<(), SoundError> {
355        self.status = Status::Stopped;
356
357        self.buf_read_pos = 0.0;
358        self.playback_pos = 0.0;
359
360        if let Some(buffer) = self.buffer.as_ref() {
361            if let Some(SoundBuffer::Streaming(streaming)) = buffer.state().data() {
362                streaming.rewind()?;
363            }
364        }
365
366        Ok(())
367    }
368    /// Sets position of source in world space.
369    pub fn set_position(&mut self, position: Vector3<f32>) -> &mut Self {
370        self.position = position;
371        self
372    }
373
374    /// Returns positions of source.
375    pub fn position(&self) -> Vector3<f32> {
376        self.position
377    }
378
379    /// Sets radius of imaginable sphere around source in which no distance attenuation is applied.
380    pub fn set_radius(&mut self, radius: f32) -> &mut Self {
381        self.radius = radius;
382        self
383    }
384
385    /// Returns radius of source.
386    pub fn radius(&self) -> f32 {
387        self.radius
388    }
389
390    /// Sets rolloff factor. Rolloff factor is used in distance attenuation and has different meaning
391    /// in various distance models. It is applicable only for InverseDistance and ExponentDistance
392    /// distance models. See DistanceModel docs for formulae.
393    pub fn set_rolloff_factor(&mut self, rolloff_factor: f32) -> &mut Self {
394        self.rolloff_factor = rolloff_factor;
395        self
396    }
397
398    /// Returns rolloff factor.
399    pub fn rolloff_factor(&self) -> f32 {
400        self.rolloff_factor
401    }
402
403    /// Sets maximum distance until which distance gain will be applicable. Basically it doing this
404    /// min(max(distance, radius), max_distance) which clamps distance in radius..max_distance range.
405    /// From listener's perspective this will sound like source has stopped decreasing its volume even
406    /// if distance continue to grow.
407    pub fn set_max_distance(&mut self, max_distance: f32) -> &mut Self {
408        self.max_distance = max_distance;
409        self
410    }
411
412    /// Returns max distance.
413    pub fn max_distance(&self) -> f32 {
414        self.max_distance
415    }
416
417    /// Sets new name of the target audio bus. The name must be valid, otherwise the sound won't play!
418    /// Default is [`AudioBusGraph::PRIMARY_BUS`].
419    pub fn set_bus<S: AsRef<str>>(&mut self, bus: S) {
420        bus.as_ref().clone_into(&mut self.bus);
421    }
422
423    /// Return the name of the target audio bus.
424    pub fn bus(&self) -> &str {
425        &self.bus
426    }
427
428    // Distance models were taken from OpenAL Specification because it looks like they're
429    // standard in industry and there is no need to reinvent it.
430    // https://www.openal.org/documentation/openal-1.1-specification.pdf
431    pub(crate) fn calculate_distance_gain(
432        &self,
433        listener: &Listener,
434        distance_model: DistanceModel,
435    ) -> f32 {
436        let distance = self
437            .position
438            .metric_distance(&listener.position())
439            .clamp(self.radius, self.max_distance);
440        match distance_model {
441            DistanceModel::None => 1.0,
442            DistanceModel::InverseDistance => {
443                self.radius / (self.radius + self.rolloff_factor * (distance - self.radius))
444            }
445            DistanceModel::LinearDistance => {
446                1.0 - self.radius * (distance - self.radius) / (self.max_distance - self.radius)
447            }
448            DistanceModel::ExponentDistance => (distance / self.radius).powf(-self.rolloff_factor),
449        }
450    }
451
452    pub(crate) fn calculate_panning(&self, listener: &Listener) -> f32 {
453        (listener.position() - self.position)
454            .try_normalize(f32::EPSILON)
455            // Fallback to look axis will give zero panning which will result in even
456            // gain in each channels (as if there was no panning at all).
457            .unwrap_or_else(|| listener.look_axis())
458            .dot(&listener.ear_axis())
459    }
460
461    pub(crate) fn calculate_sampling_vector(&self, listener: &Listener) -> Vector3<f32> {
462        let to_self = listener.position() - self.position;
463
464        (listener.basis() * to_self)
465            .try_normalize(f32::EPSILON)
466            // This is ok to fallback to (0, 0, 1) vector because it's given
467            // in listener coordinate system.
468            .unwrap_or_else(|| Vector3::new(0.0, 0.0, 1.0))
469    }
470
471    /// Returns playback duration.
472    pub fn playback_time(&self) -> Duration {
473        if let Some(buffer) = self.buffer.as_ref() {
474            if let Some(buffer) = buffer.state().data() {
475                return Duration::from_secs_f64(self.playback_pos / (buffer.sample_rate() as f64));
476            }
477        }
478
479        Duration::from_secs(0)
480    }
481
482    /// Sets playback duration.
483    pub fn set_playback_time(&mut self, time: Duration) {
484        if let Some(buffer) = self.buffer.as_ref() {
485            if let Some(buffer) = buffer.state().data() {
486                if let SoundBuffer::Streaming(ref mut streaming) = *buffer {
487                    // Make sure decoder is at right position.
488                    streaming.time_seek(time.clamp(Duration::from_secs(0), streaming.duration()));
489                }
490                // Set absolute position first.
491                self.playback_pos = (time.as_secs_f64() * buffer.sample_rate as f64)
492                    .clamp(0.0, buffer.duration().as_secs_f64());
493                // Then adjust buffer read position.
494                self.buf_read_pos = match *buffer {
495                    SoundBuffer::Streaming(ref mut streaming) => {
496                        // Make sure to load correct data into buffer from decoder.
497                        streaming.read_next_block();
498                        // Streaming sources has different buffer read position because
499                        // buffer contains only small portion of data.
500                        self.playback_pos % (StreamingBuffer::STREAM_SAMPLE_COUNT as f64)
501                    }
502                    SoundBuffer::Generic(_) => self.playback_pos,
503                };
504                assert!(
505                    self.buf_read_pos * (buffer.channel_count() as f64)
506                        < buffer.samples().len() as f64
507                );
508            }
509        }
510    }
511
512    pub(crate) fn render(&mut self, amount: usize) {
513        if self.frame_samples.capacity() < amount {
514            self.frame_samples = Vec::with_capacity(amount);
515        }
516
517        self.frame_samples.clear();
518
519        if let Some(buffer) = self.buffer.clone() {
520            let mut state = buffer.state();
521            if let Some(buffer) = state.data() {
522                if self.status == Status::Playing && !buffer.is_empty() {
523                    self.render_playing(buffer, amount);
524                }
525            }
526        }
527        // Fill the remaining part of frame_samples.
528        self.frame_samples.resize(amount, (0.0, 0.0));
529    }
530
531    fn render_playing(&mut self, buffer: &mut SoundBuffer, amount: usize) {
532        let mut count = 0;
533        loop {
534            count += self.render_until_block_end(buffer, amount - count);
535            if count == amount {
536                break;
537            }
538
539            let channel_count = buffer.channel_count();
540            let len = buffer.samples().len();
541            let mut end_reached = true;
542            if let SoundBuffer::Streaming(streaming) = buffer {
543                // Means that this is the last available block.
544                if len != channel_count * StreamingBuffer::STREAM_SAMPLE_COUNT {
545                    let _ = streaming.rewind();
546                } else {
547                    end_reached = false;
548                }
549                self.prev_buffer_sample = get_last_sample(streaming);
550                streaming.read_next_block();
551            }
552            if end_reached {
553                self.buf_read_pos = 0.0;
554                self.playback_pos = 0.0;
555                if !self.looping {
556                    self.status = Status::Stopped;
557                    return;
558                }
559            } else {
560                self.buf_read_pos -= len as f64 / channel_count as f64;
561            }
562        }
563    }
564
565    // Renders until the end of the block or until amount samples is written and returns
566    // the number of written samples.
567    fn render_until_block_end(&mut self, buffer: &mut SoundBuffer, mut amount: usize) -> usize {
568        let step = self.pitch * self.resampling_multiplier;
569        if step == 1.0 {
570            if self.buf_read_pos < 0.0 {
571                // This can theoretically happen if we change pitch on the fly.
572                self.frame_samples.push(self.prev_buffer_sample);
573                self.buf_read_pos = 0.0;
574                amount -= 1;
575            }
576            // Fast-path for common case when there is no resampling and no pitch change.
577            let from = self.buf_read_pos as usize;
578            let buffer_len = buffer.samples.len() / buffer.channel_count;
579            let rendered = (buffer_len - from).min(amount);
580            if buffer.channel_count == 2 {
581                for i in from..from + rendered {
582                    self.frame_samples
583                        .push((buffer.samples[i * 2], buffer.samples[i * 2 + 1]))
584                }
585            } else {
586                for i in from..from + rendered {
587                    self.frame_samples
588                        .push((buffer.samples[i], buffer.samples[i]))
589                }
590            }
591            self.buf_read_pos += rendered as f64;
592            self.playback_pos += rendered as f64;
593            rendered
594        } else {
595            self.render_until_block_end_resample(buffer, amount, step)
596        }
597    }
598
599    // Does linear resampling while rendering until the end of the block.
600    fn render_until_block_end_resample(
601        &mut self,
602        buffer: &mut SoundBuffer,
603        amount: usize,
604        step: f64,
605    ) -> usize {
606        let mut rendered = 0;
607
608        while self.buf_read_pos < 0.0 {
609            // Interpolate between last sample of previous buffer and first sample of current
610            // buffer. This is important, otherwise there will be quiet but audible pops
611            // in the output.
612            let w = (self.buf_read_pos - self.buf_read_pos.floor()) as f32;
613            let cur_first_sample = if buffer.channel_count == 2 {
614                (buffer.samples[0], buffer.samples[1])
615            } else {
616                (buffer.samples[0], buffer.samples[0])
617            };
618            let l = self.prev_buffer_sample.0 * (1.0 - w) + cur_first_sample.0 * w;
619            let r = self.prev_buffer_sample.1 * (1.0 - w) + cur_first_sample.1 * w;
620            self.frame_samples.push((l, r));
621            self.buf_read_pos += step;
622            self.playback_pos += step;
623            rendered += 1;
624        }
625
626        // We want to keep global positions in f64, but use f32 in inner loops (this improves
627        // code generation and performance at least on some systems), so we split the buf_read_pos
628        // into integer and f32 part.
629        let buffer_base_idx = self.buf_read_pos as usize;
630        let mut buffer_rel_pos = (self.buf_read_pos - buffer_base_idx as f64) as f32;
631        let start_buffer_rel_pos = buffer_rel_pos;
632        let rel_step = step as f32;
633        // We skip one last element because the hot loop resampling between current and next
634        // element. Last elements are appended after the hot loop.
635        let buffer_last = buffer.samples.len() / buffer.channel_count - 1;
636        if buffer.channel_count == 2 {
637            while rendered < amount {
638                let (idx, w) = {
639                    let idx = buffer_rel_pos as usize;
640                    // This looks a bit complicated but fract() is quite a bit slower on x86,
641                    // because it turns into a function call on targets < SSE4.1, unlike aarch64)
642                    (idx + buffer_base_idx, buffer_rel_pos - idx as f32)
643                };
644                if idx >= buffer_last {
645                    break;
646                }
647                let l = buffer.samples[idx * 2] * (1.0 - w) + buffer.samples[idx * 2 + 2] * w;
648                let r = buffer.samples[idx * 2 + 1] * (1.0 - w) + buffer.samples[idx * 2 + 3] * w;
649                self.frame_samples.push((l, r));
650                buffer_rel_pos += rel_step;
651                rendered += 1;
652            }
653        } else {
654            while rendered < amount {
655                let (idx, w) = {
656                    let idx = buffer_rel_pos as usize;
657                    // See comment above.
658                    (idx + buffer_base_idx, buffer_rel_pos - idx as f32)
659                };
660                if idx >= buffer_last {
661                    break;
662                }
663                let v = buffer.samples[idx] * (1.0 - w) + buffer.samples[idx + 1] * w;
664                self.frame_samples.push((v, v));
665                buffer_rel_pos += rel_step;
666                rendered += 1;
667            }
668        }
669
670        self.buf_read_pos += (buffer_rel_pos - start_buffer_rel_pos) as f64;
671        self.playback_pos += (buffer_rel_pos - start_buffer_rel_pos) as f64;
672        rendered
673    }
674
675    pub(crate) fn frame_samples(&self) -> &[(f32, f32)] {
676        &self.frame_samples
677    }
678}
679
680fn get_last_sample(buffer: &StreamingBuffer) -> (f32, f32) {
681    let len = buffer.samples.len();
682    if len == 0 {
683        return (0.0, 0.0);
684    }
685    if buffer.channel_count == 2 {
686        (buffer.samples[len - 2], buffer.samples[len - 1])
687    } else {
688        (buffer.samples[len - 1], buffer.samples[len - 1])
689    }
690}
691
692impl Drop for SoundSource {
693    fn drop(&mut self) {
694        if let Some(buffer) = self.buffer.as_ref() {
695            if let Some(SoundBuffer::Streaming(streaming)) = buffer.state().data() {
696                streaming.use_count = streaming.use_count.saturating_sub(1);
697            }
698        }
699    }
700}
701
702/// Allows you to construct generic sound source with desired state.
703///
704/// # Usage
705///
706/// ```no_run
707/// use std::sync::{Arc, Mutex};
708/// use fyrox_sound::buffer::SoundBufferResource;
709/// use fyrox_sound::source::{SoundSourceBuilder};
710/// use fyrox_sound::source::{Status, SoundSource};
711///
712/// fn make_sound_source(buffer: SoundBufferResource) -> SoundSource {
713///     SoundSourceBuilder::new()
714///         .with_buffer(buffer)
715///         .with_status(Status::Playing)
716///         .with_gain(0.5)
717///         .with_looping(true)
718///         .with_pitch(1.25)
719///         .build()
720///         .unwrap()
721/// }
722/// ```
723pub struct SoundSourceBuilder {
724    buffer: Option<SoundBufferResource>,
725    gain: f32,
726    pitch: f64,
727    name: String,
728    panning: f32,
729    looping: bool,
730    status: Status,
731    play_once: bool,
732    playback_time: Duration,
733    radius: f32,
734    position: Vector3<f32>,
735    max_distance: f32,
736    rolloff_factor: f32,
737    spatial_blend: f32,
738    bus: String,
739}
740
741impl Default for SoundSourceBuilder {
742    fn default() -> Self {
743        Self::new()
744    }
745}
746
747impl SoundSourceBuilder {
748    /// Creates new generic source builder with specified buffer.
749    pub fn new() -> Self {
750        Self {
751            buffer: None,
752            gain: 1.0,
753            pitch: 1.0,
754            name: Default::default(),
755            panning: 0.0,
756            looping: false,
757            status: Status::Stopped,
758            play_once: false,
759            playback_time: Default::default(),
760            radius: 1.0,
761            position: Vector3::new(0.0, 0.0, 0.0),
762            max_distance: f32::MAX,
763            rolloff_factor: 1.0,
764            spatial_blend: 1.0,
765            bus: AudioBusGraph::PRIMARY_BUS.to_string(),
766        }
767    }
768
769    /// Sets desired sound buffer to play.
770    pub fn with_buffer(mut self, buffer: SoundBufferResource) -> Self {
771        self.buffer = Some(buffer);
772        self
773    }
774
775    /// Sets desired sound buffer to play.
776    pub fn with_opt_buffer(mut self, buffer: Option<SoundBufferResource>) -> Self {
777        self.buffer = buffer;
778        self
779    }
780
781    /// See [`SoundSource::set_gain`]
782    pub fn with_gain(mut self, gain: f32) -> Self {
783        self.gain = gain;
784        self
785    }
786
787    /// See [`SoundSource::set_spatial_blend`]
788    pub fn with_spatial_blend_factor(mut self, k: f32) -> Self {
789        self.spatial_blend = k.clamp(0.0, 1.0);
790        self
791    }
792
793    /// See [`SoundSource::set_pitch`]
794    pub fn with_pitch(mut self, pitch: f64) -> Self {
795        self.pitch = pitch;
796        self
797    }
798
799    /// See [`SoundSource::set_panning`]
800    pub fn with_panning(mut self, panning: f32) -> Self {
801        self.panning = panning;
802        self
803    }
804
805    /// See [`SoundSource::set_looping`]
806    pub fn with_looping(mut self, looping: bool) -> Self {
807        self.looping = looping;
808        self
809    }
810
811    /// Sets desired status of source.
812    pub fn with_status(mut self, status: Status) -> Self {
813        self.status = status;
814        self
815    }
816
817    /// See `set_play_once` of SoundSource
818    pub fn with_play_once(mut self, play_once: bool) -> Self {
819        self.play_once = play_once;
820        self
821    }
822
823    /// Sets desired name of the source.
824    pub fn with_name<N: AsRef<str>>(mut self, name: N) -> Self {
825        name.as_ref().clone_into(&mut self.name);
826        self
827    }
828
829    /// Sets desired starting playback time.
830    pub fn with_playback_time(mut self, time: Duration) -> Self {
831        self.playback_time = time;
832        self
833    }
834
835    /// See `set_position` of SpatialSource.
836    pub fn with_position(mut self, position: Vector3<f32>) -> Self {
837        self.position = position;
838        self
839    }
840
841    /// See `set_radius` of SpatialSource.
842    pub fn with_radius(mut self, radius: f32) -> Self {
843        self.radius = radius;
844        self
845    }
846
847    /// See `set_max_distance` of SpatialSource.
848    pub fn with_max_distance(mut self, max_distance: f32) -> Self {
849        self.max_distance = max_distance;
850        self
851    }
852
853    /// See `set_rolloff_factor` of SpatialSource.
854    pub fn with_rolloff_factor(mut self, rolloff_factor: f32) -> Self {
855        self.rolloff_factor = rolloff_factor;
856        self
857    }
858
859    /// Sets desired output bus for the sound source.
860    pub fn with_bus<S: AsRef<str>>(mut self, bus: S) -> Self {
861        self.bus = bus.as_ref().to_string();
862        self
863    }
864
865    /// Creates new instance of generic sound source. May fail if buffer is invalid.
866    pub fn build(self) -> Result<SoundSource, SoundError> {
867        let mut source = SoundSource {
868            buffer: self.buffer.clone(),
869            gain: self.gain,
870            pitch: self.pitch,
871            play_once: self.play_once,
872            panning: self.panning,
873            status: self.status,
874            looping: self.looping,
875            name: self.name,
876            frame_samples: Default::default(),
877            radius: self.radius,
878            position: self.position,
879            max_distance: self.max_distance,
880            rolloff_factor: self.rolloff_factor,
881            spatial_blend: self.spatial_blend,
882            prev_left_samples: Default::default(),
883            prev_right_samples: Default::default(),
884            bus: self.bus,
885            ..Default::default()
886        };
887
888        source.set_buffer(self.buffer)?;
889        source.set_playback_time(self.playback_time);
890
891        Ok(source)
892    }
893}