Skip to main content

firewheel_nodes/
beep_test.rs

1#[cfg(not(feature = "std"))]
2use num_traits::Float;
3
4use firewheel_core::node::NodeError;
5use firewheel_core::{
6    channel_config::{ChannelConfig, ChannelCount},
7    diff::{Diff, Patch},
8    dsp::volume::{DEFAULT_MIN_AMP, Volume},
9    event::ProcEvents,
10    node::{
11        AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, EmptyConfig,
12        ProcBuffers, ProcExtra, ProcInfo, ProcessStatus,
13    },
14};
15
16/// A simple node that outputs a sine wave, used for testing purposes.
17///
18/// Note that because this node is for testing purposes, it does not
19/// bother with parameter smoothing.
20#[derive(Diff, Patch, Debug, Clone, Copy, PartialEq)]
21#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
22#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct BeepTestNode {
25    /// The frequency of the sine wave in the range `[20.0, 20_000.0]`. A good
26    /// value for testing is `440` (middle C).
27    pub freq_hz: f32,
28
29    /// The overall volume.
30    ///
31    /// NOTE, a sine wave at `Volume::Linear(1.0) or Volume::Decibels(0.0)` volume
32    /// is *LOUD*, prefer to use a value `Volume::Linear(0.5) or
33    /// Volume::Decibels(-12.0)`.
34    pub volume: Volume,
35}
36
37impl Default for BeepTestNode {
38    fn default() -> Self {
39        Self {
40            freq_hz: 440.0,
41            volume: Volume::Linear(0.5),
42        }
43    }
44}
45
46impl AudioNode for BeepTestNode {
47    type Configuration = EmptyConfig;
48
49    fn info(&self, _config: &Self::Configuration) -> Result<AudioNodeInfo, NodeError> {
50        Ok(AudioNodeInfo::new()
51            .debug_name("beep_test")
52            .channel_config(ChannelConfig {
53                num_inputs: ChannelCount::ZERO,
54                num_outputs: ChannelCount::MONO,
55            }))
56    }
57
58    fn construct_processor(
59        &self,
60        _config: &Self::Configuration,
61        cx: ConstructProcessorContext,
62    ) -> Result<impl AudioNodeProcessor, NodeError> {
63        Ok(Processor {
64            phasor: 0.0,
65            phasor_inc: self.freq_hz.clamp(20.0, 20_000.0)
66                * cx.stream_info.sample_rate_recip as f32,
67            gain: self.volume.amp_clamped(DEFAULT_MIN_AMP),
68        })
69    }
70}
71
72struct Processor {
73    phasor: f32,
74    phasor_inc: f32,
75    gain: f32,
76}
77
78impl AudioNodeProcessor for Processor {
79    fn events(&mut self, info: &ProcInfo, events: &mut ProcEvents, _extra: &mut ProcExtra) {
80        for patch in events.drain_patches::<BeepTestNode>() {
81            match patch {
82                BeepTestNodePatch::FreqHz(f) => {
83                    self.phasor_inc = f.clamp(20.0, 20_000.0) * info.sample_rate_recip as f32;
84                }
85                BeepTestNodePatch::Volume(v) => {
86                    self.gain = v.amp_clamped(DEFAULT_MIN_AMP);
87                }
88            }
89        }
90    }
91
92    fn process(
93        &mut self,
94        _info: &ProcInfo,
95        buffers: ProcBuffers,
96        _extra: &mut ProcExtra,
97    ) -> ProcessStatus {
98        for s in buffers.outputs[0].iter_mut() {
99            *s = (self.phasor * core::f32::consts::TAU).sin() * self.gain;
100            self.phasor = (self.phasor + self.phasor_inc).fract();
101        }
102
103        ProcessStatus::OutputsModified
104    }
105}