1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (c) 2022 Dimas Leenman <skythedragon@outlook.com>
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
// See README.md and COPYING for details.

use crate::dsp::{
    DspNode, GraphFun, LedPhaseVals, NodeContext, NodeGlobalRef, NodeId, ProcBuf, SAtom,
};
use crate::nodes::{NodeAudioContext, NodeExecContext};

/// A simple amplifier
#[derive(Debug, Clone)]
pub struct FormFM {
    inv_sample_rate: f32,
    phase: f32,
}

impl FormFM {
    pub fn new(_nid: &NodeId, _node_global: &NodeGlobalRef) -> Self {
        Self { inv_sample_rate: 1.0 / 44100.0, phase: 0.0 }
    }
    pub const freq: &'static str = "Base frequency to oscillate at\n";
    pub const det: &'static str = "Detune the oscillator in semitones and cents.\n";
    pub const form: &'static str =
        "Frequency of the formant\nThis affects how much lower or higher tones the sound has.";
    pub const side: &'static str =
        "Which side the peak of the wave is. Values more towards **0.0** or **1.0** make the base frequency more pronounced";
    pub const peak: &'static str =
        "How high the peak amplitude is. Lower values make the effect more pronounced";
    pub const sig: &'static str = "Generated formant signal";
    pub const DESC: &'static str = r#"Formant oscillator

Simple formant oscillator that generates a formant like sound.
Loosely based on the ModFM synthesis method.
"#;
    pub const HELP: &'static str = r#"Direct formant synthesizer

This is a formant synthesizer that directly generates 
the audio of a single formant.

This can be seen as passing a saw wave with frequency ~~freq~~ 
into a bandpass filter with the cutoff at ~~form~~

~~freq~~ controls the base frequency of the formant.
~~form~~ controls the formant frequency. Lower values give more bass to the sound,
and higher values give the high frequencies more sound.

~~side~~ controls where the peak of the carrier wave is, 
and in turn controls the bandwidth of the effect. The more towards **0.0** or **1.0**,
the more the formant is audible.

~~peak~~ controls how high the peak of the carrier wave is.
This also controls the bandwidth of the effect, where lower means a higher 
bandwidth, and thus more audible formant.
"#;

    pub fn graph_fun() -> Option<GraphFun> {
        None
    }
}

impl DspNode for FormFM {
    fn set_sample_rate(&mut self, srate: f32) {
        self.inv_sample_rate = 1.0 / srate;
    }

    fn reset(&mut self) {
        self.phase = 0.0;
    }

    #[inline]
    fn process(
        &mut self,
        ctx: &mut dyn NodeAudioContext,
        _ectx: &mut NodeExecContext,
        _nctx: &NodeContext,
        _atoms: &[SAtom],
        inputs: &[ProcBuf],
        outputs: &mut [ProcBuf],
        _ctx_vals: LedPhaseVals,
    ) {
        use crate::dsp::{denorm, denorm_offs, inp, out};

        let base_freq = inp::FormFM::freq(inputs);
        let det = inp::FormFM::det(inputs);
        let formant_freq = inp::FormFM::form(inputs);
        let side_val = inp::FormFM::side(inputs);
        let peak_val = inp::FormFM::peak(inputs);
        let out = out::FormFM::sig(outputs);

        for frame in 0..ctx.nframes() {
            // get the inputs
            let base_freq = denorm_offs::FormFM::freq(base_freq, det.read(frame), frame);
            let formant_freq = denorm::FormFM::form(formant_freq, frame);
            let side_val = denorm::FormFM::side(side_val, frame).min(1.0 - 1e-6).max(1e-6);
            let peak_val = denorm::FormFM::peak(peak_val, frame);

            // make a triangle wave, with the peak at carrier center
            let carrier_base = (self.phase / side_val).min((1.0 - self.phase) / (1.0 - side_val));

            // smoothstep
            let carrier = 1.0
                - ((1.0 - peak_val) * (carrier_base * carrier_base * (3.0 - 2.0 * carrier_base)));

            // multiple of the frequency the modulators are at
            let multiple = formant_freq / base_freq.max(1e-6);

            // round them to the closest integer of the formant freq
            let freq_a = multiple.floor();
            let freq_b = freq_a + 1.0;

            // and how much to lerp between them
            let blend = multiple.fract();

            // get the true modulator
            let modulator = (1.0 - blend)
                * if multiple < 1.0 {
                    0.0
                } else {
                    (std::f32::consts::TAU * self.phase * freq_a).cos()
                }
                + blend * (std::f32::consts::TAU * self.phase * freq_b).cos();

            // entire wave
            let wave = carrier * modulator;

            // increment phase (very imporant)
            self.phase += base_freq * self.inv_sample_rate;

            // wrap around
            self.phase = self.phase.fract();

            out.write(frame, wave);
        }
    }
}