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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
//! Loading sound files and other data and reading from them.
//! Module containing buffer functionality:
//! - [`Buffer`] for storing sound and other data
//! - [`BufferReader`] for reading a single channel [`Buffer`] or only the first channel from a multi channel buffer
//! - [`BufferReaderMulti`] for reading multiple channels from a [`Buffer`]. The number of channels is fixed once it has been added to a [`Graph`]
use std::{fs::File, path::PathBuf};
#[allow(unused)]
use crate::gen::BufferReader;
#[allow(unused)]
use crate::gen::BufferReaderMulti;
#[allow(unused)]
use crate::graph::Graph;
use slotmap::new_key_type;
use symphonia::core::errors::Error as SymphoniaError;
use symphonia::core::{
audio::SampleBuffer,
codecs::{DecoderOptions, CODEC_TYPE_NULL},
formats::FormatOptions,
io::MediaSourceStream,
meta::MetadataOptions,
probe::Hint,
};
use crate::SampleRate;
use super::Sample;
#[allow(missing_docs)]
#[derive(thiserror::Error, Debug)]
pub enum BufferError {
#[error("Tried to load a file in an unsupported format: {0}")]
FileFormatNotSupported(PathBuf),
#[error("Symphonia error: {0}")]
SymphoniaError(#[from] SymphoniaError),
}
new_key_type! {
/// Refer to a specific buffer in a SlotMap
pub struct BufferKey;
}
/// A buffer containing sound or data. Channels are stored interleaved in a 1-dimensional list.
#[derive(Clone, Debug)]
pub struct Buffer {
buffer: Vec<Sample>,
num_channels: usize,
/// Size in number of frames independent on the number of channels.
num_frames: f64,
/// The sample rate of the buffer, can be different from the sample rate of the audio server
sample_rate: f64,
}
impl Buffer {
/// Create an empty buffer with the specified options
pub fn new(size: usize, num_channels: usize, sample_rate: f64) -> Self {
Buffer {
buffer: vec![0.0; size],
num_channels,
num_frames: size as f64,
sample_rate,
}
}
/// Create a [`Buffer`] from a single channel buffer.
pub fn from_vec(buffer: Vec<Sample>, sample_rate: f64) -> Self {
let size = buffer.len() as f64;
Buffer {
buffer,
num_channels: 1,
num_frames: size,
sample_rate,
}
}
/// Create a [`Buffer`] from a multi channel buffer. Channels should be
/// interleaved e.g. [sample0_channel0, sample0_channel1, sample1_channel0,
/// sample1_channel1, ..] etc
pub fn from_vec_interleaved(
buffer: Vec<Sample>,
num_channels: usize,
sample_rate: f64,
) -> Self {
let size = (buffer.len() / num_channels) as f64;
Buffer {
buffer,
num_channels,
num_frames: size,
sample_rate,
}
}
/// Create a [`Buffer`] by loading a sound file from disk. Currently
/// supported file formats: Wave, Ogg Vorbis, FLAC, MP3
pub fn from_sound_file(path: impl Into<PathBuf>) -> Result<Self, BufferError> {
let path = path.into();
let mut buffer = Vec::new();
let inp_file = File::open(&path).expect("Buffer: failed to open file!");
// hint to the format registry of the decoder what file format it might be
let mut hint = Hint::new();
// Provide the file extension as a hint.
if let Some(extension) = path.extension() {
if let Some(extension_str) = extension.to_str() {
hint.with_extension(extension_str);
}
}
let mss = MediaSourceStream::new(Box::new(inp_file), Default::default());
// Use the default options for metadata and format readers.
let format_opts: FormatOptions = Default::default();
let metadata_opts: MetadataOptions = Default::default();
let mut sample_buf = None;
// Probe the media source stream for metadata and get the format reader.
let codec_params = match symphonia::default::get_probe().format(
&hint,
mss,
&format_opts,
&metadata_opts,
) {
Ok(probed) => {
let mut reader = probed.format;
// Find the first audio track with a known (decodeable) codec.
let track = reader
.tracks()
.iter()
.find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
.expect("no supported audio tracks");
// Set the decoder options.
let decode_options = DecoderOptions {
..Default::default()
};
// Create a decoder for the stream.
let mut decoder = symphonia::default::get_codecs()
.make(&track.codec_params, &decode_options)
.expect("unsupported codec");
let codec_params = track.codec_params.clone();
// Store the track identifier, it will be used to filter packets.
let track_id = track.id;
// The decode loop.
loop {
// Get the next packet from the media format.
let packet = match reader.next_packet() {
Ok(packet) => packet,
Err(SymphoniaError::ResetRequired) => {
// The track list has been changed. Re-examine it and create a new set of decoders,
// then restart the decode loop. This is an advanced feature and it is not
// unreasonable to consider this "the end." As of v0.5.0, the only usage of this is
// for chained OGG physical streams.
unimplemented!();
}
Err(err) => match err {
SymphoniaError::IoError(_e) => {
// println!("{e}");
break;
}
SymphoniaError::DecodeError(_) => todo!(),
SymphoniaError::SeekError(_) => todo!(),
SymphoniaError::Unsupported(_) => todo!(),
SymphoniaError::LimitError(_) => todo!(),
SymphoniaError::ResetRequired => todo!(),
},
};
// Consume any new metadata that has been read since the last packet.
while !reader.metadata().is_latest() {
// Pop the old head of the metadata queue.
reader.metadata().pop();
// Consume the new metadata at the head of the metadata queue.
}
// If the packet does not belong to the selected track, skip over it.
if packet.track_id() != track_id {
continue;
}
// Decode the packet into audio samples.
match decoder.decode(&packet) {
Ok(audio_buf) => {
// Consume the decoded audio samples
// If this is the *first* decoded packet, create a sample buffer matching the
// decoded audio buffer format.
if sample_buf.is_none() {
// Get the audio buffer specification.
let spec = *audio_buf.spec();
// Get the capacity of the decoded buffer. Note: This is capacity, not length!
let duration = audio_buf.capacity() as u64;
// Create the Sample sample buffer.
sample_buf = Some(SampleBuffer::<Sample>::new(duration, spec));
}
// Copy the decoded audio buffer into the sample buffer in an interleaved format.
if let Some(buf) = &mut sample_buf {
buf.copy_interleaved_ref(audio_buf);
// TODO: Get only one channel
for sample in buf.samples() {
buffer.push(*sample);
}
}
}
Err(SymphoniaError::IoError(_)) => {
// The packet failed to decode due to an IO error, skip the packet.
continue;
}
Err(SymphoniaError::DecodeError(_)) => {
// The packet failed to decode due to invalid data, skip the packet.
continue;
}
Err(err) => {
// An unrecoverable error occured, halt decoding.
return Err(From::from(err));
}
}
}
codec_params
}
Err(_err) => {
// The input was not supported by any format reader.
return Err(BufferError::FileFormatNotSupported(path));
}
};
let (sampling_rate, num_channels) = {
let cp = codec_params;
// println!(
// "channels: {}, rate: {}, num samples: {}",
// cp.channels.unwrap(),
// cp.sample_rate.unwrap(),
// buffer.len()
// );
// The channels are stored as a bit field
// https://docs.rs/symphonia-core/0.5.1/src/symphonia_core/audio.rs.html#29-90
// The number of bits set to 1 is the number of channels in the buffer.
(
cp.sample_rate.unwrap() as f64,
cp.channels.unwrap().bits().count_ones() as usize,
)
};
let buffer = buffer.into_iter().map(|v| v as Sample).collect();
// TODO: Return Err if there's no audio data
Ok(Self::from_vec_interleaved(
buffer,
num_channels,
sampling_rate,
))
}
/// Returns the step size in samples for playing this buffer with the correct speed
pub fn buf_rate_scale(&self, server_sample_rate: SampleRate) -> f64 {
self.sample_rate / f64::from(server_sample_rate)
}
/// Linearly interpolate between the value in between to samples
#[inline]
pub fn get_linear_interp(&self, index: Sample) -> Sample {
let mix = index.fract();
let index_u = index as usize;
unsafe {
*self.buffer.get_unchecked(index_u) * (1.0 - mix)
+ *self.buffer.get_unchecked((index_u + 1) % self.buffer.len()) * mix
}
}
/// Get the samples for all channels at the index.
#[inline]
pub fn get_interleaved(&self, index: usize) -> &[Sample] {
let index = index * self.num_channels;
&self.buffer[index..index + self.num_channels]
// unsafe{ *self.buffer.get_unchecked(index) }
}
/// Save the buffer to a 16 bit wave file
pub fn save_to_disk(&self, path: impl Into<PathBuf>) -> Result<(), hound::Error> {
let spec = hound::WavSpec {
channels: self.num_channels as u16,
sample_rate: self.sample_rate as u32,
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
let mut writer = hound::WavWriter::create(path.into(), spec)?;
for sample in &self.buffer {
let amplitude = i16::MAX as Sample;
writer.write_sample((sample * amplitude) as i16)?;
}
Ok(())
}
/// Size in number of frames regardless of the number of samples
pub fn num_frames(&self) -> f64 {
self.num_frames
}
/// The number of channels in the Buffer. Mustn't change once uploaded/inserted in a Resources or anything else on the audio thread.
pub fn num_channels(&self) -> usize {
self.num_channels
}
/// The sample rate of the buffer. This depends on the loaded sound file or generated buffer and may be different from the sample rate of a graph playing the buffer.
pub fn sample_rate(&self) -> usize {
self.sample_rate as usize
}
/// Returns the length of the buffer in seconds
pub fn length_seconds(&self) -> f64 {
self.num_frames / self.sample_rate
}
/// Apply a DC highpass filter to the buffer content
pub fn remove_dc(&mut self) {
let mut prev = vec![0.0; self.num_channels];
let mut lpf_sample = vec![0.0; self.num_channels];
for (i, sample) in self.buffer.iter_mut().enumerate() {
let c = i % self.num_channels;
lpf_sample[c] = lpf_sample[c] * 0.999 + *sample - prev[c];
prev[c] = *sample;
*sample = lpf_sample[c];
}
}
}