firewheel_nodes/
beep_test.rs1#[cfg(not(feature = "std"))]
2use num_traits::Float;
3
4use firewheel_core::{
5 channel_config::{ChannelConfig, ChannelCount},
6 diff::{Diff, Patch},
7 dsp::volume::{Volume, DEFAULT_AMP_EPSILON},
8 event::ProcEvents,
9 node::{
10 AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, EmptyConfig,
11 ProcBuffers, ProcExtra, ProcInfo, ProcessStatus,
12 },
13};
14
15#[derive(Diff, Patch, Debug, Clone, Copy, PartialEq)]
20#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
21#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct BeepTestNode {
24 pub freq_hz: f32,
27
28 pub volume: Volume,
34
35 pub enabled: bool,
37}
38
39impl Default for BeepTestNode {
40 fn default() -> Self {
41 Self {
42 freq_hz: 440.0,
43 volume: Volume::Linear(0.5),
44 enabled: true,
45 }
46 }
47}
48
49impl AudioNode for BeepTestNode {
50 type Configuration = EmptyConfig;
51
52 fn info(&self, _config: &Self::Configuration) -> AudioNodeInfo {
53 AudioNodeInfo::new()
54 .debug_name("beep_test")
55 .channel_config(ChannelConfig {
56 num_inputs: ChannelCount::ZERO,
57 num_outputs: ChannelCount::MONO,
58 })
59 }
60
61 fn construct_processor(
62 &self,
63 _config: &Self::Configuration,
64 cx: ConstructProcessorContext,
65 ) -> impl AudioNodeProcessor {
66 Processor {
67 phasor: 0.0,
68 phasor_inc: self.freq_hz.clamp(20.0, 20_000.0)
69 * cx.stream_info.sample_rate_recip as f32,
70 gain: self.volume.amp_clamped(DEFAULT_AMP_EPSILON),
71 enabled: self.enabled,
72 }
73 }
74}
75
76struct Processor {
77 phasor: f32,
78 phasor_inc: f32,
79 gain: f32,
80 enabled: bool,
81}
82
83impl AudioNodeProcessor for Processor {
84 fn process(
85 &mut self,
86 info: &ProcInfo,
87 buffers: ProcBuffers,
88 events: &mut ProcEvents,
89 _extra: &mut ProcExtra,
90 ) -> ProcessStatus {
91 let Some(out) = buffers.outputs.first_mut() else {
92 return ProcessStatus::ClearAllOutputs;
93 };
94
95 for patch in events.drain_patches::<BeepTestNode>() {
96 match patch {
97 BeepTestNodePatch::FreqHz(f) => {
98 self.phasor_inc = f.clamp(20.0, 20_000.0) * info.sample_rate_recip as f32;
99 }
100 BeepTestNodePatch::Volume(v) => {
101 self.gain = v.amp_clamped(DEFAULT_AMP_EPSILON);
102 }
103 BeepTestNodePatch::Enabled(e) => self.enabled = e,
104 }
105 }
106
107 if !self.enabled {
108 return ProcessStatus::ClearAllOutputs;
109 }
110
111 for s in out.iter_mut() {
112 *s = (self.phasor * core::f32::consts::TAU).sin() * self.gain;
113 self.phasor = (self.phasor + self.phasor_inc).fract();
114 }
115
116 ProcessStatus::OutputsModified
117 }
118}