From 52b420e258609999ef8006351a359acdd2ec107a Mon Sep 17 00:00:00 2001 From: Connor Slade Date: Sat, 1 Jun 2024 16:09:14 -0400 Subject: [PATCH] Configure audio pickup in demo config --- configs/reverb/params.toml | 9 ++- configs/reverb/shader.wgsl | 9 +++ configs/spacial_audio/params.toml | 17 +++++ configs/spacial_audio/shader.wgsl | 9 +++ src/config.rs | 1 + src/misc/audio.rs | 36 ++++----- src/misc/preprocess.rs | 119 ++++++++++++++++++++++++------ src/shaders/shader.wgsl | 13 ++-- src/simulation.rs | 21 ++++-- 9 files changed, 180 insertions(+), 54 deletions(-) create mode 100644 configs/reverb/shader.wgsl create mode 100644 configs/spacial_audio/params.toml create mode 100644 configs/spacial_audio/shader.wgsl diff --git a/configs/reverb/params.toml b/configs/reverb/params.toml index 3b81c83..62e2b00 100644 --- a/configs/reverb/params.toml +++ b/configs/reverb/params.toml @@ -2,10 +2,15 @@ size = [1920, 1080] reflective_boundary = false map = "map.png" +shader = "shader.wgsl" dx = 0.0810 dt = 0.0625 v = 340.29 -amplitude = 0.3 -frequency = 4.0 # in x100 Hz +amplitude = 1.5 +frequency = 4.0 # in x100 Hz + +[audio] +input = "input.wav" +output = "output-circle.wav" diff --git a/configs/reverb/shader.wgsl b/configs/reverb/shader.wgsl new file mode 100644 index 0000000..28ed51e --- /dev/null +++ b/configs/reverb/shader.wgsl @@ -0,0 +1,9 @@ +// Damping +*mul *= 1 - 0.0001; + +// Emitter +let emitter = vec2(f32(ctx.width) / 2.0, f32(ctx.height) / 2.0); +*distance = distance(vec2(f32(x), f32(y)), emitter); + +// Boundary conditions +*wall = x != 240; \ No newline at end of file diff --git a/configs/spacial_audio/params.toml b/configs/spacial_audio/params.toml new file mode 100644 index 0000000..e14094a --- /dev/null +++ b/configs/spacial_audio/params.toml @@ -0,0 +1,17 @@ +size = [1920, 1080] +reflective_boundary = false + +shader = "shader.wgsl" + +dx = 0.0810 +dt = 0.0625 + +v = 340.29 +amplitude = 1.0 +frequency = 4.0 # in x100 Hz + +[audio] +input = "../reverb/input.wav" +output = "output-left.wav" +pickup = [959, 540] # left +# pickup = [961, 540] # right diff --git a/configs/spacial_audio/shader.wgsl b/configs/spacial_audio/shader.wgsl new file mode 100644 index 0000000..214a1b4 --- /dev/null +++ b/configs/spacial_audio/shader.wgsl @@ -0,0 +1,9 @@ +// Damping +*mul = 1 - 0.001; + +// Emitter +let emitter = vec2( + f32(ctx.width) / 2.0 + 100.0 * cos(0.00000314159265358979323846 * f32(ctx.tick)), + f32(ctx.height) / 2.0 + 100.0 * sin(0.00000314159265358979323846 * f32(ctx.tick)) +); +*distance = distance(vec2(f32(x), f32(y)), emitter); \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index c406ee8..a84fa82 100644 --- a/src/config.rs +++ b/src/config.rs @@ -42,6 +42,7 @@ pub struct Config { pub struct AudioConfig { pub input: PathBuf, pub output: PathBuf, + pub pickup: (u32, u32), } #[derive(Parser)] diff --git a/src/misc/audio.rs b/src/misc/audio.rs index e08f6eb..7e7e2fb 100644 --- a/src/misc/audio.rs +++ b/src/misc/audio.rs @@ -29,7 +29,7 @@ impl Audio { pub fn new(device: &Device, wav_in: impl Read, wav_out: File) -> Result { let mut audio_in_reader = WavReader::new(wav_in)?; let audio_in_spec = audio_in_reader.spec(); - let audio_in = match audio_in_spec.sample_format { + let mut audio_in = match audio_in_spec.sample_format { SampleFormat::Float => audio_in_reader .samples::() .collect::, hound::Error>>(), @@ -42,21 +42,23 @@ impl Audio { } }?; - let params = SincInterpolationParameters { - sinc_len: 256, - f_cutoff: 0.95, - interpolation: SincInterpolationType::Linear, - oversampling_factor: 256, - window: WindowFunction::BlackmanHarris2, - }; - let mut resampler = SincFixedIn::::new( - SAMPLE_RATE as f64 / audio_in_spec.sample_rate as f64, - 2.0, - params, - 1024, - 1, - )?; - let audio_in = &resampler.process(&[&audio_in], None)?[0]; + if audio_in_spec.sample_rate != SAMPLE_RATE { + let params = SincInterpolationParameters { + sinc_len: 256, + f_cutoff: 0.95, + interpolation: SincInterpolationType::Linear, + oversampling_factor: 256, + window: WindowFunction::BlackmanHarris2, + }; + let mut resampler = SincFixedIn::::new( + SAMPLE_RATE as f64 / audio_in_spec.sample_rate as f64, + 2.0, + params, + audio_in.len(), + 1, + )?; + audio_in = resampler.process(&[&audio_in], None)?.remove(0); + } let audio_writer = WavWriter::new( wav_out, @@ -71,7 +73,7 @@ impl Audio { let audio_in_buffer = device.create_buffer_init(&BufferInitDescriptor { label: None, - contents: bytemuck::cast_slice(audio_in), + contents: bytemuck::cast_slice(&audio_in), usage: BufferUsages::STORAGE, }); diff --git a/src/misc/preprocess.rs b/src/misc/preprocess.rs index 5ef6cca..d6a70b3 100644 --- a/src/misc/preprocess.rs +++ b/src/misc/preprocess.rs @@ -1,32 +1,45 @@ -use std::{borrow::Cow, collections::HashSet}; +use std::{borrow::Cow, collections::HashMap, fmt::Write}; pub struct Preprocessor { - defined: HashSet, + defined: HashMap, +} + +#[allow(unused)] +#[derive(Clone, Debug, PartialEq)] +pub enum Data { + Bool(bool), + I32(i32), + U32(u32), + F32(f32), + F16(f32), + Vec { n: usize, data: Vec }, + Null, } impl Preprocessor { pub fn new() -> Self { Self { - defined: HashSet::new(), + defined: HashMap::new(), } } - pub fn define(mut self, name: &str) -> Self { - self.defined.insert(name.to_string()); + pub fn define(mut self, name: &str, data: Data) -> Self { + self.defined.insert(name.to_string(), data); self } - pub fn define_cond(self, name: &str, cond: bool) -> Self { - if cond { - self.define(name) - } else { - self - } - } - - pub fn process<'a>(&self, input: &'a str) -> Cow<'a, str> { + pub fn process(&self, input: &str) -> String { let mut out = String::new(); - let mut dirty = false; + + for (name, value) in self.defined.iter().filter(|x| x.1 != &Data::Null) { + out.write_fmt(format_args!( + "const {name}: {} = {};\n", + value.as_type(), + value.as_value(), + name = name + )) + .unwrap(); + } let lines = input.lines().collect::>(); let mut i = 0; @@ -49,11 +62,9 @@ impl Preprocessor { block.push(line); } - if self.defined.contains(expr) { + if self.defined.contains_key(expr) { out.push_str(&block.join("\n")); out.push('\n'); - } else { - dirty = true; } } else { out.push_str(line); @@ -63,10 +74,74 @@ impl Preprocessor { i += 1; } - if dirty { - Cow::Owned(out) - } else { - Cow::Borrowed(input) + out + } +} + +impl Data { + fn as_type(&self) -> Cow<'static, str> { + match self { + Data::Bool(_) => Cow::Borrowed("bool"), + Data::I32(_) => Cow::Borrowed("i32"), + Data::U32(_) => Cow::Borrowed("u32"), + Data::F32(_) => Cow::Borrowed("f32"), + Data::F16(_) => Cow::Borrowed("f16"), + Data::Null => Cow::Borrowed(""), + Data::Vec { n, data } => Cow::Owned(format!("vec{n}<{}>", data[0].as_type())), + } + } + + fn as_value(&self) -> String { + let mut out = String::new(); + + match self { + Data::Bool(x) => out.push_str(&x.to_string()), + Data::I32(x) => out.push_str(&x.to_string()), + Data::U32(x) => out.push_str(&x.to_string()), + Data::F32(x) => out.push_str(&x.to_string()), + Data::F16(x) => out.push_str(&x.to_string()), + Data::Vec { n, data } => { + let data = data + .iter() + .map(|x| x.as_value()) + .collect::>() + .join(", "); + out.write_fmt(format_args!("vec{n}({data})")).unwrap(); + } + _ => unreachable!(), + } + + out + } + + pub fn vec2(x: impl Into, y: impl Into) -> Data { + Data::Vec { + n: 2, + data: vec![x.into(), y.into()], } } } + +impl From for Data { + fn from(x: bool) -> Self { + Data::Bool(x) + } +} + +impl From for Data { + fn from(x: i32) -> Self { + Data::I32(x) + } +} + +impl From for Data { + fn from(x: u32) -> Self { + Data::U32(x) + } +} + +impl From for Data { + fn from(x: f32) -> Self { + Data::F32(x) + } +} diff --git a/src/shaders/shader.wgsl b/src/shaders/shader.wgsl index 8cd499c..da0d5e3 100644 --- a/src/shaders/shader.wgsl +++ b/src/shaders/shader.wgsl @@ -1,4 +1,4 @@ -fn tick(x: u32, y: u32, wall: ptr, distance: ptr, c: ptr) {} // Populated at runtime +fn tick(x: u32, y: u32, mul: ptr, distance: ptr, c: ptr) {} // Populated at runtime @group(0) @binding(0) var ctx: Context; @group(0) @binding(1) var map: array; @@ -50,10 +50,10 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { let y = global_id.y; let map_value = get_map(x, y); - var wall = map_value.r == 0; + var mul = f32(map_value.r == 0); var distance = f32(map_value.g) / 255.0; var c = pow(ctx.c * (f32(map_value.b) / 255.0 * 2.0), 2.0); - tick(x, y, &wall, &distance, &c); + tick(x, y, &mul, &distance, &c); let next = tick % 3; let current = (tick + 2) % 3; @@ -89,18 +89,19 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { + states[index(x, y - 1, current)] + states[index(x, y + 1, current)] - 4.0 * states[index(x, y, current)] - ) * f32(wall); + ); + states[ni] *= mul; // #if OSCILLATOR states[ni] += ctx.amplitude * exp(-abs(distance)) * cos(f32(ctx.tick) * ctx.oscillation); // #endif // #if AUDIO - if y == 540 && x == 960 { + if y == AUDIO.y && x == AUDIO.x { audio_out[ctx.tick % 512] = states[ni]; } - states[ni] += exp(-abs(distance)) * audio_in[ctx.tick]; + states[ni] += ctx.amplitude * exp(-abs(distance)) * audio_in[ctx.tick]; // #endif let nd = f32(tick) + 1.0; diff --git a/src/simulation.rs b/src/simulation.rs index 06b0b86..09d6622 100644 --- a/src/simulation.rs +++ b/src/simulation.rs @@ -19,11 +19,14 @@ use winit::dpi::PhysicalSize; use crate::{ config::Config, - misc::{audio::Audio, preprocess::Preprocessor}, + misc::{ + audio::Audio, + preprocess::{Data, Preprocessor}, + }, GraphicsContext, }; -const TICK_SIGNATURE: &str = "fn tick(x: u32, y: u32, wall: ptr, distance: ptr, c: ptr)"; +const TICK_SIGNATURE: &str = "fn tick(x: u32, y: u32, mul: ptr, distance: ptr, c: ptr)"; pub struct Simulation { compute_pipeline: ComputePipeline, @@ -109,14 +112,18 @@ impl Simulation { &raw_shader[line_end..] )); } - let raw_shader = Preprocessor::new() - .define_cond("AUDIO", audio.is_some()) - .define_cond("OSCILLATOR", false) - .process(&raw_shader); + let mut preprocessor = Preprocessor::new(); + if let Some(audio) = &args.audio { + preprocessor = preprocessor.define("AUDIO", Data::vec2(audio.pickup.0, audio.pickup.1)); + } else { + preprocessor = preprocessor.define("OSCILLATOR", Data::Null); + } + + let raw_shader = preprocessor.process(&raw_shader); let compute_shader = device.create_shader_module(ShaderModuleDescriptor { label: None, - source: ShaderSource::Wgsl(raw_shader), + source: ShaderSource::Wgsl(raw_shader.into()), }); let map_data = match map {