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}