chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:24:51 +08:00
commit e74a3762b8
963 changed files with 299400 additions and 0 deletions
+181
View File
@@ -0,0 +1,181 @@
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! Generic buffer module.
//!
//! # Overview
//!
//! Generic buffer is just an array of raw samples in IEEE float format with some additional info (sample rate,
//! channel count, etc.). All samples stored in interleaved format, which means samples for each channel are
//! near - `LRLRLR...`.
//!
//! # Usage
//!
//! Generic source can be created like so:
//!
//! ```no_run
//! use std::sync::{Mutex, Arc};
//! use fyrox_sound::buffer::{SoundBufferResource, DataSource, SoundBufferResourceExtension};
//! use fyrox_resource::io::FsResourceIo;
//!
//! async fn make_buffer() -> SoundBufferResource {
//! let data_source = DataSource::from_file("sound.wav", &FsResourceIo).await.unwrap();
//! SoundBufferResource::new_generic(data_source).unwrap()
//! }
//! ```
#![allow(clippy::manual_range_contains)]
use crate::{buffer::DataSource, decoder::Decoder};
use fyrox_core::{reflect::prelude::*, visitor::prelude::*};
use std::ops::{Deref, DerefMut};
use std::time::Duration;
use super::SoundBufferResourceLoadError;
/// Samples container.
#[derive(Debug, Default, PartialEq, Visit, Clone, Reflect)]
#[reflect(type_uuid = "7b0ba106-9ed5-41e1-a8c7-82c609d6f74e")]
pub struct Samples(pub Vec<f32>);
impl Deref for Samples {
type Target = Vec<f32>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Samples {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
/// Generic sound buffer that contains decoded samples and allows random access.
#[derive(Debug, Clone, Default, PartialEq, Visit, Reflect)]
#[reflect(type_uuid = "e5bf74a5-13d9-4518-96f2-0e4f3413966e")]
pub struct GenericBuffer {
/// Interleaved decoded samples (mono sounds: L..., stereo sounds: LR...)
/// For streaming buffers it contains only small part of decoded data
/// (usually something around 1 sec).
#[visit(skip)]
pub(crate) samples: Samples,
#[visit(skip)]
pub(crate) channel_count: usize,
#[visit(skip)]
pub(crate) sample_rate: usize,
#[visit(skip)]
pub(crate) channel_duration_in_samples: usize,
}
impl GenericBuffer {
/// Creates new generic buffer from specified data source. May fail if data source has unsupported
/// format, corrupted, etc.
///
/// # Notes
///
/// `DataSource::RawStreaming` is **not** supported with generic buffers, use streaming buffer
/// instead!
///
/// Data source with raw samples must have sample count multiple of channel count, otherwise this
/// function will return `Err`.
pub fn new(source: DataSource) -> Result<Self, SoundBufferResourceLoadError> {
match source {
DataSource::Raw {
sample_rate,
channel_count,
samples,
} => {
if channel_count < 1 || channel_count > 2 || samples.len() % channel_count != 0 {
Err(SoundBufferResourceLoadError::DataSourceError)
} else {
Ok(Self {
channel_duration_in_samples: samples.len() / channel_count,
samples: Samples(samples),
channel_count,
sample_rate,
})
}
}
DataSource::RawStreaming(_) => Err(SoundBufferResourceLoadError::DataSourceError),
_ => {
let decoder = Decoder::new(source)?;
if decoder.get_channel_count() < 1 || decoder.get_channel_count() > 2 {
return Err(SoundBufferResourceLoadError::DataSourceError);
}
Ok(Self {
sample_rate: decoder.get_sample_rate(),
channel_count: decoder.get_channel_count(),
channel_duration_in_samples: decoder.channel_duration_in_samples(),
samples: Samples(decoder.into_samples()),
})
}
}
}
/// Checks if buffer is empty or not.
#[inline]
pub fn is_empty(&self) -> bool {
self.samples.is_empty()
}
/// Returns shared reference to an array with samples.
#[inline]
pub fn samples(&self) -> &[f32] {
&self.samples
}
/// Returns mutable reference to an array with samples that could be modified.
pub fn samples_mut(&mut self) -> &mut [f32] {
&mut self.samples
}
/// Returns exact amount of channels in the buffer.
#[inline]
pub fn channel_count(&self) -> usize {
self.channel_count
}
/// Returns sample rate of the buffer.
#[inline]
pub fn sample_rate(&self) -> usize {
self.sample_rate
}
/// Returns exact time length of the buffer.
#[inline]
pub fn duration(&self) -> Duration {
if self.sample_rate == 0 {
return Duration::ZERO;
}
Duration::from_nanos(
(self.channel_duration_in_samples as u64 * 1_000_000_000u64) / self.sample_rate as u64,
)
}
/// Returns exact duration of each channel (in samples) of the buffer. The returned value represents the entire length
/// of each channel in the buffer, even if it is streaming and its content is not yet fully decoded.
#[inline]
pub fn channel_duration_in_samples(&self) -> usize {
self.channel_duration_in_samples
}
}
+101
View File
@@ -0,0 +1,101 @@
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! Sound buffer loader.
use crate::buffer::{DataSource, SoundBuffer};
use fyrox_core::{reflect::prelude::*, uuid::Uuid};
use fyrox_resource::{
io::ResourceIo,
loader::{BoxedImportOptionsLoaderFuture, BoxedLoaderFuture, LoaderPayload, ResourceLoader},
options::{
try_get_import_settings, try_get_import_settings_opaque, BaseImportOptions, ImportOptions,
},
state::LoadError,
};
use serde::{Deserialize, Serialize};
use std::{path::PathBuf, sync::Arc};
/// Defines sound buffer resource import options.
#[derive(Clone, Deserialize, Serialize, Default, Debug, PartialEq, Reflect)]
#[reflect(type_uuid = "032e333a-f0bd-41f2-a7e5-79207f6064ce")]
pub struct SoundBufferImportOptions {
/// Whether the buffer is streaming or not.
pub stream: bool,
}
impl ImportOptions for SoundBufferImportOptions {}
/// Default implementation for sound buffer loading.
pub struct SoundBufferLoader {
/// Default import options for sound buffer resources.
pub default_import_options: SoundBufferImportOptions,
}
impl ResourceLoader for SoundBufferLoader {
fn extensions(&self) -> &[&str] {
&["wav", "ogg"]
}
fn data_type_uuid(&self) -> Uuid {
<SoundBuffer as Reflect>::type_info().type_uuid
}
fn load(&self, path: PathBuf, io: Arc<dyn ResourceIo>) -> BoxedLoaderFuture {
let default_import_options = self.default_import_options.clone();
Box::pin(async move {
let io = io.as_ref();
let import_options = try_get_import_settings(&path, io)
.await
.unwrap_or(default_import_options);
let source = DataSource::from_file(&path, io)
.await
.map_err(LoadError::new)?;
let result = if import_options.stream {
SoundBuffer::raw_streaming(source)
} else {
SoundBuffer::raw_generic(source)
};
match result {
Ok(buffer) => Ok(LoaderPayload::new(buffer)),
Err(_) => Err(LoadError::new("Invalid data source.")),
}
})
}
fn try_load_import_settings(
&self,
resource_path: PathBuf,
io: Arc<dyn ResourceIo>,
) -> BoxedImportOptionsLoaderFuture {
Box::pin(async move {
try_get_import_settings_opaque::<SoundBufferImportOptions>(&resource_path, &*io).await
})
}
fn default_import_options(&self) -> Option<Box<dyn BaseImportOptions>> {
Some(Box::<SoundBufferImportOptions>::default())
}
}
+354
View File
@@ -0,0 +1,354 @@
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! This module provides all needed types and methods to create/load sound buffers from different sources.
//!
//! # Overview
//!
//! Buffer is data source for sound sources in the engine. Each sound sound will fetch samples it needs
//! from a buffer, process them and send to output device. Buffer can be shared across multiple sources,
//! this is why each instance wrapped into `Arc<Mutex<>>`. Why not just load a buffer per source? This
//! is just inefficient memory-wise. Sound samples are very heavy: for example a mono sound that lasts
//! just 1 second will take ~172 Kb of memory (with 44100 Hz sampling rate and float sample representation).
use crate::{
buffer::{generic::GenericBuffer, streaming::StreamingBuffer},
error::SoundError,
};
use fyrox_core::{io::FileError, reflect::prelude::*, uuid::Uuid, visitor::prelude::*};
use fyrox_resource::untyped::ResourceKind;
use fyrox_resource::{
io::{FileReader, ResourceIo},
Resource, ResourceData,
};
use std::{
error::Error,
fmt::Debug,
io::{Cursor, Read, Seek, SeekFrom},
ops::{Deref, DerefMut},
path::{Path, PathBuf},
time::Duration,
};
use symphonia::core::io::MediaSource;
pub mod generic;
pub mod loader;
pub mod streaming;
/// Data source enumeration. Provides unified way of selecting data source for sound buffers. It can be either
/// a file or memory block.
#[derive(Debug)]
pub enum DataSource {
/// Data source is a file of any supported format.
File {
/// Path to file.
path: PathBuf,
/// Reader for reading from the source
data: Box<dyn FileReader>,
},
/// Data source is a memory block. Memory block must be in valid format (wav or vorbis/ogg). This variant can
/// be used together with virtual file system.
Memory(Cursor<Vec<u8>>),
/// Raw samples in interleaved format with specified sample rate and channel count. Can be used for procedural
/// sounds.
///
/// # Notes
///
/// Cannot be used with streaming buffers - it makes no sense to stream data that is already loaded into memory.
Raw {
/// Sample rate, typical values 22050, 44100, 48000, etc.
sample_rate: usize,
/// Total amount of channels.
channel_count: usize,
/// Raw samples in interleaved format. Count of samples must be multiple to channel count, otherwise you'll
/// get error at attempt to use such buffer.
samples: Vec<f32>,
},
/// Raw streaming source.
RawStreaming(Box<dyn RawStreamingDataSource>),
}
/// A samples generator.
///
/// # Notes
///
/// Iterator implementation (the `next()` method) must produce samples in interleaved format, this
/// means that samples emitted by the method should be in `LRLRLR..` order, where `L` and `R` are
/// samples from left and right channels respectively. The sound engine supports both mono and
/// stereo sample sources.
pub trait RawStreamingDataSource: Iterator<Item = f32> + Send + Sync + Debug {
/// Should return sample rate of the source.
fn sample_rate(&self) -> usize;
/// Should return total channel count.
fn channel_count(&self) -> usize;
/// Tells whether the provider should restart.
///
/// Default implementation calls [`Self::time_seek`] with a zero duration
fn rewind(&mut self) -> Result<(), SoundError> {
self.time_seek(Duration::from_secs(0))
}
/// Allows you to start playback from given duration.
fn time_seek(&mut self, _duration: Duration) -> Result<(), SoundError> {
Ok(())
}
/// Returns total duration of the data.
fn channel_duration_in_samples(&self) -> usize {
0
}
}
impl DataSource {
/// Tries to create new `File` data source from given path. May fail if file does not exists.
pub async fn from_file<P>(path: P, io: &dyn ResourceIo) -> Result<Self, FileError>
where
P: AsRef<Path>,
{
Ok(DataSource::File {
path: path.as_ref().to_path_buf(),
data: io.file_reader(path.as_ref()).await?,
})
}
/// Creates new data source from given memory block. This function does not checks if this is valid source or
/// not. Data source validity will be checked on first use.
pub fn from_memory(data: Vec<u8>) -> Self {
DataSource::Memory(Cursor::new(data))
}
/// Tries to get a path to external data source.
pub fn path(&self) -> Option<&Path> {
match self {
DataSource::File { path, .. } => Some(path),
_ => None,
}
}
/// Tries to get a path to external data source.
pub fn path_owned(&self) -> Option<PathBuf> {
match self {
DataSource::File { path, .. } => Some(path.clone()),
_ => None,
}
}
}
impl Read for DataSource {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
match self {
DataSource::File { data, .. } => data.read(buf),
DataSource::Memory(b) => b.read(buf),
DataSource::Raw { .. } => unreachable!("Raw data source does not supports Read trait!"),
DataSource::RawStreaming { .. } => {
unreachable!("Raw data source does not supports Read trait!")
}
}
}
}
impl Seek for DataSource {
fn seek(&mut self, pos: SeekFrom) -> Result<u64, std::io::Error> {
match self {
DataSource::File { data, .. } => data.seek(pos),
DataSource::Memory(b) => b.seek(pos),
DataSource::Raw { .. } => unreachable!("Raw data source does not supports Seek trait!"),
DataSource::RawStreaming { .. } => {
unreachable!("Raw data source does not supports Seek trait!")
}
}
}
}
impl MediaSource for DataSource {
fn is_seekable(&self) -> bool {
match self {
DataSource::File { .. } | DataSource::Memory(_) => true,
DataSource::Raw { .. } | DataSource::RawStreaming(_) => false,
}
}
fn byte_len(&self) -> Option<u64> {
match self {
DataSource::File { path: _, data } => data.byte_len(),
DataSource::Memory(cursor) => MediaSource::byte_len(cursor),
DataSource::Raw { .. } | DataSource::RawStreaming(_) => None,
}
}
}
/// An error that can occur during loading of sound buffer.
#[derive(Debug)]
pub enum SoundBufferResourceLoadError {
/// A format is not supported.
UnsupportedFormat,
/// File load error.
Io(FileError),
/// Errors involving the data source
///
/// This could be, e.g., wrong number of channels, or attempting to use a
/// [`DataSource::RawStreaming`] where it doesn't make sense
DataSourceError,
/// Underlying sound error
SoundError(SoundError),
}
impl std::fmt::Display for SoundBufferResourceLoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SoundBufferResourceLoadError::UnsupportedFormat => {
write!(f, "unsupported file format")
}
SoundBufferResourceLoadError::Io(e) => write!(f, "{e:?}"),
SoundBufferResourceLoadError::DataSourceError => {
write!(f, "error in underlying data source")
}
SoundBufferResourceLoadError::SoundError(e) => write!(f, "{e:?}"),
}
}
}
impl std::error::Error for SoundBufferResourceLoadError {}
/// Sound buffer is a data source for sound sources. See module documentation for more info.
#[derive(Debug, Visit, PartialEq, Reflect)]
#[reflect(non_cloneable)]
#[reflect(type_uuid = "f6a077b7-c8ff-4473-a95b-0289441ea9d8")]
pub enum SoundBuffer {
/// General-purpose buffer, usually contains all the data and allows random
/// access to samples. It is also used to make streaming buffer via composition.
Generic(GenericBuffer),
/// Buffer that will be filled by small portions of data only when it is needed.
/// Ideal for large sounds (music, ambient, etc.), because unpacked PCM data
/// takes very large amount of RAM. Allows random access only to currently loaded
/// block, so in general there is no *true* random access.
Streaming(StreamingBuffer),
}
impl From<SoundError> for SoundBufferResourceLoadError {
fn from(err: SoundError) -> Self {
Self::SoundError(err)
}
}
/// Type alias for sound buffer resource.
pub type SoundBufferResource = Resource<SoundBuffer>;
/// Extension trait for sound buffer resource.
pub trait SoundBufferResourceExtension {
/// Tries to create new streaming sound buffer from a given data source.
fn new_streaming(
data_source: DataSource,
) -> Result<Resource<SoundBuffer>, SoundBufferResourceLoadError>;
/// Tries to create new generic sound buffer from a given data source.
fn new_generic(
data_source: DataSource,
) -> Result<Resource<SoundBuffer>, SoundBufferResourceLoadError>;
}
impl SoundBufferResourceExtension for SoundBufferResource {
fn new_streaming(
data_source: DataSource,
) -> Result<Resource<SoundBuffer>, SoundBufferResourceLoadError> {
Ok(Resource::new_ok(
Uuid::new_v4(),
ResourceKind::External,
SoundBuffer::Streaming(StreamingBuffer::new(data_source)?),
))
}
fn new_generic(
data_source: DataSource,
) -> Result<Resource<SoundBuffer>, SoundBufferResourceLoadError> {
Ok(Resource::new_ok(
Uuid::new_v4(),
ResourceKind::External,
SoundBuffer::Generic(GenericBuffer::new(data_source)?),
))
}
}
impl SoundBuffer {
/// Tries to create new streaming sound buffer from a given data source. It returns raw sound
/// buffer that has to be wrapped into Arc<Mutex<>> for use with sound sources.
pub fn raw_streaming(data_source: DataSource) -> Result<Self, SoundBufferResourceLoadError> {
Ok(Self::Streaming(StreamingBuffer::new(data_source)?))
}
/// Tries to create new generic sound buffer from a given data source. It returns raw sound
/// buffer that has to be wrapped into Arc<Mutex<>> for use with sound sources.
pub fn raw_generic(data_source: DataSource) -> Result<Self, SoundBufferResourceLoadError> {
Ok(Self::Generic(GenericBuffer::new(data_source)?))
}
}
impl Default for SoundBuffer {
fn default() -> Self {
SoundBuffer::Generic(Default::default())
}
}
impl Deref for SoundBuffer {
type Target = GenericBuffer;
/// Returns shared reference to generic buffer for any enum variant. It is possible because
/// streaming sound buffers are built on top of generic buffers.
fn deref(&self) -> &Self::Target {
match self {
SoundBuffer::Generic(v) => v,
SoundBuffer::Streaming(v) => v,
}
}
}
impl DerefMut for SoundBuffer {
/// Returns mutable reference to generic buffer for any enum variant. It is possible because
/// streaming sound buffers are built on top of generic buffers.
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
SoundBuffer::Generic(v) => v,
SoundBuffer::Streaming(v) => v,
}
}
}
impl ResourceData for SoundBuffer {
fn save(&mut self, _path: &Path) -> Result<(), Box<dyn Error>> {
Err("Saving is not supported!".to_string().into())
}
fn can_be_saved(&self) -> bool {
false
}
fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
None
}
}
+241
View File
@@ -0,0 +1,241 @@
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! Streaming buffer.
//!
//! # Overview
//!
//! Streaming buffers are used for long sounds (usually longer than 15 seconds) to reduce memory usage.
//! Some sounds in games are very long - music, ambient sounds, voice, etc. and it is too inefficient
//! to load and decode them directly into memory all at once - it will just take enormous amount of memory
//! that could be used to something more useful.
//!
//! # Usage
//!
//! There are almost no difference with generic buffers:
//!
//! ```no_run
//! use std::sync::{Mutex, Arc};
//! use fyrox_sound::buffer::{SoundBufferResource, DataSource, SoundBufferResourceExtension};
//! use fyrox_resource::io::FsResourceIo;
//!
//! async fn make_streaming_buffer() -> SoundBufferResource {
//! let data_source = DataSource::from_file("some_long_sound.ogg", &FsResourceIo).await.unwrap();
//! SoundBufferResource::new_streaming(data_source).unwrap()
//! }
//! ```
//!
//! # Notes
//!
//! Streaming buffer cannot be shared across multiple source. On attempt to create a source with a streaming
//! buffer that already in use you'll get error.
use crate::buffer::generic::Samples;
use crate::{
buffer::{generic::GenericBuffer, DataSource, RawStreamingDataSource},
decoder::Decoder,
error::SoundError,
};
use fyrox_core::{reflect::prelude::*, visitor::prelude::*};
use std::{
ops::{Deref, DerefMut},
time::Duration,
};
/// Streaming buffer for long sounds. Does not support random access.
#[derive(Debug, Default, Visit, Reflect)]
#[reflect(non_cloneable)]
#[reflect(type_uuid = "2e668c89-5998-4e66-a908-f5d96d700c9e")]
pub struct StreamingBuffer {
pub(crate) generic: GenericBuffer,
/// Count of sources that share this buffer, it is important to keep only one
/// user of streaming buffer, because streaming buffer does not allow random
/// access.
#[visit(skip)]
pub(crate) use_count: usize,
#[visit(skip)]
#[reflect(hidden)]
streaming_source: StreamingSource,
}
impl PartialEq for StreamingBuffer {
fn eq(&self, other: &Self) -> bool {
self.generic == other.generic
}
}
#[derive(Debug, Default)]
enum StreamingSource {
#[default]
Null,
Decoder(Decoder),
Raw(Box<dyn RawStreamingDataSource>),
}
impl StreamingSource {
#[inline]
fn new(data_source: DataSource) -> Result<Self, SoundError> {
match data_source {
DataSource::File { .. } | DataSource::Memory(_) => {
Ok(Self::Decoder(Decoder::new(data_source)?))
}
DataSource::RawStreaming(raw) => Ok(Self::Raw(raw)),
// It makes no sense to stream raw data which is already loaded into memory.
_ => Err(SoundError::UnsupportedFormat),
}
}
#[inline]
fn sample_rate(&self) -> usize {
match self {
StreamingSource::Decoder(decoder) => decoder.get_sample_rate(),
StreamingSource::Raw(raw) => raw.sample_rate(),
StreamingSource::Null => 0,
}
}
#[inline]
fn channel_count(&self) -> usize {
match self {
StreamingSource::Decoder(decoder) => decoder.get_channel_count(),
StreamingSource::Raw(raw) => raw.channel_count(),
StreamingSource::Null => 0,
}
}
fn channel_duration_in_samples(&self) -> usize {
match self {
StreamingSource::Null => 0,
StreamingSource::Decoder(decoder) => decoder.channel_duration_in_samples(),
StreamingSource::Raw(raw) => raw.channel_duration_in_samples(),
}
}
fn rewind(&mut self) -> Result<(), SoundError> {
match self {
StreamingSource::Null => Ok(()),
StreamingSource::Decoder(decoder) => decoder.rewind(),
StreamingSource::Raw(raw) => raw.rewind(),
}
}
fn time_seek(&mut self, location: Duration) -> Result<(), SoundError> {
match self {
StreamingSource::Null => Ok(()),
StreamingSource::Decoder(decoder) => decoder.time_seek(location),
StreamingSource::Raw(raw) => raw.time_seek(location),
}
}
#[inline]
fn read_next_samples_block_into(&mut self, buffer: &mut Vec<f32>) -> usize {
buffer.clear();
let count = StreamingBuffer::STREAM_SAMPLE_COUNT * self.channel_count();
match self {
StreamingSource::Decoder(decoder) => {
for _ in 0..count {
if let Some(sample) = decoder.next() {
buffer.push(sample)
} else {
break;
}
}
}
StreamingSource::Raw(raw_streaming) => {
for _ in 0..count {
if let Some(sample) = raw_streaming.next() {
buffer.push(sample)
} else {
break;
}
}
}
StreamingSource::Null => (),
}
buffer.len()
}
}
impl StreamingBuffer {
/// Defines amount of samples `per channel` which each streaming buffer will use for internal buffer.
pub const STREAM_SAMPLE_COUNT: usize = 44100;
/// Creates new streaming buffer using given data source. May fail if data source has unsupported format
/// or it has corrupted data. Length of internal generic buffer cannot be changed but can be fetched from
/// `StreamingBuffer::STREAM_SAMPLE_COUNT`
///
/// # Notes
///
/// This function will return Err if data source is `Raw`. It makes no sense to stream raw data which
/// is already loaded into memory. Use Generic source instead!
pub fn new(source: DataSource) -> Result<Self, SoundError> {
let mut streaming_source = StreamingSource::new(source)?;
let mut samples = Vec::new();
let channel_count = streaming_source.channel_count();
streaming_source.read_next_samples_block_into(&mut samples);
debug_assert_eq!(samples.len() % channel_count, 0);
Ok(Self {
generic: GenericBuffer {
samples: Samples(samples),
sample_rate: streaming_source.sample_rate(),
channel_count: streaming_source.channel_count(),
channel_duration_in_samples: streaming_source.channel_duration_in_samples(),
},
use_count: 0,
streaming_source,
})
}
#[inline]
pub(crate) fn read_next_block(&mut self) {
self.streaming_source
.read_next_samples_block_into(&mut self.generic.samples);
}
#[inline]
pub(crate) fn rewind(&mut self) -> Result<(), SoundError> {
self.streaming_source.rewind()
}
#[inline]
pub(crate) fn time_seek(&mut self, location: Duration) -> Result<(), SoundError> {
self.streaming_source.time_seek(location)
}
}
impl Deref for StreamingBuffer {
type Target = GenericBuffer;
/// Returns shared reference to internal generic buffer. Can be useful to get some info (sample rate,
/// channel count).
fn deref(&self) -> &Self::Target {
&self.generic
}
}
impl DerefMut for StreamingBuffer {
/// Returns mutable reference to internal generic buffer. Can be used to modify it.
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.generic
}
}