Skip to content

Commit 63eb45d

Browse files
authored
Merge pull request #595 from orottier/feature/osc-table-size
Reduce oscillator sine table from 8192 to 2048 samples
2 parents 34402c9 + 37937d6 commit 63eb45d

4 files changed

Lines changed: 42 additions & 42 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Decoders are enabled always - the legacy cargo features mp3/ogg/flac/wav/.. are now no-op
66
- Added .aiff decoder support
77
- Fix: mp3 decoding would sometimes insert leading silent frames
8+
- Improved sine oscillator performance
89

910
## Version 1.4.0 (2026-05-18)
1011

src/node/mod.rs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
//! The AudioNode interface and concrete types
22
3-
use std::f32::consts::PI;
4-
use std::sync::OnceLock;
5-
63
use crate::render::{
74
AudioParamValues, AudioProcessor, AudioRenderQuantum, AudioWorkletGlobalScope,
85
};
@@ -58,20 +55,6 @@ pub use stereo_panner::*;
5855
mod waveshaper;
5956
pub use waveshaper::*;
6057

61-
pub(crate) const TABLE_LENGTH_USIZE: usize = 8192;
62-
pub(crate) const TABLE_LENGTH_F32: f32 = TABLE_LENGTH_USIZE as f32;
63-
64-
/// Precomputed sine table
65-
pub(crate) fn precomputed_sine_table() -> &'static [f32] {
66-
static INSTANCE: OnceLock<Vec<f32>> = OnceLock::new();
67-
INSTANCE.get_or_init(|| {
68-
// Compute one period sine wavetable of size TABLE_LENGTH
69-
(0..TABLE_LENGTH_USIZE)
70-
.map(|x| ((x as f32) * 2.0 * PI * (1. / (TABLE_LENGTH_F32))).sin())
71-
.collect()
72-
})
73-
}
74-
7558
// `MediaStreamRenderer` is internally used by `MediaElementAudioSourceNode` and
7659
// `MediaStreamAudioSourceNode`.
7760
struct MediaStreamRenderer<R> {

src/node/oscillator.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::any::Any;
2+
use std::f32::consts::PI;
23
use std::fmt::Debug;
4+
use std::sync::OnceLock;
35

46
use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
57
use crate::param::{AudioParam, AudioParamDescriptor, AutomationRate};
@@ -9,10 +11,21 @@ use crate::render::{
911
use crate::PeriodicWave;
1012
use crate::{assert_valid_time_value, RENDER_QUANTUM_SIZE};
1113

12-
use super::{
13-
precomputed_sine_table, AudioNode, AudioNodeOptions, AudioScheduledSourceNode, ChannelConfig,
14-
TABLE_LENGTH_USIZE,
15-
};
14+
use super::{AudioNode, AudioNodeOptions, AudioScheduledSourceNode, ChannelConfig};
15+
16+
const SINE_TABLE_LENGTH_USIZE: usize = 2048;
17+
const SINE_TABLE_LENGTH_F32: f32 = SINE_TABLE_LENGTH_USIZE as f32;
18+
19+
/// Precomputed sine table
20+
fn precomputed_sine_table() -> &'static [f32] {
21+
static INSTANCE: OnceLock<Vec<f32>> = OnceLock::new();
22+
INSTANCE.get_or_init(|| {
23+
// Compute one period sine wavetable of size SINE_TABLE_LENGTH.
24+
(0..SINE_TABLE_LENGTH_USIZE)
25+
.map(|x| ((x as f32) * 2.0 * PI * (1. / (SINE_TABLE_LENGTH_F32))).sin())
26+
.collect()
27+
})
28+
}
1629

1730
fn get_phase_incr(freq: f32, detune: f32, sample_rate: f64) -> f64 {
1831
let computed_freq = freq as f64 * (detune as f64 / 1200.).exp2();
@@ -522,12 +535,12 @@ impl OscillatorRenderer {
522535

523536
#[inline]
524537
fn generate_sine(&mut self) -> f32 {
525-
let position = self.phase * TABLE_LENGTH_USIZE as f64;
538+
let position = self.phase * SINE_TABLE_LENGTH_USIZE as f64;
526539
let floored = position.floor();
527540

528541
let prev_index = floored as usize;
529542
let mut next_index = prev_index + 1;
530-
if next_index == TABLE_LENGTH_USIZE {
543+
if next_index == SINE_TABLE_LENGTH_USIZE {
531544
next_index = 0;
532545
}
533546

@@ -573,12 +586,13 @@ impl OscillatorRenderer {
573586
#[inline]
574587
fn generate_custom(&mut self) -> f32 {
575588
let periodic_wave = self.periodic_wave.as_ref().unwrap().as_slice();
576-
let position = self.phase * TABLE_LENGTH_USIZE as f64;
589+
let table_length = periodic_wave.len();
590+
let position = self.phase * table_length as f64;
577591
let floored = position.floor();
578592

579593
let prev_index = floored as usize;
580594
let mut next_index = prev_index + 1;
581-
if next_index == TABLE_LENGTH_USIZE {
595+
if next_index == table_length {
582596
next_index = 0;
583597
}
584598

src/periodic_wave.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ use std::sync::Arc;
55

66
use crate::context::BaseAudioContext;
77

8-
use crate::node::TABLE_LENGTH_USIZE;
9-
108
/// Options for constructing a [`PeriodicWave`]
119
#[derive(Debug, Default, Clone)]
1210
pub struct PeriodicWaveOptions {
@@ -75,6 +73,8 @@ pub struct PeriodicWave {
7573
wavetable: Arc<Vec<f32>>,
7674
}
7775

76+
const PERIODIC_WAVE_TABLE_LENGTH: usize = 8192;
77+
7878
impl PeriodicWave {
7979
/// Returns a `PeriodicWave`
8080
///
@@ -148,7 +148,8 @@ impl PeriodicWave {
148148

149149
let normalize = !disable_normalization;
150150
// [spec] A conforming implementation MUST support PeriodicWave up to at least 8192 elements.
151-
let wavetable = Self::generate_wavetable(&real, &imag, normalize, TABLE_LENGTH_USIZE);
151+
let wavetable =
152+
Self::generate_wavetable(&real, &imag, normalize, PERIODIC_WAVE_TABLE_LENGTH);
152153

153154
Self {
154155
wavetable: Arc::new(wavetable),
@@ -213,10 +214,8 @@ mod tests {
213214
use float_eq::assert_float_eq;
214215
use std::f32::consts::PI;
215216

217+
use super::{PeriodicWave, PeriodicWaveOptions, PERIODIC_WAVE_TABLE_LENGTH};
216218
use crate::context::AudioContext;
217-
use crate::node::{TABLE_LENGTH_F32, TABLE_LENGTH_USIZE};
218-
219-
use super::{PeriodicWave, PeriodicWaveOptions};
220219

221220
#[test]
222221
#[should_panic]
@@ -279,11 +278,12 @@ mod tests {
279278
let reals = [0., 0.];
280279
let imags = [0., 1.];
281280

282-
let result = PeriodicWave::generate_wavetable(&reals, &imags, true, TABLE_LENGTH_USIZE);
281+
let result =
282+
PeriodicWave::generate_wavetable(&reals, &imags, true, PERIODIC_WAVE_TABLE_LENGTH);
283283
let mut expected = Vec::new();
284284

285-
for i in 0..TABLE_LENGTH_USIZE {
286-
let sample = (i as f32 / TABLE_LENGTH_F32 * 2. * PI).sin();
285+
for i in 0..PERIODIC_WAVE_TABLE_LENGTH {
286+
let sample = (i as f32 / PERIODIC_WAVE_TABLE_LENGTH as f32 * 2. * PI).sin();
287287
expected.push(sample);
288288
}
289289

@@ -295,15 +295,16 @@ mod tests {
295295
let reals = [0., 0., 0.];
296296
let imags = [0., 0.5, 0.5];
297297

298-
let result = PeriodicWave::generate_wavetable(&reals, &imags, false, TABLE_LENGTH_USIZE);
298+
let result =
299+
PeriodicWave::generate_wavetable(&reals, &imags, false, PERIODIC_WAVE_TABLE_LENGTH);
299300
let mut expected = Vec::new();
300301

301-
for i in 0..TABLE_LENGTH_USIZE {
302+
for i in 0..PERIODIC_WAVE_TABLE_LENGTH {
302303
let mut sample = 0.;
303304
// fundamental frequency
304-
sample += 0.5 * (1. * i as f32 / TABLE_LENGTH_F32 * 2. * PI).sin();
305+
sample += 0.5 * (1. * i as f32 / PERIODIC_WAVE_TABLE_LENGTH as f32 * 2. * PI).sin();
305306
// 1rst partial
306-
sample += 0.5 * (2. * i as f32 / TABLE_LENGTH_F32 * 2. * PI).sin();
307+
sample += 0.5 * (2. * i as f32 / PERIODIC_WAVE_TABLE_LENGTH as f32 * 2. * PI).sin();
307308

308309
expected.push(sample);
309310
}
@@ -335,15 +336,16 @@ mod tests {
335336
let reals = [0., 0., 0.];
336337
let imags = [0., 0.5, 0.5];
337338

338-
let result = PeriodicWave::generate_wavetable(&reals, &imags, true, TABLE_LENGTH_USIZE);
339+
let result =
340+
PeriodicWave::generate_wavetable(&reals, &imags, true, PERIODIC_WAVE_TABLE_LENGTH);
339341
let mut expected = Vec::new();
340342

341-
for i in 0..TABLE_LENGTH_USIZE {
343+
for i in 0..PERIODIC_WAVE_TABLE_LENGTH {
342344
let mut sample = 0.;
343345
// fundamental frequency
344-
sample += 0.5 * (1. * i as f32 / TABLE_LENGTH_F32 * 2. * PI).sin();
346+
sample += 0.5 * (1. * i as f32 / PERIODIC_WAVE_TABLE_LENGTH as f32 * 2. * PI).sin();
345347
// 1rst partial
346-
sample += 0.5 * (2. * i as f32 / TABLE_LENGTH_F32 * 2. * PI).sin();
348+
sample += 0.5 * (2. * i as f32 / PERIODIC_WAVE_TABLE_LENGTH as f32 * 2. * PI).sin();
347349

348350
expected.push(sample);
349351
}

0 commit comments

Comments
 (0)