rg3d_sound/buffer/generic.rs
1//! Generic buffer module.
2//!
3//! # Overview
4//!
5//! Generic buffer is just an array of raw samples in IEEE float format with some additional info (sample rate,
6//! channel count, etc.). All samples stored in interleaved format, which means samples for each channel are
7//! near - `LRLRLR...`.
8//!
9//! # Usage
10//!
11//! Generic source can be created like so:
12//!
13//! ```no_run
14//! use std::sync::{Mutex, Arc};
15//! use rg3d_sound::buffer::{SoundBufferResource, DataSource};
16//!
17//! async fn make_buffer() -> SoundBufferResource {
18//! let data_source = DataSource::from_file("sound.wav").await.unwrap();
19//! SoundBufferResource::new_generic(data_source).unwrap()
20//! }
21//! ```
22
23#![allow(clippy::manual_range_contains)]
24
25use crate::{buffer::DataSource, decoder::Decoder};
26use rg3d_core::visitor::{Visit, VisitResult, Visitor};
27use std::path::Path;
28use std::{path::PathBuf, time::Duration};
29
30/// Generic sound buffer that contains decoded samples and allows random access.
31#[derive(Debug, Default)]
32pub struct GenericBuffer {
33 /// Interleaved decoded samples (mono sounds: L..., stereo sounds: LR...)
34 /// For streaming buffers it contains only small part of decoded data
35 /// (usually something around 1 sec).
36 pub(in crate) samples: Vec<f32>,
37 pub(in crate) channel_count: usize,
38 pub(in crate) sample_rate: usize,
39 pub(in crate) external_source_path: PathBuf,
40}
41
42impl Visit for GenericBuffer {
43 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
44 visitor.enter_region(name)?;
45
46 self.external_source_path.visit("Path", visitor)?;
47
48 visitor.leave_region()
49 }
50}
51
52impl GenericBuffer {
53 /// Creates new generic buffer from specified data source. May fail if data source has unsupported
54 /// format, corrupted, etc.
55 ///
56 /// # Notes
57 ///
58 /// `DataSource::RawStreaming` is **not** supported with generic buffers, use streaming buffer
59 /// instead!
60 ///
61 /// Data source with raw samples must have sample count multiple of channel count, otherwise this
62 /// function will return `Err`.
63 pub fn new(source: DataSource) -> Result<Self, DataSource> {
64 match source {
65 DataSource::Raw {
66 sample_rate,
67 channel_count,
68 samples,
69 } => {
70 if channel_count < 1 || channel_count > 2 || samples.len() % channel_count != 0 {
71 Err(DataSource::Raw {
72 sample_rate,
73 channel_count,
74 samples,
75 })
76 } else {
77 Ok(Self {
78 samples,
79 channel_count,
80 sample_rate,
81 external_source_path: Default::default(),
82 })
83 }
84 }
85 DataSource::RawStreaming(_) => Err(source),
86 _ => {
87 let external_source_path = if let DataSource::File { path, .. } = &source {
88 path.clone()
89 } else {
90 Default::default()
91 };
92
93 // Store cursor to handle errors.
94 let (is_memory, external_cursor) = if let DataSource::Memory(cursor) = &source {
95 (true, cursor.clone())
96 } else {
97 (false, Default::default())
98 };
99
100 let decoder = Decoder::new(source)?;
101 if decoder.get_channel_count() < 1 || decoder.get_channel_count() > 2 {
102 if is_memory {
103 return Err(DataSource::Memory(external_cursor));
104 } else {
105 // There is not much we can do here: if the user supplied DataSource::File,
106 // they probably do not want us to re-read the file again in
107 // DataSource::from_file.
108 return Err(DataSource::Raw {
109 sample_rate: decoder.get_sample_rate(),
110 channel_count: decoder.get_channel_count(),
111 samples: vec![],
112 });
113 }
114 }
115
116 Ok(Self {
117 sample_rate: decoder.get_sample_rate(),
118 channel_count: decoder.get_channel_count(),
119 samples: decoder.into_samples(),
120 external_source_path,
121 })
122 }
123 }
124 }
125
126 /// In case if buffer was created from file, this method returns file name. Can be useful for
127 /// serialization needs where you just need to know which file needs to be reloaded from disk
128 /// when you loading a saved game.
129 #[inline]
130 pub fn external_data_path(&self) -> &Path {
131 &self.external_source_path
132 }
133
134 /// Sets new path for external data source.
135 pub fn set_external_data_path(&mut self, path: PathBuf) -> PathBuf {
136 std::mem::replace(&mut self.external_source_path, path)
137 }
138
139 /// Checks if buffer is empty or not.
140 #[inline]
141 pub fn is_empty(&self) -> bool {
142 self.samples.is_empty()
143 }
144
145 /// Returns shared reference to an array with samples.
146 #[inline]
147 pub fn samples(&self) -> &[f32] {
148 &self.samples
149 }
150
151 /// Returns mutable reference to an array with samples that could be modified.
152 pub fn samples_mut(&mut self) -> &mut [f32] {
153 &mut self.samples
154 }
155
156 /// Returns exact amount of channels in the buffer.
157 #[inline]
158 pub fn channel_count(&self) -> usize {
159 self.channel_count
160 }
161
162 /// Returns sample rate of the buffer.
163 #[inline]
164 pub fn sample_rate(&self) -> usize {
165 self.sample_rate
166 }
167
168 /// Returns exact duration of the buffer.
169 #[inline]
170 pub fn duration(&self) -> Duration {
171 Duration::from_secs_f64(
172 (self.samples.len() / (self.channel_count * self.sample_rate)) as f64,
173 )
174 }
175}