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}