From edad8660cc3f9b71cdd115070239dd1f8eb01f11 Mon Sep 17 00:00:00 2001 From: Otto Date: Thu, 30 Nov 2023 21:02:15 +0100 Subject: [PATCH] Add bindings for AudioListener --- examples/iir-filter.mjs | 4 + examples/panner.mjs | 24 ++++ generator/templates/audio_context.tmpl.rs | 7 + generator/templates/lib.tmpl.rs | 3 + src/audio_context.rs | 7 + src/audio_listener.rs | 156 ++++++++++++++++++++++ src/lib.rs | 3 + 7 files changed, 204 insertions(+) create mode 100644 examples/panner.mjs create mode 100644 src/audio_listener.rs diff --git a/examples/iir-filter.mjs b/examples/iir-filter.mjs index 2bf9a160..64b95349 100644 --- a/examples/iir-filter.mjs +++ b/examples/iir-filter.mjs @@ -5,6 +5,10 @@ import { AudioContext } from '../index.mjs'; const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive'; const audioContext = new AudioContext({ latencyHint }); +console.log(audioContext.listener.position_x.value); +audioContext.listener.position_x.set_value(1.); +console.log(audioContext.listener.position_x.value); + const pathname = path.join(process.cwd(), 'samples', 'think-stereo-48000.wav'); const arrayBuffer = fs.readFileSync(pathname).buffer; const buffer = await audioContext.decodeAudioData(arrayBuffer); diff --git a/examples/panner.mjs b/examples/panner.mjs new file mode 100644 index 00000000..928e1e1d --- /dev/null +++ b/examples/panner.mjs @@ -0,0 +1,24 @@ +import { AudioContext } from '../index.mjs'; + +const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive'; +const audioContext = new AudioContext({ latencyHint }); + +audioContext.listener.positionZ.value = 1; +audioContext.listener.positionX.value = -10; +audioContext.listener.positionX.linearRampToValueAtTime(10, 4); + +const osc = audioContext.createOscillator(); +const panner = audioContext.createPanner(); +osc.connect(panner); +panner.connect(audioContext.destination); +osc.start(); + +let direction = 1; +setInterval(function loop() { + console.log(audioContext.listener.positionX.value); + if (Math.abs(audioContext.listener.positionX.value) >= 10.) { + direction *= -1; + const now = audioContext.currentTime; + audioContext.listener.positionX.linearRampToValueAtTime(10 * direction, now + 4); + } +}, 500); diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index 0583348d..1599c1fd 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -116,6 +116,7 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); + let native_listener = audio_context.listener(); let napi_audio_context = NapiAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; @@ -134,6 +135,12 @@ fn constructor(ctx: CallContext) -> Result { let js_obj = ctor.new_instance(&[&js_this])?; js_this.set_named_property("destination", &js_obj)?; + // Audio Listener + let napi_listener = NapiAudioListener::new(native_listener); + let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_listener)?; + js_this.set_named_property("listener", &js_obj)?; + ctx.env.get_undefined() } diff --git a/generator/templates/lib.tmpl.rs b/generator/templates/lib.tmpl.rs index 477d9806..8fdce03c 100644 --- a/generator/templates/lib.tmpl.rs +++ b/generator/templates/lib.tmpl.rs @@ -7,6 +7,9 @@ use napi_derive::module_exports; #[macro_use] mod audio_node; +mod audio_listener; +use audio_listener::NapiAudioListener; + mod audio_param; use crate::audio_param::{NapiAudioParam}; // public diff --git a/src/audio_context.rs b/src/audio_context.rs index 314fb581..a010f358 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -138,6 +138,7 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); + let native_listener = audio_context.listener(); let napi_audio_context = NapiAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; @@ -155,6 +156,12 @@ fn constructor(ctx: CallContext) -> Result { let js_obj = ctor.new_instance(&[&js_this])?; js_this.set_named_property("destination", &js_obj)?; + // Audio Listener + let napi_listener = NapiAudioListener::new(native_listener); + let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_listener)?; + js_this.set_named_property("listener", &js_obj)?; + ctx.env.get_undefined() } diff --git a/src/audio_listener.rs b/src/audio_listener.rs new file mode 100644 index 00000000..303e4f38 --- /dev/null +++ b/src/audio_listener.rs @@ -0,0 +1,156 @@ +// @TODO +// This should be generated as any other AudioNode + +use crate::*; +use napi::*; +use napi_derive::js_function; +use web_audio_api::AudioListener; + +pub struct NapiAudioListener(AudioListener); + +impl NapiAudioListener { + pub fn new(audio_listener: AudioListener) -> Self { + Self(audio_listener) + } + + pub fn create_js_object(env: &Env) -> Result { + let mut obj = env.create_object()?; + obj.define_properties(&[ + Property::new("Symbol.toStringTag")? + .with_value(&env.create_string("AudioListener")?) + .with_property_attributes(PropertyAttributes::Static), + Property::new("positionX")?.with_getter(get_position_x), + Property::new("positionY")?.with_getter(get_position_y), + Property::new("positionZ")?.with_getter(get_position_z), + Property::new("forwardX")?.with_getter(get_forward_x), + Property::new("forwardY")?.with_getter(get_forward_y), + Property::new("forwardZ")?.with_getter(get_forward_z), + Property::new("upX")?.with_getter(get_up_x), + Property::new("upY")?.with_getter(get_up_y), + Property::new("upZ")?.with_getter(get_up_z), + ])?; + + Ok(obj) + } + + pub fn unwrap(&self) -> &AudioListener { + &self.0 + } +} + +#[js_function] +fn get_position_x(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.position_x().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_position_y(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.position_y().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_position_z(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.position_z().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_forward_x(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.forward_x().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_forward_y(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.forward_y().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_forward_z(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.forward_z().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_up_x(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.up_x().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_up_y(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.up_y().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_up_z(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.up_z().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} diff --git a/src/lib.rs b/src/lib.rs index c0a7e3ee..ea61b765 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,9 @@ use napi_derive::module_exports; #[macro_use] mod audio_node; +mod audio_listener; +use audio_listener::NapiAudioListener; + mod audio_param; use crate::audio_param::NapiAudioParam; // public