rg3d_sound/buffer/
generic.rs

1//! Generic buffer module.
2//!
3//! # Overview
4//!
5//! Generic buffer is just an array of raw samples in IEEE float format with some additional info (sample rate,
6//! channel count, etc.). All samples stored in interleaved format, which means samples for each channel are
7//! near - `LRLRLR...`.
8//!
9//! # Usage
10//!
11//! Generic source can be created like so:
12//!
13//! ```no_run
14//! use std::sync::{Mutex, Arc};
15//! use rg3d_sound::buffer::{SoundBufferResource, DataSource};
16//!
17//! async fn make_buffer() -> SoundBufferResource {
18//!     let data_source = DataSource::from_file("sound.wav").await.unwrap();
19//!     SoundBufferResource::new_generic(data_source).unwrap()
20//! }
21//! ```
22
23#![allow(clippy::manual_range_contains)]
24
25use crate::{buffer::DataSource, decoder::Decoder};
26use rg3d_core::visitor::{Visit, VisitResult, Visitor};
27use std::path::Path;
28use std::{path::PathBuf, time::Duration};
29
30/// Generic sound buffer that contains decoded samples and allows random access.
31#[derive(Debug, Default)]
32pub struct GenericBuffer {
33    /// Interleaved decoded samples (mono sounds: L..., stereo sounds: LR...)
34    /// For streaming buffers it contains only small part of decoded data
35    /// (usually something around 1 sec).
36    pub(in crate) samples: Vec<f32>,
37    pub(in crate) channel_count: usize,
38    pub(in crate) sample_rate: usize,
39    pub(in crate) external_source_path: PathBuf,
40}
41
42impl Visit for GenericBuffer {
43    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
44        visitor.enter_region(name)?;
45
46        self.external_source_path.visit("Path", visitor)?;
47
48        visitor.leave_region()
49    }
50}
51
52impl GenericBuffer {
53    /// Creates new generic buffer from specified data source. May fail if data source has unsupported
54    /// format, corrupted, etc.
55    ///
56    /// # Notes
57    ///
58    /// `DataSource::RawStreaming` is **not** supported with generic buffers, use streaming buffer
59    /// instead!
60    ///
61    /// Data source with raw samples must have sample count multiple of channel count, otherwise this
62    /// function will return `Err`.
63    pub fn new(source: DataSource) -> Result<Self, DataSource> {
64        match source {
65            DataSource::Raw {
66                sample_rate,
67                channel_count,
68                samples,
69            } => {
70                if channel_count < 1 || channel_count > 2 || samples.len() % channel_count != 0 {
71                    Err(DataSource::Raw {
72                        sample_rate,
73                        channel_count,
74                        samples,
75                    })
76                } else {
77                    Ok(Self {
78                        samples,
79                        channel_count,
80                        sample_rate,
81                        external_source_path: Default::default(),
82                    })
83                }
84            }
85            DataSource::RawStreaming(_) => Err(source),
86            _ => {
87                let external_source_path = if let DataSource::File { path, .. } = &source {
88                    path.clone()
89                } else {
90                    Default::default()
91                };
92
93                // Store cursor to handle errors.
94                let (is_memory, external_cursor) = if let DataSource::Memory(cursor) = &source {
95                    (true, cursor.clone())
96                } else {
97                    (false, Default::default())
98                };
99
100                let decoder = Decoder::new(source)?;
101                if decoder.get_channel_count() < 1 || decoder.get_channel_count() > 2 {
102                    if is_memory {
103                        return Err(DataSource::Memory(external_cursor));
104                    } else {
105                        // There is not much we can do here: if the user supplied DataSource::File,
106                        // they probably do not want us to re-read the file again in
107                        // DataSource::from_file.
108                        return Err(DataSource::Raw {
109                            sample_rate: decoder.get_sample_rate(),
110                            channel_count: decoder.get_channel_count(),
111                            samples: vec![],
112                        });
113                    }
114                }
115
116                Ok(Self {
117                    sample_rate: decoder.get_sample_rate(),
118                    channel_count: decoder.get_channel_count(),
119                    samples: decoder.into_samples(),
120                    external_source_path,
121                })
122            }
123        }
124    }
125
126    /// In case if buffer was created from file, this method returns file name. Can be useful for
127    /// serialization needs where you just need to know which file needs to be reloaded from disk
128    /// when you loading a saved game.
129    #[inline]
130    pub fn external_data_path(&self) -> &Path {
131        &self.external_source_path
132    }
133
134    /// Sets new path for external data source.
135    pub fn set_external_data_path(&mut self, path: PathBuf) -> PathBuf {
136        std::mem::replace(&mut self.external_source_path, path)
137    }
138
139    /// Checks if buffer is empty or not.
140    #[inline]
141    pub fn is_empty(&self) -> bool {
142        self.samples.is_empty()
143    }
144
145    /// Returns shared reference to an array with samples.
146    #[inline]
147    pub fn samples(&self) -> &[f32] {
148        &self.samples
149    }
150
151    /// Returns mutable reference to an array with samples that could be modified.
152    pub fn samples_mut(&mut self) -> &mut [f32] {
153        &mut self.samples
154    }
155
156    /// Returns exact amount of channels in the buffer.
157    #[inline]
158    pub fn channel_count(&self) -> usize {
159        self.channel_count
160    }
161
162    /// Returns sample rate of the buffer.
163    #[inline]
164    pub fn sample_rate(&self) -> usize {
165        self.sample_rate
166    }
167
168    /// Returns exact duration of the buffer.
169    #[inline]
170    pub fn duration(&self) -> Duration {
171        Duration::from_secs_f64(
172            (self.samples.len() / (self.channel_count * self.sample_rate)) as f64,
173        )
174    }
175}