use firewheel_core::{
channel_config::{ChannelConfig, ChannelCount},
diff::{Diff, Patch},
dsp::volume::{Volume, DEFAULT_AMP_EPSILON},
event::NodeEventList,
log::RealtimeLogger,
node::{
AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, ProcBuffers,
ProcInfo, ProcessStatus,
},
param::smoother::{SmoothedParam, SmootherConfig},
SilenceMask,
};
#[derive(Diff, Patch, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct WhiteNoiseGenNode {
pub volume: Volume,
pub enabled: bool,
}
impl Default for WhiteNoiseGenNode {
fn default() -> Self {
Self {
volume: Volume::Linear(0.4),
enabled: true,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct WhiteNoiseGenConfig {
pub seed: i32,
pub smooth_secs: f32,
}
impl Default for WhiteNoiseGenConfig {
fn default() -> Self {
Self {
seed: 17,
smooth_secs: 10.0 / 1_000.0,
}
}
}
impl AudioNode for WhiteNoiseGenNode {
type Configuration = WhiteNoiseGenConfig;
fn info(&self, _config: &Self::Configuration) -> AudioNodeInfo {
AudioNodeInfo::new()
.debug_name("white_noise_gen")
.channel_config(ChannelConfig {
num_inputs: ChannelCount::ZERO,
num_outputs: ChannelCount::MONO,
})
}
fn construct_processor(
&self,
config: &Self::Configuration,
cx: ConstructProcessorContext,
) -> impl AudioNodeProcessor {
let seed = if config.seed == 0 { 17 } else { config.seed };
Processor {
fpd: seed,
gain: SmoothedParam::new(
self.volume.amp_clamped(DEFAULT_AMP_EPSILON),
SmootherConfig {
smooth_secs: config.smooth_secs,
..Default::default()
},
cx.stream_info.sample_rate,
),
params: *self,
}
}
}
struct Processor {
fpd: i32,
params: WhiteNoiseGenNode,
gain: SmoothedParam,
}
impl AudioNodeProcessor for Processor {
fn process(
&mut self,
buffers: ProcBuffers,
_proc_info: &ProcInfo,
events: &mut NodeEventList,
_logger: &mut RealtimeLogger,
) -> ProcessStatus {
for patch in events.drain_patches::<WhiteNoiseGenNode>() {
if let WhiteNoiseGenNodePatch::Volume(vol) = patch {
self.gain.set_value(vol.amp_clamped(DEFAULT_AMP_EPSILON));
}
self.params.apply(patch);
}
if !self.params.enabled || (self.gain.target_value() == 0.0 && !self.gain.is_smoothing()) {
self.gain.reset();
return ProcessStatus::ClearAllOutputs;
}
for s in buffers.outputs[0].iter_mut() {
self.fpd ^= self.fpd << 13;
self.fpd ^= self.fpd >> 17;
self.fpd ^= self.fpd << 5;
let r = self.fpd as f32 * (1.0 / 2_147_483_648.0);
*s = r * self.gain.next_smoothed();
}
ProcessStatus::OutputsModified {
out_silence_mask: SilenceMask::NONE_SILENT,
}
}
}