From 4e48fbed2114b8b47b4ed8d62750c29617fb2b6d Mon Sep 17 00:00:00 2001 From: Jeron Aldaron Lau Date: Wed, 16 Feb 2022 20:12:01 -0600 Subject: [PATCH] Add audio mixing example. --- Cargo.toml | 2 +- examples/mix.rs | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ src/math.rs | 2 +- 3 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 examples/mix.rs diff --git a/Cargo.toml b/Cargo.toml index cf40392..a63b815 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ categories = [ "data-structures", "no-std", ] -include = ["Cargo.toml", "src/*"] +include = ["README.md", "Cargo.toml", "src/*"] [dependencies] libm = "0.2" diff --git a/examples/mix.rs b/examples/mix.rs new file mode 100644 index 0000000..234f9a8 --- /dev/null +++ b/examples/mix.rs @@ -0,0 +1,117 @@ +// Audio mixing example + +use fon::{ + chan::{Ch32, Channel}, + Audio, Frame, Sink, Stream, +}; +use std::num::NonZeroU32; + +#[derive(Debug)] +pub struct Mixer<'a, Chan: Channel, const CH: usize> { + index: usize, + audio: &'a mut Audio, +} + +#[allow(single_use_lifetimes)] +impl<'a, Chan: Channel, const CH: usize> Mixer<'a, Chan, CH> { + #[inline(always)] + fn new(audio: &'a mut Audio) -> Self { + let index = 0; + + Mixer { index, audio } + } +} + +// Using '_ results in reserved lifetime error. +#[allow(single_use_lifetimes)] +impl<'a, Chan: Channel, const CH: usize> Sink + for Mixer<'a, Chan, CH> +{ + #[inline(always)] + fn sample_rate(&self) -> NonZeroU32 { + self.audio.sample_rate() + } + + #[inline(always)] + fn len(&self) -> usize { + self.audio.len() + } + + #[inline(always)] + fn sink_with(&mut self, iter: &mut dyn Iterator>) { + let mut this = self; + Sink::::sink_with(&mut this, iter) + } +} + +impl Sink + for &mut Mixer<'_, Chan, CH> +{ + #[inline(always)] + fn sample_rate(&self) -> NonZeroU32 { + self.audio.sample_rate() + } + + #[inline(always)] + fn len(&self) -> usize { + self.audio.len() + } + + #[inline(always)] + fn sink_with(&mut self, iter: &mut dyn Iterator>) { + for frame in self.audio.iter_mut().skip(self.index) { + if let Some(other) = iter.next() { + for (channel, chan) in + frame.channels_mut().iter_mut().zip(other.channels()) + { + *channel += *chan; + } + } else { + break; + } + self.index += 1; + } + } +} + +fn load_file(in_hz: u32, in_file: &str) -> Audio { + // Load file as f32 buffer. + let rawfile = std::fs::read(in_file).unwrap(); + let mut audio = Vec::new(); + for sample in rawfile.chunks(4) { + audio.push(f32::from_le_bytes(sample.try_into().unwrap())); + } + // Create type-safe audio type from f32 buffer. + Audio::with_f32_buffer(in_hz, audio) +} + +fn save_file(name: &str, audio: &Audio) -> std::io::Result<()> { + // Convert audio to byte buffer + let mut samples = Vec::::new(); + for frame in audio.iter() { + for channel in frame.channels() { + samples.extend(channel.to_f32().to_le_bytes()); + } + } + // Save byte buffer + std::fs::write(name, samples) +} + +fn main() -> std::io::Result<()> { + // We are mixing file 1 audio down into file 2 audio. + + // Load file 1 + let source = load_file(44_100, "examples/44_1k.raw"); + // Load file 2 + let mut out = load_file(48_000, "examples/48k.raw"); + // Create mixer sink over output buffer + let mut mixer = Mixer::new(&mut out); + + // Create a stream to convert to 48k + let mut stream = Stream::<2>::new(48_000); + stream.pipe(&source, &mut mixer); + stream.flush(&mut mixer); + + // Save the mixed audio + save_file("examples/output.raw", &out) +} diff --git a/src/math.rs b/src/math.rs index 3247dfd..11ab8f7 100644 --- a/src/math.rs +++ b/src/math.rs @@ -9,7 +9,7 @@ use core::ops::Rem; -/// Floating point methods currently only availabe on std, that may be +/// Floating point methods currently only available on std, that may be /// implemented with the libm crate as dependency of core in the future. pub(crate) trait Libm: Rem + Sized { fn sin(self) -> Self;