-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic waveform view and playback
- Loading branch information
Showing
40 changed files
with
840 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use super::utils::deinterleave; | ||
use hound::{SampleFormat, WavReader}; | ||
|
||
/// An audio file, loaded into memory. | ||
pub struct AudioData { | ||
/// The sample data. | ||
pub data: Vec<f32>, | ||
/// Sample rate of the audio file. | ||
pub sample_rate: f64, | ||
/// number of channels in the audio file. | ||
pub num_channels: usize, | ||
/// number of samples in the audio file. | ||
pub num_samples: usize, | ||
} | ||
|
||
impl AudioData { | ||
/// return a buffer of samples corresponding to a channel in the audio file | ||
#[allow(dead_code)] | ||
pub fn get_channel(&self, idx: usize) -> &'_ [f32] { | ||
debug_assert!(idx < self.num_channels); | ||
let start = self.num_samples * idx; | ||
&self.data[start..(start + self.num_samples)] | ||
} | ||
|
||
/// open a file | ||
pub fn open(path: &str) -> Result<Self, hound::Error> { | ||
let mut reader = WavReader::open(path)?; | ||
let spec = reader.spec(); | ||
let mut data = Vec::with_capacity((spec.channels as usize) * (reader.duration() as usize)); | ||
match (spec.bits_per_sample, spec.sample_format) { | ||
(16, SampleFormat::Int) => { | ||
for sample in reader.samples::<i16>() { | ||
data.push((sample? as f32) / (0x7fffi32 as f32)); | ||
} | ||
} | ||
(24, SampleFormat::Int) => { | ||
for sample in reader.samples::<i32>() { | ||
let val = (sample? as f32) / (0x00ff_ffffi32 as f32); | ||
data.push(val); | ||
} | ||
} | ||
(32, SampleFormat::Int) => { | ||
for sample in reader.samples::<i32>() { | ||
data.push((sample? as f32) / (0x7fff_ffffi32 as f32)); | ||
} | ||
} | ||
(32, SampleFormat::Float) => { | ||
for sample in reader.samples::<f32>() { | ||
data.push(sample?); | ||
} | ||
} | ||
_ => return Err(hound::Error::Unsupported), | ||
} | ||
|
||
let mut deinterleaved = vec![0.0; data.len()]; | ||
let num_channels = spec.channels as usize; | ||
let num_samples = deinterleaved.len() / num_channels; | ||
deinterleave(&data, &mut deinterleaved, num_channels); | ||
Ok(Self { | ||
data: deinterleaved, | ||
sample_rate: spec.sample_rate as f64, | ||
num_channels, | ||
num_samples, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use crate::utils::interleave; | ||
use cpal::traits::{DeviceTrait, HostTrait}; | ||
use cpal::Stream; | ||
|
||
/// The playback context is used by the audio callback to map data from the audio | ||
/// file to the playback buffer. | ||
pub struct PlaybackContext<'a> { | ||
pub buffer_size: usize, | ||
pub sample_rate: f64, | ||
pub num_channels: usize, | ||
output_buffer: &'a mut [f32], | ||
} | ||
|
||
impl<'a> PlaybackContext<'a> { | ||
/// Return a buffer of output samples corresponding to a channel index | ||
pub fn get_output(&mut self, idx: usize) -> &'_ mut [f32] { | ||
let offset = idx * self.buffer_size; | ||
&mut self.output_buffer[offset..offset + self.buffer_size] | ||
} | ||
} | ||
|
||
/// Start the audio stream | ||
pub fn audio_stream(mut main_callback: impl FnMut(PlaybackContext) + Send + 'static) -> Stream { | ||
let host = cpal::default_host(); | ||
let output_device = host.default_output_device().expect("no output found"); | ||
let config = output_device.default_output_config().expect("no default output config").config(); | ||
|
||
let sample_rate = config.sample_rate.0 as f64; | ||
let num_channels = config.channels as usize; | ||
let mut output_buffer = vec![]; | ||
let mut input_buffer = vec![]; | ||
|
||
output_buffer.resize_with(1 << 16, || 0.0); | ||
input_buffer.resize_with(1 << 16, || 0.0); | ||
|
||
let callback = move |data: &mut [f32], _: &cpal::OutputCallbackInfo| { | ||
let buffer_size = data.len() / num_channels; | ||
output_buffer.resize(data.len(), 0.0); | ||
for sample in data.iter_mut() { | ||
*sample = 0.0; | ||
} | ||
for sample in &mut output_buffer.iter_mut() { | ||
*sample = 0.0; | ||
} | ||
|
||
let context = PlaybackContext { | ||
buffer_size, | ||
num_channels, | ||
sample_rate, | ||
output_buffer: &mut output_buffer, | ||
}; | ||
|
||
main_callback(context); | ||
interleave(&output_buffer, data, num_channels); | ||
}; | ||
|
||
output_device | ||
.build_output_stream(&config, callback, |err| eprintln!("{}", err), None) | ||
.expect("failed to open stream") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
pub mod audio_data; | ||
pub use audio_data::*; | ||
|
||
pub mod utils; | ||
pub use utils::*; | ||
|
||
pub mod audio_stream; | ||
pub use audio_stream::*; | ||
|
||
pub mod sample_player; | ||
pub use sample_player::*; | ||
|
||
pub mod waveform; | ||
pub use waveform::*; |
Oops, something went wrong.