use crate::buffer::{generic::GenericBuffer, streaming::StreamingBuffer};
use crate::error::SoundError;
use rg3d_core::{io::FileLoadError, visitor::prelude::*};
use rg3d_resource::{define_new_resource, Resource, ResourceData, ResourceState};
use std::fmt::Debug;
use std::time::Duration;
use std::{
borrow::Cow,
io::{Cursor, Read, Seek, SeekFrom},
ops::{Deref, DerefMut},
path::{Path, PathBuf},
};
pub mod generic;
pub mod streaming;
#[derive(Debug)]
pub enum DataSource {
File {
path: PathBuf,
#[cfg(not(target_arch = "wasm32"))]
data: std::io::BufReader<std::fs::File>,
#[cfg(target_arch = "wasm32")]
data: Cursor<Vec<u8>>,
},
Memory(Cursor<Vec<u8>>),
Raw {
sample_rate: usize,
channel_count: usize,
samples: Vec<f32>,
},
RawStreaming(Box<dyn RawStreamingDataSource>),
}
pub trait RawStreamingDataSource: Iterator<Item = f32> + Send + Sync + Debug {
fn sample_rate(&self) -> usize;
fn channel_count(&self) -> usize;
fn rewind(&mut self) -> Result<(), SoundError> {
Ok(())
}
fn time_seek(&mut self, _duration: Duration) {}
fn duration(&self) -> Option<Duration> {
None
}
}
impl DataSource {
pub async fn from_file<P>(path: P) -> Result<Self, FileLoadError>
where
P: AsRef<Path>,
{
Ok(DataSource::File {
path: path.as_ref().to_path_buf(),
#[cfg(not(target_arch = "wasm32"))]
data: std::io::BufReader::new(match std::fs::File::open(path) {
Ok(file) => file,
Err(e) => return Err(FileLoadError::Io(e)),
}),
#[cfg(target_arch = "wasm32")]
data: Cursor::new(rg3d_core::io::load_file(path).await?),
})
}
pub fn from_memory(data: Vec<u8>) -> Self {
DataSource::Memory(Cursor::new(data))
}
}
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!")
}
}
}
}
#[derive(Debug)]
pub enum SoundBufferResourceLoadError {
UnsupportedFormat,
Io(FileLoadError),
}
define_new_resource!(
SoundBufferResource<SoundBufferState, SoundBufferResourceLoadError>
);
impl SoundBufferResource {
pub fn new_streaming(data_source: DataSource) -> Result<Self, DataSource> {
Ok(Self(Resource::new(ResourceState::Ok(
SoundBufferState::Streaming(StreamingBuffer::new(data_source)?),
))))
}
pub fn new_generic(data_source: DataSource) -> Result<Self, DataSource> {
Ok(Self(Resource::new(ResourceState::Ok(
SoundBufferState::Generic(GenericBuffer::new(data_source)?),
))))
}
}
#[derive(Debug, Visit)]
pub enum SoundBufferState {
Generic(GenericBuffer),
Streaming(StreamingBuffer),
}
impl SoundBufferState {
pub fn raw_streaming(data_source: DataSource) -> Result<Self, DataSource> {
Ok(Self::Streaming(StreamingBuffer::new(data_source)?))
}
pub fn raw_generic(data_source: DataSource) -> Result<Self, DataSource> {
Ok(Self::Generic(GenericBuffer::new(data_source)?))
}
}
impl Default for SoundBufferState {
fn default() -> Self {
SoundBufferState::Generic(Default::default())
}
}
impl Deref for SoundBufferState {
type Target = GenericBuffer;
fn deref(&self) -> &Self::Target {
match self {
SoundBufferState::Generic(v) => v,
SoundBufferState::Streaming(v) => v,
}
}
}
impl DerefMut for SoundBufferState {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
SoundBufferState::Generic(v) => v,
SoundBufferState::Streaming(v) => v,
}
}
}
impl ResourceData for SoundBufferState {
fn path(&self) -> Cow<Path> {
Cow::from(&self.external_source_path)
}
fn set_path(&mut self, path: PathBuf) {
self.external_source_path = path;
}
}