Skip to content

Commit 254267d

Browse files
committed
Flush denormal float values in audio graph processing
Fixes #194
1 parent 2796013 commit 254267d

File tree

3 files changed

+47
-1
lines changed

3 files changed

+47
-1
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ hound = "3.5"
2525
hrtf = "0.8.1"
2626
llq = "0.1.1"
2727
log = "0.4"
28+
no_denormals = "0.1.2"
2829
num-complex = "0.4"
2930
realfft = "3.3"
3031
rubato = "0.14"

src/render/graph.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,21 @@ impl Graph {
399399
let params = AudioParamValues::from(&*nodes);
400400
scope.node_id.set(*index);
401401
let (success, tail_time) = {
402+
// for x64 and aarch, process with denormal floats disabled (for performance, #194)
403+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
404+
let process_fn = || no_denormals::no_denormals(|| node.process(params, scope));
405+
#[cfg(not(any(
406+
target_arch = "x86",
407+
target_arch = "x86_64",
408+
target_arch = "aarch64"
409+
)))]
410+
let process_fn = node.process(params, scope);
411+
402412
// We are abusing AssertUnwindSafe here, we cannot guarantee it upholds.
403413
// This may lead to logic bugs later on, but it is the best that we can do.
404414
// The alternative is to crash and reboot the render thread.
405-
let catch_me = AssertUnwindSafe(|| node.process(params, scope));
415+
let catch_me = AssertUnwindSafe(process_fn);
416+
406417
match panic::catch_unwind(catch_me) {
407418
Ok(tail_time) => (true, tail_time),
408419
Err(e) => {

tests/denormals.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use web_audio_api::context::{BaseAudioContext, OfflineAudioContext};
2+
use web_audio_api::node::{AudioNode, AudioScheduledSourceNode};
3+
4+
#[test]
5+
fn test_flush_denormals() {
6+
let context = OfflineAudioContext::new(1, 128, 48000.);
7+
8+
let mut signal = context.create_constant_source();
9+
signal.start();
10+
11+
let gain1 = context.create_gain();
12+
gain1.gain().set_value(0.001);
13+
signal.connect(&gain1);
14+
15+
let gain2 = context.create_gain();
16+
gain2.gain().set_value(f32::MIN_POSITIVE);
17+
gain1.connect(&gain2);
18+
19+
let gain2 = context.create_gain();
20+
gain2.gain().set_value(f32::MIN_POSITIVE);
21+
gain1.connect(&gain2);
22+
23+
let gain3 = context.create_gain();
24+
gain3.gain().set_value(f32::MAX);
25+
gain2.connect(&gain3);
26+
27+
gain3.connect(&context.destination());
28+
29+
let output = context.start_rendering_sync();
30+
31+
// When denormals are flushed, we expect the output to be exactly 0.0
32+
// If not, the output will be ~0.004
33+
assert_eq!(output.get_channel_data(0), &[0.; 128][..]);
34+
}

0 commit comments

Comments
 (0)