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