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
//! 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};
//!
//! async fn make_buffer() -> SoundBufferResource {
//!     let data_source = DataSource::from_file("sound.wav").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::{path::Path, path::PathBuf, time::Duration};

/// Generic sound buffer that contains decoded samples and allows random access.
#[derive(Debug, Default, Visit, Reflect)]
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: Vec<f32>,
    #[visit(skip)]
    pub(crate) channel_count: usize,
    #[visit(skip)]
    pub(crate) sample_rate: usize,
    #[visit(rename = "Path")]
    pub(crate) external_source_path: PathBuf,
}

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, DataSource> {
        match source {
            DataSource::Raw {
                sample_rate,
                channel_count,
                samples,
            } => {
                if channel_count < 1 || channel_count > 2 || samples.len() % channel_count != 0 {
                    Err(DataSource::Raw {
                        sample_rate,
                        channel_count,
                        samples,
                    })
                } else {
                    Ok(Self {
                        samples,
                        channel_count,
                        sample_rate,
                        external_source_path: Default::default(),
                    })
                }
            }
            DataSource::RawStreaming(_) => Err(source),
            _ => {
                let external_source_path = if let DataSource::File { path, .. } = &source {
                    path.clone()
                } else {
                    Default::default()
                };

                // Store cursor to handle errors.
                let (is_memory, external_cursor) = if let DataSource::Memory(cursor) = &source {
                    (true, cursor.clone())
                } else {
                    (false, Default::default())
                };

                let decoder = Decoder::new(source)?;
                if decoder.get_channel_count() < 1 || decoder.get_channel_count() > 2 {
                    if is_memory {
                        return Err(DataSource::Memory(external_cursor));
                    } else {
                        // There is not much we can do here: if the user supplied DataSource::File,
                        // they probably do not want us to re-read the file again in
                        // DataSource::from_file.
                        return Err(DataSource::Raw {
                            sample_rate: decoder.get_sample_rate(),
                            channel_count: decoder.get_channel_count(),
                            samples: vec![],
                        });
                    }
                }

                Ok(Self {
                    sample_rate: decoder.get_sample_rate(),
                    channel_count: decoder.get_channel_count(),
                    samples: decoder.into_samples(),
                    external_source_path,
                })
            }
        }
    }

    /// In case if buffer was created from file, this method returns file name. Can be useful for
    /// serialization needs where you just need to know which file needs to be reloaded from disk
    /// when you loading a saved game.
    #[inline]
    pub fn external_data_path(&self) -> &Path {
        &self.external_source_path
    }

    /// Sets new path for external data source.
    pub fn set_external_data_path(&mut self, path: PathBuf) -> PathBuf {
        std::mem::replace(&mut self.external_source_path, path)
    }

    /// 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 duration of the buffer.
    #[inline]
    pub fn duration(&self) -> Duration {
        Duration::from_secs_f64(
            (self.samples.len() / (self.channel_count * self.sample_rate)) as f64,
        )
    }
}