Skip to content

Instantly share code, notes, and snippets.

@mbillingr
Created July 19, 2018 16:20
Show Gist options
  • Save mbillingr/25d1020bf8c0e56e6f1a3d8e1bc458c1 to your computer and use it in GitHub Desktop.
Save mbillingr/25d1020bf8c0e56e6f1a3d8e1bc458c1 to your computer and use it in GitHub Desktop.
Simple Ambisonics on top of CPAL (and inspired by Rodio)
extern crate cpal;
extern crate hound;
use std::f32;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::ops;
use cpal::{StreamData, UnknownTypeOutputBuffer};
trait Sample {
fn as_u16(&self) -> u16;
fn as_i16(&self) -> i16;
fn as_f32(&self) -> f32;
fn zero() -> Self;
}
/*
/// Blanket impl for references
impl<'a, T: Sample> Sample for &'a T {
fn as_u16(&self) -> u16 {
T::as_u16(self)
}
fn as_i16(&self) -> i16 {
T::as_i16(self)
}
fn as_f32(&self) -> f32 {
T::as_f32(self)
}
fn zero() -> Self {
T::zero()
}
}*/
impl Sample for i16 {
fn as_u16(&self) -> u16 {
((u16::max_value() / 2) as i32 + *self as i32) as u16
}
fn as_i16(&self) -> i16 {
*self
}
fn as_f32(&self) -> f32 {
*self as f32 / i16::max_value() as f32
}
fn zero() -> Self {
0
}
}
impl Sample for f32 {
fn as_u16(&self) -> u16 {
((u16::max_value() / 2) as i32 + self.as_i16() as i32) as u16
}
fn as_i16(&self) -> i16 {
(*self * i16::max_value() as f32) as i16
}
fn as_f32(&self) -> f32 {
*self
}
fn zero() -> Self {
0.0
}
}
trait Stream: Iterator {
fn n_channels(&self) -> usize;
fn broadcast(self, n: usize) -> Broadcast<Self::Item, Self>
where
Self: Sized,
Self::Item: Sample,
{
Broadcast {
inner: self,
n_channels: n,
current_sample: Self::Item::zero(),
reps_to_go: 0,
}
}
fn floatify(self) -> Floatify<Self>
where
Self: Sized,
{
Floatify {
inner: self,
}
}
fn repeat(self) -> Repeated<Self::Item>
where
Self: Sized,
{
Repeated {
n_channels: self.n_channels(),
buffer: self.collect(),
index: 0,
}
}
}
impl<'a, S> Stream for std::iter::Cloned<std::slice::Iter<'a, S>>
where S: Sample + Clone
{
fn n_channels(&self) -> usize {
1
}
}
impl<'a, S> Stream for std::vec::IntoIter<S>
where S: Sample + Clone
{
fn n_channels(&self) -> usize {
1
}
}
struct Floatify<T> {
inner: T,
}
impl<S, T> Stream for Floatify<T>
where
T: Stream<Item=S>,
S: Sample,
{
fn n_channels(&self) -> usize {
self.inner.n_channels()
}
}
impl<S, T> Iterator for Floatify<T>
where
T: Stream<Item=S>,
S: Sample,
{
type Item = f32;
fn next(&mut self) -> Option<f32> {
self.inner.next().map(|s| s.as_f32())
}
}
struct Repeated<S> {
n_channels: usize,
buffer: Vec<S>,
index: usize,
}
impl<S> Stream for Repeated<S>
where
S: Clone,
{
fn n_channels(&self) -> usize {
self.n_channels
}
}
impl<S> Iterator for Repeated<S>
where
S: Clone,
{
type Item = S;
fn next(&mut self) -> Option<S> {
if self.index >= self.buffer.len() {
if self.buffer.len() == 0 {
return None
}
self.index = 0;
}
let x = self.buffer[self.index].clone();
self.index += 1;
Some(x)
}
}
#[derive(Clone)]
struct SharedBuffer<S: 'static> {
n_channels: usize,
buffer: Arc<Vec<S>>,
index: usize,
}
impl<S> Stream for SharedBuffer<S>
where
S: Clone + 'static,
{
fn n_channels(&self) -> usize {
self.n_channels
}
}
impl<S> Iterator for SharedBuffer<S>
where
S: Clone + 'static,
{
type Item = S;
fn next(&mut self) -> Option<S> {
if self.index >= self.buffer.len() {
None
} else {
let x = self.buffer[self.index].clone();
self.index += 1;
Some(x)
}
}
}
struct Broadcast<S, T>
{
inner: T,
n_channels: usize,
current_sample: S,
reps_to_go: usize,
}
impl<S, T> Stream for Broadcast<S, T>
where
T: Stream<Item=S>,
S: Sample + Clone,
{
fn n_channels(&self) -> usize {
self.n_channels
}
}
impl<S, T> Iterator for Broadcast<S, T>
where
T: Stream<Item=S>,
S: Sample + Clone,
{
type Item = S;
fn next(&mut self) -> Option<Self::Item> {
if self.reps_to_go == 0 {
self.current_sample = self.inner.next()?;
self.reps_to_go = self.n_channels;
}
self.reps_to_go -= 1;
Some(self.current_sample.clone())
}
}
struct BFormat {
w: f32,
x: f32,
y: f32,
z: f32,
}
impl BFormat {
fn virtual_microphone(&self, dir: [f32; 3], p: f32) -> f32 {
p * 2f32.sqrt() * self.w + (1.0 - p) * (dir[0] * self.x + dir[1] * self.y + dir[2] * self.z)
}
}
impl Default for BFormat {
fn default() -> Self {
BFormat {w: 0.0, x: 0.0, y: 0.0, z: 0.0}
}
}
impl ops::AddAssign for BFormat {
fn add_assign(&mut self, other: Self) {
self.w += other.w;
self.x += other.x;
self.y += other.y;
self.z += other.z;
}
}
impl<'a> ops::Mul<f32> for &'a BFormat {
type Output = BFormat;
fn mul(self, s: f32) -> BFormat {
BFormat {
w: self.w * s,
x: self.x * s,
y: self.y * s,
z: self.z * s,
}
}
}
impl From<[f32; 3]> for BFormat {
fn from(dir: [f32; 3]) -> Self {
let l = (dir[0] * dir[0] + dir[1] * dir[1] + dir[1] * dir[2]).sqrt();
BFormat {
w: 1.0 / 2f32.sqrt(),
x: dir[0] / l,
y: dir[1] / l,
z: dir[2] / l,
}
}
}
struct BStreamStereoDecoder {
input: BStreamMixer,
left_mic: [f32; 3],
right_mic: [f32; 3],
buffered_sample: Option<f32>,
}
impl BStreamStereoDecoder {
fn new(input: BStreamMixer) -> Self {
BStreamStereoDecoder {
input,
left_mic: [-1.0 / 2f32.sqrt(), 1.0 / 2f32.sqrt(), 0.0],
right_mic: [1.0 / 2f32.sqrt(), 1.0 / 2f32.sqrt(), 0.0],
buffered_sample: None,
}
}
}
impl Stream for BStreamStereoDecoder {
fn n_channels(&self) -> usize {
2
}
}
impl Iterator for BStreamStereoDecoder {
type Item = f32;
fn next(&mut self) -> Option<f32> {
match self.buffered_sample.take() {
Some(s) => Some(s),
None => {
let sample = self.input.next()?;
let left = sample.virtual_microphone(self.left_mic, 0.5);
let right = sample.virtual_microphone(self.right_mic, 0.5);
// emit left channel now, and right channel on next call
self.buffered_sample = Some(right);
Some(left)
},
}
}
}
fn bstream() -> (BStreamMixer, Arc<BStreamController>) {
let controller = Arc::new(BStreamController {
pending_streams: Mutex::new(Vec::new()),
has_pending: AtomicBool::new(false),
});
let mixer = BStreamMixer {
input: controller.clone(),
active_streams: Vec::with_capacity(8),
};
(mixer, controller)
}
struct BStreamMixer {
input: Arc<BStreamController>,
active_streams: Vec<SpatialStream>,
}
impl Iterator for BStreamMixer {
type Item = BFormat;
fn next(&mut self) -> Option<Self::Item> {
if self.input.has_pending.load(Ordering::SeqCst) {
let mut pending = self.input.pending_streams.lock().expect("Cannot lock pending streams");
self.active_streams.extend(pending.drain(..));
self.input.has_pending.store(false, Ordering::SeqCst);
}
let mut mix = BFormat::default();
let mut done = Vec::new();
for (i, stream) in self.active_streams.iter_mut().enumerate() {
match stream.next() {
Some(x) => mix += x,
None => done.push(i),
}
}
for i in done {
self.active_streams.remove(i);
}
Some(mix)
}
}
struct BStreamController {
pending_streams: Mutex<Vec<SpatialStream>>,
has_pending: AtomicBool,
}
impl BStreamController {
fn attach<T: 'static + Stream<Item = f32> + Send>(&self, stream: T, direction: [f32; 3]) -> Arc<SpatialStreamController> {
let controller = Arc::new(SpatialStreamController{
direction: Mutex::new(direction),
update: AtomicBool::new(false),
});
let sstream = SpatialStream {
inner: Box::new(stream),
bfactors: direction.into(),
controller: controller.clone(),
};
self.pending_streams.lock().expect("Cannot lock pending streams").push(sstream);
self.has_pending.store(true, Ordering::SeqCst);
controller
}
}
struct SpatialStream {
inner: Box<Stream<Item = f32> + Send>,
bfactors: BFormat,
controller: Arc<SpatialStreamController>,
}
impl Iterator for SpatialStream {
type Item = BFormat;
fn next(&mut self) -> Option<Self::Item> {
if self.controller.update.load(Ordering::SeqCst) {
self.bfactors = (*self.controller.direction.lock().unwrap()).into();
self.controller.update.store(false, Ordering::SeqCst);
}
let x = self.inner.next()?;
Some(&self.bfactors * x)
}
}
struct SpatialStreamController {
direction: Mutex<[f32; 3]>,
update: AtomicBool,
}
impl SpatialStreamController {
fn set_direction(&self, d: [f32; 3]) {
*self.direction.lock().unwrap() = d;
self.update.store(true, Ordering::SeqCst);
}
}
fn stream_player<T>(device: &cpal::Device, format: &cpal::Format, mut stream: T) -> thread::JoinHandle<()>
where
T: Stream + Send + 'static,
T::Item: Sample,
{
let event_loop = cpal::EventLoop::new();
let stream_id = event_loop.build_output_stream(&device, &format).unwrap();
event_loop.play_stream(stream_id);
thread::spawn(move || {
event_loop.run(move |_stream_id, stream_data| {
match stream_data {
StreamData::Output { buffer: UnknownTypeOutputBuffer::U16(mut buffer) } => {
for elem in buffer.iter_mut() {
*elem = stream.next().unwrap().as_u16();
}
},
StreamData::Output { buffer: UnknownTypeOutputBuffer::I16(mut buffer) } => {
for elem in buffer.iter_mut() {
*elem = stream.next().unwrap().as_i16();
}
},
StreamData::Output { buffer: UnknownTypeOutputBuffer::F32(mut buffer) } => {
for elem in buffer.iter_mut() {
*elem = stream.next().unwrap().as_f32();
}
},
_ => (),
}
});
})
}
fn main() {
println!("Hello, world!");
let mut reader = hound::WavReader::open(r"U:\Ling\Audio wav files\short\A.wav").unwrap();
println!("{:?}", reader.spec());
let sound: Vec<_> = reader
.samples::<i16>()
.map(|s| s.unwrap())
.collect();
let sound = Arc::new(sound);
let sound = SharedBuffer {
n_channels: 1,
buffer: sound,
index: 0,
};
println!("Audio Devices:");
for device in cpal::devices() {
println!(" -> {}", device.name());
}
let device = cpal::default_output_device().expect("no output device available");
println!("Default Device:\n {}", device.name());
let supported_formats_range = device.supported_output_formats()
.expect("error while querying formats");
println!("Supported formats:");
for format in supported_formats_range {
println!("{:?}", format);
}
let format = device.supported_output_formats().unwrap().next()
.expect("no supported format?!")
.with_max_sample_rate();
let (mixer, controller) = bstream();
let decoder = BStreamStereoDecoder::new(mixer);
let player = stream_player(&device, &format, decoder);
/*for a in 0..=16 {
let a = a as f32 / 16.0 * 2.0 * f32::consts::PI;
controller.attach(sound.clone().into_iter().floatify(), [a.sin(), a.cos(), 0.0]);
thread::sleep_ms(1000);
}*/
//let player = stream_player(&device, &format, sound.into_iter().broadcast(2));
//let sc = controller.attach(sound.clone().into_iter().floatify().repeat(), [0.0, 1.0, 0.0]);
//controller.attach(sound.clone().floatify(), [-1.0, 0.0, 0.0]);
//thread::sleep_ms(500);
let sc = controller.attach(sound.floatify().repeat(), [0.0, 1.0, 0.0]);
for a in 0..1000 {
let a = a as f32 / 100.0;
sc.set_direction([a.sin(), a.cos(), 0.0]);
thread::sleep_ms(10);
}
//thread::sleep_ms(5000);
//player.join();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment