Skip to main content

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