1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
//! 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::FileLoadError, reflect::prelude::*, uuid::Uuid, visitor::prelude::*, TypeUuidProvider,
};
use fyrox_resource::{Resource, ResourceData, SOUND_BUFFER_RESOURCE_UUID};
use std::{
    any::Any,
    borrow::Cow,
    fmt::Debug,
    io::{Cursor, Read, Seek, SeekFrom},
    ops::{Deref, DerefMut},
    path::{Path, PathBuf},
    time::Duration,
};

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,

        /// Buffered file opened for read.
        #[cfg(not(target_arch = "wasm32"))]
        data: std::io::BufReader<std::fs::File>,

        /// TODO: In case of WASM load file entirely.
        #[cfg(target_arch = "wasm32")]
        data: Cursor<Vec<u8>>,
    },

    /// 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.
    fn rewind(&mut self) -> Result<(), SoundError> {
        Ok(())
    }

    /// Allows you to start playback from given duration.
    fn time_seek(&mut self, _duration: Duration) {}

    /// Returns total duration of data. Can be `None` if internal decoder does not supports seeking.
    fn duration(&self) -> Option<Duration> {
        None
    }
}

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) -> 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(fyrox_core::io::load_file(path).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))
    }
}

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!")
            }
        }
    }
}

/// 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(FileLoadError),
}

/// Sound buffer is a data source for sound sources. See module documentation for more info.
#[derive(Debug, Visit, Reflect)]
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),
}

/// 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>, DataSource>;

    /// Tries to create new generic sound buffer from a given data source.
    fn new_generic(data_source: DataSource) -> Result<Resource<SoundBuffer>, DataSource>;
}

impl SoundBufferResourceExtension for SoundBufferResource {
    fn new_streaming(data_source: DataSource) -> Result<Resource<SoundBuffer>, DataSource> {
        Ok(Resource::new_ok(SoundBuffer::Streaming(
            StreamingBuffer::new(data_source)?,
        )))
    }

    fn new_generic(data_source: DataSource) -> Result<Resource<SoundBuffer>, DataSource> {
        Ok(Resource::new_ok(SoundBuffer::Generic(GenericBuffer::new(
            data_source,
        )?)))
    }
}

impl TypeUuidProvider for SoundBuffer {
    fn type_uuid() -> Uuid {
        SOUND_BUFFER_RESOURCE_UUID
    }
}

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, DataSource> {
        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, DataSource> {
        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 path(&self) -> Cow<Path> {
        Cow::from(&self.external_source_path)
    }

    fn set_path(&mut self, path: PathBuf) {
        self.external_source_path = path;
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn type_uuid(&self) -> Uuid {
        SOUND_BUFFER_RESOURCE_UUID
    }
}