rg3d_sound/buffer/
mod.rs

1//! This module provides all needed types and methods to create/load sound buffers from different sources.
2//!
3//! # Overview
4//!
5//! Buffer is data source for sound sources in the engine. Each sound sound will fetch samples it needs
6//! from a buffer, process them and send to output device. Buffer can be shared across multiple sources,
7//! this is why each instance wrapped into `Arc<Mutex<>>`. Why not just load a buffer per source? This
8//! is just inefficient memory-wise. Sound samples are very heavy: for example a mono sound that lasts
9//! just 1 second will take ~172 Kb of memory (with 44100 Hz sampling rate and float sample representation).
10
11use crate::buffer::{generic::GenericBuffer, streaming::StreamingBuffer};
12use crate::error::SoundError;
13use rg3d_core::{io::FileLoadError, visitor::prelude::*};
14use rg3d_resource::{define_new_resource, Resource, ResourceData, ResourceState};
15use std::fmt::Debug;
16use std::time::Duration;
17use std::{
18    borrow::Cow,
19    io::{Cursor, Read, Seek, SeekFrom},
20    ops::{Deref, DerefMut},
21    path::{Path, PathBuf},
22};
23
24pub mod generic;
25pub mod streaming;
26
27/// Data source enumeration. Provides unified way of selecting data source for sound buffers. It can be either
28/// a file or memory block.
29#[derive(Debug)]
30pub enum DataSource {
31    /// Data source is a file of any supported format.
32    File {
33        /// Path to file.
34        path: PathBuf,
35
36        /// Buffered file opened for read.
37        #[cfg(not(target_arch = "wasm32"))]
38        data: std::io::BufReader<std::fs::File>,
39
40        /// TODO: In case of WASM load file entirely.
41        #[cfg(target_arch = "wasm32")]
42        data: Cursor<Vec<u8>>,
43    },
44
45    /// Data source is a memory block. Memory block must be in valid format (wav or vorbis/ogg). This variant can
46    /// be used together with virtual file system.
47    Memory(Cursor<Vec<u8>>),
48
49    /// Raw samples in interleaved format with specified sample rate and channel count. Can be used for procedural
50    /// sounds.
51    ///
52    /// # Notes
53    ///
54    /// Cannot be used with streaming buffers - it makes no sense to stream data that is already loaded into memory.
55    Raw {
56        /// Sample rate, typical values 22050, 44100, 48000, etc.
57        sample_rate: usize,
58
59        /// Total amount of channels.
60        channel_count: usize,
61
62        /// Raw samples in interleaved format. Count of samples must be multiple to channel count, otherwise you'll
63        /// get error at attempt to use such buffer.
64        samples: Vec<f32>,
65    },
66
67    /// Raw streaming source.
68    RawStreaming(Box<dyn RawStreamingDataSource>),
69}
70
71/// A samples generator.
72///
73/// # Notes
74///
75/// Iterator implementation (the `next()` method) must produce samples in interleaved format, this
76/// means that samples emitted by the method should be in `LRLRLR..` order, where `L` and `R` are
77/// samples from left and right channels respectively. The sound engine supports both mono and
78/// stereo sample sources.
79pub trait RawStreamingDataSource: Iterator<Item = f32> + Send + Sync + Debug {
80    /// Should return sample rate of the source.
81    fn sample_rate(&self) -> usize;
82
83    /// Should return total channel count.
84    fn channel_count(&self) -> usize;
85
86    /// Tells whether the provider should restart.
87    fn rewind(&mut self) -> Result<(), SoundError> {
88        Ok(())
89    }
90
91    /// Allows you to start playback from given duration.
92    fn time_seek(&mut self, _duration: Duration) {}
93
94    /// Returns total duration of data. Can be `None` if internal decoder does not supports seeking.
95    fn duration(&self) -> Option<Duration> {
96        None
97    }
98}
99
100impl DataSource {
101    /// Tries to create new `File` data source from given path. May fail if file does not exists.
102    pub async fn from_file<P>(path: P) -> Result<Self, FileLoadError>
103    where
104        P: AsRef<Path>,
105    {
106        Ok(DataSource::File {
107            path: path.as_ref().to_path_buf(),
108
109            #[cfg(not(target_arch = "wasm32"))]
110            data: std::io::BufReader::new(match std::fs::File::open(path) {
111                Ok(file) => file,
112                Err(e) => return Err(FileLoadError::Io(e)),
113            }),
114
115            #[cfg(target_arch = "wasm32")]
116            data: Cursor::new(rg3d_core::io::load_file(path).await?),
117        })
118    }
119
120    /// Creates new data source from given memory block. This function does not checks if this is valid source or
121    /// not. Data source validity will be checked on first use.
122    pub fn from_memory(data: Vec<u8>) -> Self {
123        DataSource::Memory(Cursor::new(data))
124    }
125}
126
127impl Read for DataSource {
128    fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
129        match self {
130            DataSource::File { data, .. } => data.read(buf),
131            DataSource::Memory(b) => b.read(buf),
132            DataSource::Raw { .. } => unreachable!("Raw data source does not supports Read trait!"),
133            DataSource::RawStreaming { .. } => {
134                unreachable!("Raw data source does not supports Read trait!")
135            }
136        }
137    }
138}
139
140impl Seek for DataSource {
141    fn seek(&mut self, pos: SeekFrom) -> Result<u64, std::io::Error> {
142        match self {
143            DataSource::File { data, .. } => data.seek(pos),
144            DataSource::Memory(b) => b.seek(pos),
145            DataSource::Raw { .. } => unreachable!("Raw data source does not supports Seek trait!"),
146            DataSource::RawStreaming { .. } => {
147                unreachable!("Raw data source does not supports Seek trait!")
148            }
149        }
150    }
151}
152
153/// An error that can occur during loading of sound buffer.
154#[derive(Debug)]
155pub enum SoundBufferResourceLoadError {
156    /// A format is not supported.
157    UnsupportedFormat,
158    /// File load error.
159    Io(FileLoadError),
160}
161
162define_new_resource!(
163    /// A shared sound buffer resource.
164    SoundBufferResource<SoundBufferState, SoundBufferResourceLoadError>
165);
166
167impl SoundBufferResource {
168    /// Tries to create new streaming sound buffer from a given data source. Returns sound source
169    /// wrapped into Arc<Mutex<>> that can be directly used with sound sources.
170    pub fn new_streaming(data_source: DataSource) -> Result<Self, DataSource> {
171        Ok(Self(Resource::new(ResourceState::Ok(
172            SoundBufferState::Streaming(StreamingBuffer::new(data_source)?),
173        ))))
174    }
175
176    /// Tries to create new generic sound buffer from a given data source. Returns sound source
177    /// wrapped into Arc<Mutex<>> that can be directly used with sound sources.
178    pub fn new_generic(data_source: DataSource) -> Result<Self, DataSource> {
179        Ok(Self(Resource::new(ResourceState::Ok(
180            SoundBufferState::Generic(GenericBuffer::new(data_source)?),
181        ))))
182    }
183}
184
185/// Sound buffer is a data source for sound sources. See module documentation for more info.
186#[derive(Debug, Visit)]
187pub enum SoundBufferState {
188    /// General-purpose buffer, usually contains all the data and allows random
189    /// access to samples. It is also used to make streaming buffer via composition.
190    Generic(GenericBuffer),
191
192    /// Buffer that will be filled by small portions of data only when it is needed.
193    /// Ideal for large sounds (music, ambient, etc.), because unpacked PCM data
194    /// takes very large amount of RAM. Allows random access only to currently loaded
195    /// block, so in general there is no *true* random access.
196    Streaming(StreamingBuffer),
197}
198
199impl SoundBufferState {
200    /// Tries to create new streaming sound buffer from a given data source. It returns raw sound
201    /// buffer that has to be wrapped into Arc<Mutex<>> for use with sound sources.
202    pub fn raw_streaming(data_source: DataSource) -> Result<Self, DataSource> {
203        Ok(Self::Streaming(StreamingBuffer::new(data_source)?))
204    }
205
206    /// Tries to create new generic sound buffer from a given data source. It returns raw sound
207    /// buffer that has to be wrapped into Arc<Mutex<>> for use with sound sources.
208    pub fn raw_generic(data_source: DataSource) -> Result<Self, DataSource> {
209        Ok(Self::Generic(GenericBuffer::new(data_source)?))
210    }
211}
212
213impl Default for SoundBufferState {
214    fn default() -> Self {
215        SoundBufferState::Generic(Default::default())
216    }
217}
218
219impl Deref for SoundBufferState {
220    type Target = GenericBuffer;
221
222    /// Returns shared reference to generic buffer for any enum variant. It is possible because
223    /// streaming sound buffers are built on top of generic buffers.
224    fn deref(&self) -> &Self::Target {
225        match self {
226            SoundBufferState::Generic(v) => v,
227            SoundBufferState::Streaming(v) => v,
228        }
229    }
230}
231
232impl DerefMut for SoundBufferState {
233    /// Returns mutable reference to generic buffer for any enum variant. It is possible because
234    /// streaming sound buffers are built on top of generic buffers.
235    fn deref_mut(&mut self) -> &mut Self::Target {
236        match self {
237            SoundBufferState::Generic(v) => v,
238            SoundBufferState::Streaming(v) => v,
239        }
240    }
241}
242
243impl ResourceData for SoundBufferState {
244    fn path(&self) -> Cow<Path> {
245        Cow::from(&self.external_source_path)
246    }
247
248    fn set_path(&mut self, path: PathBuf) {
249        self.external_source_path = path;
250    }
251}