From a1f4198e29c3d0d39a4ecf3e3868e5d695efb67f Mon Sep 17 00:00:00 2001 From: Otto Date: Sun, 26 May 2024 14:59:53 +0200 Subject: [PATCH 1/8] AudioWorkletNode: add processor that yields to V8 once per quantum However - load only goes up - process does not terminate anymore --- examples/audio-worklet.js | 3 +++ src/audio_context.rs | 3 ++- src/audio_worklet_node.rs | 42 ++++++++++++++++++++++++++++++++++-- src/offline_audio_context.rs | 3 ++- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/examples/audio-worklet.js b/examples/audio-worklet.js index aed837b..3b60e07 100644 --- a/examples/audio-worklet.js +++ b/examples/audio-worklet.js @@ -42,13 +42,16 @@ const whiteNoise = new AudioWorkletNode(audioContext, 'white-noise'); whiteNoise.connect(audioContext.destination); if (TEST_ONLINE) { + var maxPeakLoad = 0.; audioContext.renderCapacity.addEventListener('update', e => { const { timestamp, averageLoad, peakLoad, underrunRatio } = e; console.log('AudioRenderCapacityEvent:', { timestamp, averageLoad, peakLoad, underrunRatio }); + maxPeakLoad = Math.max(maxPeakLoad, peakLoad); }); audioContext.renderCapacity.start({ updateInterval: 1. }); await sleep(8); + console.log('maxPeakLoad', maxPeakLoad); await audioContext.close(); } else { const buffer = await audioContext.startRendering(); diff --git a/src/audio_context.rs b/src/audio_context.rs index 5dc8df1..66b572c 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -100,7 +100,8 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); - let worklet_id = crate::audio_worklet_node::allocate_process_call_channel(); + let base = audio_context.base(); + let worklet_id = crate::audio_worklet_node::allocate_process_call_channel(base); // ------------------------------------------------- // Wrap context diff --git a/src/audio_worklet_node.rs b/src/audio_worklet_node.rs index cc98c74..7eba58e 100644 --- a/src/audio_worklet_node.rs +++ b/src/audio_worklet_node.rs @@ -6,6 +6,7 @@ use napi::bindgen_prelude::Array; use napi::*; use napi_derive::js_function; +use web_audio_api::context::ConcreteBaseAudioContext; use web_audio_api::node::{AudioNode, AudioNodeOptions, ChannelCountMode, ChannelInterpretation}; use web_audio_api::worklet::{ AudioParamValues, AudioWorkletGlobalScope, AudioWorkletNode, AudioWorkletNodeOptions, @@ -26,6 +27,7 @@ static INCREMENTING_ID: AtomicU32 = AtomicU32::new(0); enum WorkletCommand { Drop(u32), Process(ProcessorArguments), + Tick, } /// Render thread to Worker processor arguments @@ -62,7 +64,7 @@ struct ProcessCallChannel { static GLOBAL_PROCESS_CALL_CHANNEL_MAP: RwLock> = RwLock::new(vec![]); /// Request a new channel + ID for a newly created (Offline)AudioContext -pub(crate) fn allocate_process_call_channel() -> usize { +pub(crate) fn allocate_process_call_channel(ctx: &ConcreteBaseAudioContext) -> usize { // Only one process message can be sent at same time from a given context, // but Drop messages could be send too, so let's take some room let (send, recv) = crossbeam_channel::bounded(32); @@ -72,6 +74,16 @@ pub(crate) fn allocate_process_call_channel() -> usize { exited: Arc::new(AtomicBool::new(false)), }; + let options = AudioWorkletNodeOptions { + number_of_inputs: 1, + number_of_outputs: 1, // should be zero, but bug in base lib + output_channel_count: Default::default(), + parameter_data: Default::default(), + audio_node_options: AudioNodeOptions::default(), + processor_options: channel.send.clone(), + }; + AudioWorkletNode::new::(ctx, options); + // We need a write-lock to initialize the channel let mut write_lock = GLOBAL_PROCESS_CALL_CHANNEL_MAP.write().unwrap(); let id = write_lock.len(); @@ -449,7 +461,7 @@ pub(crate) fn run_audio_worklet_global_scope(ctx: CallContext) -> Result { let mut processors = ctx.get::(1)?; @@ -462,6 +474,9 @@ pub(crate) fn run_audio_worklet_global_scope(ctx: CallContext) -> Result { process_audio_worklet(ctx.env, &processors, args)?; } + WorkletCommand::Tick => { + break; + } } } @@ -845,3 +860,26 @@ impl Drop for NapiAudioWorkletProcessor { } } } + +struct RenderTickProcessor { + send: Sender, +} + +impl AudioWorkletProcessor for RenderTickProcessor { + type ProcessorOptions = Sender; + + fn constructor(send: Self::ProcessorOptions) -> Self { + Self { send } + } + + fn process<'a, 'b>( + &mut self, + _inputs: &'b [&'a [&'a [f32]]], + _outputs: &'b mut [&'a mut [&'a mut [f32]]], + _params: AudioParamValues<'b>, + _scope: &'b AudioWorkletGlobalScope, + ) -> bool { + self.send.send(WorkletCommand::Tick).unwrap(); + true + } +} diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index c89e02c..b1553fd 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -53,7 +53,8 @@ fn constructor(ctx: CallContext) -> Result { let sample_rate = ctx.get::(2)?.get_double()? as f32; let audio_context = OfflineAudioContext::new(number_of_channels, length, sample_rate); - let worklet_id = crate::audio_worklet_node::allocate_process_call_channel(); + let base = audio_context.base(); + let worklet_id = crate::audio_worklet_node::allocate_process_call_channel(base); // ------------------------------------------------- // Wrap context From 79d8b9fca9c3339776837c7c2da5c151f9eb9ef2 Mon Sep 17 00:00:00 2001 From: Otto Date: Sun, 26 May 2024 15:32:35 +0200 Subject: [PATCH 2/8] AudioWorkletNode: rewrite crossbeam channel to Mutex and Condvar The theory being that two high-prio thread sharing the Mutex will run delay-free. In practise I get frame drops.. --- src/audio_worklet_node.rs | 116 +++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 51 deletions(-) diff --git a/src/audio_worklet_node.rs b/src/audio_worklet_node.rs index 7eba58e..28155c7 100644 --- a/src/audio_worklet_node.rs +++ b/src/audio_worklet_node.rs @@ -6,7 +6,7 @@ use napi::bindgen_prelude::Array; use napi::*; use napi_derive::js_function; -use web_audio_api::context::ConcreteBaseAudioContext; +use web_audio_api::context::{BaseAudioContext, ConcreteBaseAudioContext}; use web_audio_api::node::{AudioNode, AudioNodeOptions, ChannelCountMode, ChannelInterpretation}; use web_audio_api::worklet::{ AudioParamValues, AudioWorkletGlobalScope, AudioWorkletNode, AudioWorkletNodeOptions, @@ -18,12 +18,13 @@ use std::cell::Cell; use std::collections::HashMap; use std::option::Option; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; -use std::sync::{Arc, Mutex, OnceLock, RwLock}; +use std::sync::{Arc, Condvar, Mutex, OnceLock, RwLock}; /// Unique ID generator for AudioWorkletProcessors static INCREMENTING_ID: AtomicU32 = AtomicU32::new(0); /// Command issued from render thread to the Worker +#[derive(Debug)] enum WorkletCommand { Drop(u32), Process(ProcessorArguments), @@ -31,6 +32,7 @@ enum WorkletCommand { } /// Render thread to Worker processor arguments +#[derive(Debug)] struct ProcessorArguments { // processor unique ID id: u32, @@ -50,10 +52,38 @@ struct ProcessorArguments { /// Message channel from render thread to Worker struct ProcessCallChannel { - send: Sender, - recv: Receiver, + // queue of worklet commands + command_buffer: Mutex>, + // Condition Variable to wait/notify on new worklet commands + cond_var: Condvar, // mark that the worklet has been exited to prevent any further `process` call - exited: Arc, + exited: AtomicBool, +} + +impl ProcessCallChannel { + fn push(&self, command: WorkletCommand) { + let mut buffer = self.command_buffer.lock().unwrap(); + buffer.push(command); + self.cond_var.notify_one(); + } + + fn pop(&self) -> WorkletCommand { + let mut buffer = self.command_buffer.lock().unwrap(); + while buffer.is_empty() { + buffer = self.cond_var.wait(buffer).unwrap(); + } + buffer.remove(0) + } + + fn try_pop(&self) -> Option { + let mut buffer = self.command_buffer.lock().unwrap(); + + if buffer.is_empty() { + return None; + } + + Some(buffer.remove(0)) + } } /// Global map of ID -> ProcessCallChannel @@ -61,18 +91,20 @@ struct ProcessCallChannel { /// Every (Offline)AudioContext is assigned a new channel + ID. The ID is passed to the /// AudioWorklet Worker and to every AudioNode in the context so they can grab the channel and use /// message passing. -static GLOBAL_PROCESS_CALL_CHANNEL_MAP: RwLock> = RwLock::new(vec![]); +static GLOBAL_PROCESS_CALL_CHANNEL_MAP: RwLock>> = RwLock::new(vec![]); /// Request a new channel + ID for a newly created (Offline)AudioContext pub(crate) fn allocate_process_call_channel(ctx: &ConcreteBaseAudioContext) -> usize { // Only one process message can be sent at same time from a given context, // but Drop messages could be send too, so let's take some room - let (send, recv) = crossbeam_channel::bounded(32); + let command_buffer = Mutex::new(Vec::with_capacity(32)); + let channel = ProcessCallChannel { - send, - recv, - exited: Arc::new(AtomicBool::new(false)), + command_buffer, + cond_var: Condvar::new(), + exited: AtomicBool::new(false), }; + let channel = Arc::new(channel); let options = AudioWorkletNodeOptions { number_of_inputs: 1, @@ -80,9 +112,10 @@ pub(crate) fn allocate_process_call_channel(ctx: &ConcreteBaseAudioContext) -> u output_channel_count: Default::default(), parameter_data: Default::default(), audio_node_options: AudioNodeOptions::default(), - processor_options: channel.send.clone(), + processor_options: channel.clone(), }; - AudioWorkletNode::new::(ctx, options); + let node = AudioWorkletNode::new::(ctx, options); + ctx.destination().connect(&node); // We need a write-lock to initialize the channel let mut write_lock = GLOBAL_PROCESS_CALL_CHANNEL_MAP.write().unwrap(); @@ -93,27 +126,9 @@ pub(crate) fn allocate_process_call_channel(ctx: &ConcreteBaseAudioContext) -> u } /// Obtain the WorkletCommand sender for this context ID -fn process_call_sender(id: usize) -> Sender { - // optimistically assume the channel exists and we can use a shared read-lock - GLOBAL_PROCESS_CALL_CHANNEL_MAP.read().unwrap()[id] - .send - .clone() -} - -/// Obtain the WorkletCommand receiver for this context ID -fn process_call_receiver(id: usize) -> Receiver { +fn process_call_channel(id: usize) -> Arc { // optimistically assume the channel exists and we can use a shared read-lock - GLOBAL_PROCESS_CALL_CHANNEL_MAP.read().unwrap()[id] - .recv - .clone() -} - -/// Obtain the WorkletCommand exited flag for this context ID -fn process_call_exited(id: usize) -> Arc { - // optimistically assume the channel exists and we can use a shared read-lock - GLOBAL_PROCESS_CALL_CHANNEL_MAP.read().unwrap()[id] - .exited - .clone() + Arc::clone(&GLOBAL_PROCESS_CALL_CHANNEL_MAP.read().unwrap()[id]) } /// Message channel inside the control thread to pass param descriptors of a given AudioWorkletNode @@ -461,8 +476,8 @@ pub(crate) fn run_audio_worklet_global_scope(ctx: CallContext) -> Result { let mut processors = ctx.get::(1)?; // recycle all processor buffers @@ -488,9 +503,11 @@ pub(crate) fn exit_audio_worklet_global_scope(ctx: CallContext) -> Result(0)?.get_uint32()? as usize; // Flag message channel as exited to prevent any other render call - process_call_exited(worklet_id).store(true, Ordering::SeqCst); + process_call_channel(worklet_id) + .exited + .store(true, Ordering::SeqCst); // Handle any pending message from audio thread - if let Ok(WorkletCommand::Process(args)) = process_call_receiver(worklet_id).try_recv() { + if let Some(WorkletCommand::Process(args)) = process_call_channel(worklet_id).try_pop() { let _ = args.tail_time_sender.send(false); } @@ -688,8 +705,7 @@ fn constructor(ctx: CallContext) -> Result { let id = INCREMENTING_ID.fetch_add(1, Ordering::Relaxed); let processor_options = NapiAudioWorkletProcessor { id, - send: process_call_sender(worklet_id), - exited: process_call_exited(worklet_id), + command_channel: process_call_channel(worklet_id), tail_time_channel: crossbeam_channel::bounded(1), param_values: Vec::with_capacity(32), }; @@ -780,10 +796,8 @@ audio_node_impl!(NapiAudioWorkletNode); struct NapiAudioWorkletProcessor { /// Unique id to pair Napi Worklet and JS processor id: u32, - /// Sender to the JS Worklet - send: Sender, - /// Flag that marks the JS worklet as exited - exited: Arc, + /// Command channel to the JS Worklet + command_channel: Arc, /// tail_time result channel tail_time_channel: (Sender, Receiver), /// Reusable Vec for AudioParam values @@ -813,7 +827,7 @@ impl AudioWorkletProcessor for NapiAudioWorkletProcessor { scope: &'b AudioWorkletGlobalScope, ) -> bool { // Early return if audio thread is still closing while worklet has been exited - if self.exited.load(Ordering::SeqCst) { + if self.command_channel.exited.load(Ordering::SeqCst) { return false; } @@ -847,7 +861,7 @@ impl AudioWorkletProcessor for NapiAudioWorkletProcessor { }; // send command to Worker - self.send.send(WorkletCommand::Process(item)).unwrap(); + self.command_channel.push(WorkletCommand::Process(item)); // await result self.tail_time_channel.1.recv().unwrap() } @@ -855,21 +869,21 @@ impl AudioWorkletProcessor for NapiAudioWorkletProcessor { impl Drop for NapiAudioWorkletProcessor { fn drop(&mut self) { - if !self.exited.load(Ordering::SeqCst) { - self.send.send(WorkletCommand::Drop(self.id)).unwrap(); + if !self.command_channel.exited.load(Ordering::SeqCst) { + self.command_channel.push(WorkletCommand::Drop(self.id)); } } } struct RenderTickProcessor { - send: Sender, + channel: Arc, } impl AudioWorkletProcessor for RenderTickProcessor { - type ProcessorOptions = Sender; + type ProcessorOptions = Arc; - fn constructor(send: Self::ProcessorOptions) -> Self { - Self { send } + fn constructor(channel: Self::ProcessorOptions) -> Self { + Self { channel } } fn process<'a, 'b>( @@ -879,7 +893,7 @@ impl AudioWorkletProcessor for RenderTickProcessor { _params: AudioParamValues<'b>, _scope: &'b AudioWorkletGlobalScope, ) -> bool { - self.send.send(WorkletCommand::Tick).unwrap(); + self.channel.push(WorkletCommand::Tick); true } } From 617341a069c45e524a4b5ae692cfedd4cd6687eb Mon Sep 17 00:00:00 2001 From: Otto Date: Mon, 27 May 2024 16:12:31 +0200 Subject: [PATCH 3/8] audio worklet.rs: add timing logs, change thread prio strategy --- src/audio_worklet_node.rs | 40 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/audio_worklet_node.rs b/src/audio_worklet_node.rs index 28155c7..0d7e449 100644 --- a/src/audio_worklet_node.rs +++ b/src/audio_worklet_node.rs @@ -20,6 +20,8 @@ use std::option::Option; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, Condvar, Mutex, OnceLock, RwLock}; +use std::time::{Duration, Instant}; + /// Unique ID generator for AudioWorkletProcessors static INCREMENTING_ID: AtomicU32 = AtomicU32::new(0); @@ -459,13 +461,30 @@ fn process_audio_worklet(env: &Env, processors: &JsObject, args: ProcessorArgume Ok(()) } +static PREV_START: RwLock> = RwLock::new(None); + /// The entry point into Rust from the Worker #[js_function(2)] pub(crate) fn run_audio_worklet_global_scope(ctx: CallContext) -> Result { + let enter_start = Instant::now(); + let mut lock = PREV_START.write().unwrap(); + if let Some(prev) = *lock { + let micros = enter_start.duration_since(prev).as_micros(); + if micros > 200 { + println!("return to Rust after {} micros", micros); + } + } + // Set thread priority to highest, if not done already if !HAS_THREAD_PRIO.replace(true) { // allowed to fail - let _ = thread_priority::set_current_thread_priority(thread_priority::ThreadPriority::Max); + let prio = thread_priority::ThreadPriority::Deadline { + runtime: Duration::from_millis(2), + deadline: Duration::from_millis(2), + period: Duration::from_millis(2), + flags: Default::default(), + }; + let _ = thread_priority::set_current_thread_priority(prio); } // Obtain the unique worker ID @@ -476,8 +495,16 @@ pub(crate) fn run_audio_worklet_global_scope(ctx: CallContext) -> Result 3000 { + println!("got command after {} micros", micros); + } + + match cmd { WorkletCommand::Drop(id) => { let mut processors = ctx.get::(1)?; // recycle all processor buffers @@ -493,8 +520,17 @@ pub(crate) fn run_audio_worklet_global_scope(ctx: CallContext) -> Result 200 { + println!("handled command after {} micros", micros); + } + + prev = now; } + *lock = Some(Instant::now()); ctx.env.get_undefined() } From a83a5524a09375f79bd135e0ddcd2e84f5be80e3 Mon Sep 17 00:00:00 2001 From: Otto Date: Mon, 27 May 2024 16:20:46 +0200 Subject: [PATCH 4/8] Switch thread-priority crate to audio_thread_priority --- Cargo.toml | 1 + src/audio_worklet_node.rs | 13 +++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1de5315..771f0dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ version = "1.0.7" crate-type = ["cdylib"] [dependencies] +audio_thread_priority = "0.34.0" crossbeam-channel = "0.5" napi = { version="2.16", features=["napi9", "tokio_rt"] } napi-derive = { version="2.16" } diff --git a/src/audio_worklet_node.rs b/src/audio_worklet_node.rs index 0d7e449..feaf6c4 100644 --- a/src/audio_worklet_node.rs +++ b/src/audio_worklet_node.rs @@ -477,14 +477,11 @@ pub(crate) fn run_audio_worklet_global_scope(ctx: CallContext) -> Result Date: Mon, 27 May 2024 16:22:15 +0200 Subject: [PATCH 5/8] Cleanup --- src/audio_worklet_node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio_worklet_node.rs b/src/audio_worklet_node.rs index feaf6c4..576c0aa 100644 --- a/src/audio_worklet_node.rs +++ b/src/audio_worklet_node.rs @@ -20,7 +20,7 @@ use std::option::Option; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, Condvar, Mutex, OnceLock, RwLock}; -use std::time::{Duration, Instant}; +use std::time::Instant; /// Unique ID generator for AudioWorkletProcessors static INCREMENTING_ID: AtomicU32 = AtomicU32::new(0); From 02293f68ece156bfa7e0e8787a06631e86f87351 Mon Sep 17 00:00:00 2001 From: Otto Date: Tue, 4 Jun 2024 09:33:10 +0200 Subject: [PATCH 6/8] Audio Worklet: remove tick processor, yield always to VM --- src/audio_context.rs | 3 +-- src/audio_worklet_node.rs | 52 ++---------------------------------- src/offline_audio_context.rs | 3 +-- 3 files changed, 4 insertions(+), 54 deletions(-) diff --git a/src/audio_context.rs b/src/audio_context.rs index 66b572c..5dc8df1 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -100,8 +100,7 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); - let base = audio_context.base(); - let worklet_id = crate::audio_worklet_node::allocate_process_call_channel(base); + let worklet_id = crate::audio_worklet_node::allocate_process_call_channel(); // ------------------------------------------------- // Wrap context diff --git a/src/audio_worklet_node.rs b/src/audio_worklet_node.rs index 576c0aa..9c9556c 100644 --- a/src/audio_worklet_node.rs +++ b/src/audio_worklet_node.rs @@ -6,7 +6,6 @@ use napi::bindgen_prelude::Array; use napi::*; use napi_derive::js_function; -use web_audio_api::context::{BaseAudioContext, ConcreteBaseAudioContext}; use web_audio_api::node::{AudioNode, AudioNodeOptions, ChannelCountMode, ChannelInterpretation}; use web_audio_api::worklet::{ AudioParamValues, AudioWorkletGlobalScope, AudioWorkletNode, AudioWorkletNodeOptions, @@ -30,7 +29,6 @@ static INCREMENTING_ID: AtomicU32 = AtomicU32::new(0); enum WorkletCommand { Drop(u32), Process(ProcessorArguments), - Tick, } /// Render thread to Worker processor arguments @@ -69,14 +67,6 @@ impl ProcessCallChannel { self.cond_var.notify_one(); } - fn pop(&self) -> WorkletCommand { - let mut buffer = self.command_buffer.lock().unwrap(); - while buffer.is_empty() { - buffer = self.cond_var.wait(buffer).unwrap(); - } - buffer.remove(0) - } - fn try_pop(&self) -> Option { let mut buffer = self.command_buffer.lock().unwrap(); @@ -96,7 +86,7 @@ impl ProcessCallChannel { static GLOBAL_PROCESS_CALL_CHANNEL_MAP: RwLock>> = RwLock::new(vec![]); /// Request a new channel + ID for a newly created (Offline)AudioContext -pub(crate) fn allocate_process_call_channel(ctx: &ConcreteBaseAudioContext) -> usize { +pub(crate) fn allocate_process_call_channel() -> usize { // Only one process message can be sent at same time from a given context, // but Drop messages could be send too, so let's take some room let command_buffer = Mutex::new(Vec::with_capacity(32)); @@ -108,17 +98,6 @@ pub(crate) fn allocate_process_call_channel(ctx: &ConcreteBaseAudioContext) -> u }; let channel = Arc::new(channel); - let options = AudioWorkletNodeOptions { - number_of_inputs: 1, - number_of_outputs: 1, // should be zero, but bug in base lib - output_channel_count: Default::default(), - parameter_data: Default::default(), - audio_node_options: AudioNodeOptions::default(), - processor_options: channel.clone(), - }; - let node = AudioWorkletNode::new::(ctx, options); - ctx.destination().connect(&node); - // We need a write-lock to initialize the channel let mut write_lock = GLOBAL_PROCESS_CALL_CHANNEL_MAP.write().unwrap(); let id = write_lock.len(); @@ -493,8 +472,7 @@ pub(crate) fn run_audio_worklet_global_scope(ctx: CallContext) -> Result 3000 { @@ -513,9 +491,6 @@ pub(crate) fn run_audio_worklet_global_scope(ctx: CallContext) -> Result { process_audio_worklet(ctx.env, &processors, args)?; } - WorkletCommand::Tick => { - break; - } } let end = Instant::now(); @@ -907,26 +882,3 @@ impl Drop for NapiAudioWorkletProcessor { } } } - -struct RenderTickProcessor { - channel: Arc, -} - -impl AudioWorkletProcessor for RenderTickProcessor { - type ProcessorOptions = Arc; - - fn constructor(channel: Self::ProcessorOptions) -> Self { - Self { channel } - } - - fn process<'a, 'b>( - &mut self, - _inputs: &'b [&'a [&'a [f32]]], - _outputs: &'b mut [&'a mut [&'a mut [f32]]], - _params: AudioParamValues<'b>, - _scope: &'b AudioWorkletGlobalScope, - ) -> bool { - self.channel.push(WorkletCommand::Tick); - true - } -} diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index b1553fd..c89e02c 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -53,8 +53,7 @@ fn constructor(ctx: CallContext) -> Result { let sample_rate = ctx.get::(2)?.get_double()? as f32; let audio_context = OfflineAudioContext::new(number_of_channels, length, sample_rate); - let base = audio_context.base(); - let worklet_id = crate::audio_worklet_node::allocate_process_call_channel(base); + let worklet_id = crate::audio_worklet_node::allocate_process_call_channel(); // ------------------------------------------------- // Wrap context From 258a5a1d7d1742b5b4a6e913287d6b8083eed6b2 Mon Sep 17 00:00:00 2001 From: Otto Date: Tue, 4 Jun 2024 19:15:27 +0200 Subject: [PATCH 7/8] audio_thread_priority requires libdbus-1-dev installed --- Cross.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cross.toml b/Cross.toml index c5be2fd..f058f1d 100644 --- a/Cross.toml +++ b/Cross.toml @@ -1,17 +1,17 @@ [target.aarch64-unknown-linux-gnu] pre-build = [ "dpkg --add-architecture $CROSS_DEB_ARCH", - "apt-get update && apt-get --assume-yes install libasound2-dev:$CROSS_DEB_ARCH libjack-jackd2-dev:$CROSS_DEB_ARCH" + "apt-get update && apt-get --assume-yes install libasound2-dev:$CROSS_DEB_ARCH libjack-jackd2-dev:$CROSS_DEB_ARCH libdbus-1-dev:$CROSS_DEB_ARCH" ] [target.armv7-unknown-linux-gnueabihf] pre-build = [ "dpkg --add-architecture $CROSS_DEB_ARCH", - "apt-get update && apt-get --assume-yes install libasound2-dev:$CROSS_DEB_ARCH libjack-jackd2-dev:$CROSS_DEB_ARCH" + "apt-get update && apt-get --assume-yes install libasound2-dev:$CROSS_DEB_ARCH libjack-jackd2-dev:$CROSS_DEB_ARCH libdbus-1-dev:$CROSS_DEB_ARCH" ] [target.x86_64-unknown-linux-gnu] pre-build = [ "dpkg --add-architecture $CROSS_DEB_ARCH", - "apt-get update && apt-get --assume-yes install libasound2-dev:$CROSS_DEB_ARCH libjack-jackd2-dev:$CROSS_DEB_ARCH" + "apt-get update && apt-get --assume-yes install libasound2-dev:$CROSS_DEB_ARCH libjack-jackd2-dev:$CROSS_DEB_ARCH libdbus-1-dev:$CROSS_DEB_ARCH" ] From c5ac526628089ae31c6051869e4b54a4d0fbcf56 Mon Sep 17 00:00:00 2001 From: Otto Date: Tue, 25 Nov 2025 08:28:08 +0100 Subject: [PATCH 8/8] Remove now obsolete thread-priority dependency --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 771f0dc..f9412cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ audio_thread_priority = "0.34.0" crossbeam-channel = "0.5" napi = { version="2.16", features=["napi9", "tokio_rt"] } napi-derive = { version="2.16" } -thread-priority = "1.2" web-audio-api = "1.2" # web-audio-api = { path = "../web-audio-api-rs" }