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
use crate::dsp::{
denorm, denorm_offs, inp, out, DspNode, GraphFun, LedPhaseVals, NodeContext, NodeGlobalRef,
NodeId, ProcBuf, SAtom,
};
use crate::nodes::{NodeAudioContext, NodeExecContext};
use synfx_dsp::fast_sin;
#[derive(Debug, Clone)]
pub struct Sin {
srate: f32,
phase: f32,
init_phase: f32,
}
const TWOPI: f32 = 2.0 * std::f32::consts::PI;
impl Sin {
pub fn new(nid: &NodeId, _node_global: &NodeGlobalRef) -> Self {
let init_phase = nid.init_phase();
Self { srate: 44100.0, phase: init_phase, init_phase }
}
pub const freq: &'static str = "Frequency of the oscillator.\n";
pub const det: &'static str = "Detune the oscillator in semitones and cents. \
the input of this value is rounded to semitones on coarse input. \
Fine input lets you detune in cents (rounded). \
A signal sent to this port is not rounded.\n\
Note: The signal input allows detune +-10 octaves.\
";
pub const pm: &'static str =
"Phase modulation input or phase offset. Use this for linear FM/PM modulation.\n";
pub const sig: &'static str = "Oscillator signal output.\n";
pub const DESC: &'static str = r#"Sine Oscillator
This is a very simple oscillator that generates a sine wave.
"#;
pub const HELP: &'static str = r#"A Sine Oscillator
This is a very simple oscillator that generates a sine wave.
The ~~freq~~ parameter specifies the frequency, and the ~~det~~ parameter
allows you to detune the oscillator easily.
You can send any signal to these input ports. The ~~det~~ parameter takes
the same signal range as ~~freq~~, which means, that a value of 0.1 detunes
by one octave. And a value 1.0 detunes by 10 octaves. This means that
for ~~det~~ to be usefully modulated you need to attenuate the modulation input.
For linear FM, you can use the ~~pm~~ input. It allows you to modulate the phase
of the oscillator linearly. It does so *through zero* which means that the pitch
should not detune by the amount of modulation in low frequencies.
You can do exponential FM with this node using the ~~det~~ or ~~freq~~ input,
but for easy exponential FM synthesis there might be other nodes available.
"#;
pub fn graph_fun() -> Option<GraphFun> {
None
}
}
impl DspNode for Sin {
fn set_sample_rate(&mut self, srate: f32) {
self.srate = srate;
}
fn reset(&mut self) {
self.phase = self.init_phase;
}
#[inline]
fn process(
&mut self,
ctx: &mut dyn NodeAudioContext,
_ectx: &mut NodeExecContext,
_nctx: &NodeContext,
_atoms: &[SAtom],
inputs: &[ProcBuf],
outputs: &mut [ProcBuf],
ctx_vals: LedPhaseVals,
) {
let o = out::Sin::sig(outputs);
let freq = inp::Sin::freq(inputs);
let det = inp::Sin::det(inputs);
let pm = inp::Sin::pm(inputs);
let isr = 1.0 / self.srate;
let mut last_val = 0.0;
for frame in 0..ctx.nframes() {
let freq = denorm_offs::Sin::freq(freq, det.read(frame), frame);
let mut phase = self.phase + denorm::Sin::pm(pm, frame);
while phase < 0.0 {
phase += 1.0
}
last_val = fast_sin(phase.fract() * TWOPI);
o.write(frame, last_val);
self.phase += freq * isr;
self.phase = self.phase.fract();
}
ctx_vals[0].set(last_val);
}
}