hexodsp/dsp/
node_fbwr_fbrd.rs

1// Copyright (c) 2021 Weird Constructor <weirdconstructor@gmail.com>
2// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
3// See README.md and COPYING for details.
4
5use crate::dsp::{
6    DspNode, GraphFun, LedPhaseVals, NodeContext, NodeGlobalRef, NodeId, ProcBuf, SAtom,
7};
8use crate::nodes::{NodeAudioContext, NodeExecContext};
9use crate::{SharedFeedback, SharedFeedbackReader, SharedFeedbackWriter};
10
11/// A simple amplifier
12#[derive(Debug, Clone)]
13pub struct FbWr {
14    fb_wr: Box<SharedFeedbackWriter>,
15}
16
17impl FbWr {
18    pub fn new(nid: &NodeId, node_global: &NodeGlobalRef) -> Self {
19        let fb_wr = if let Ok(mut node_global) = node_global.lock() {
20            node_global.get_feedback_writer(nid.instance() as usize)
21        } else {
22            // If we can't get the lock, other issues are active and I would
23            // rather not crash, so I just make a dummy feedback buffer:
24            let sfb = SharedFeedback::new(44100.0);
25            Box::new(SharedFeedbackWriter::new(&sfb))
26        };
27        Self { fb_wr }
28    }
29    pub const inp: &'static str = "Signal input";
30
31    pub const DESC: &'static str = "Feedback Delay Writer\n\n\
32HexoSynth does not allow direct feedback cycles in it's graph.\n\
33To make feedback possible anyways the `FbWr` and `FbRd` nodes are provided.\n\
34This node allows you to write a signal into the corresponsing signal delay buffer.\n\
35Use `FbRd` for using the signal. The delay is **3.14ms**.";
36    pub const HELP: &'static str = r#"Feedback Delay Writer
37
38HexoSynth does not allow direct feedback cycles in it's graph.
39To make feedback possible anyways the `FbWr` and `FbRd` nodes are provided.
40This node allows you to send a signal into the corresponding `FbWr` signal
41delay.
42
43The instance id of the node defines which `FbWr` and `FbRd` are connected.
44That means `FbRd 0` is connected to the corresponding `FbWr 0`. You can use
45the signal multiple times by connecting the `FbRd 0` ~~sig~~ port to multiple
46inputs.
47
48The delay is always **3.14ms**, regardless of the sampling rate the synthesizer
49is running at.
50"#;
51
52    pub fn graph_fun() -> Option<GraphFun> {
53        None
54    }
55}
56
57impl DspNode for FbWr {
58    fn set_sample_rate(&mut self, _srate: f32) {}
59    fn reset(&mut self) {}
60
61    #[inline]
62    fn process(
63        &mut self,
64        ctx: &mut dyn NodeAudioContext,
65        _ectx: &mut NodeExecContext,
66        _nctx: &NodeContext,
67        _atoms: &[SAtom],
68        inputs: &[ProcBuf],
69        _outputs: &mut [ProcBuf],
70        ctx_vals: LedPhaseVals,
71    ) {
72        use crate::dsp::inp;
73
74        let inp = inp::FbWr::inp(inputs);
75
76        for frame in 0..ctx.nframes() {
77            self.fb_wr.write(inp.read(frame));
78        }
79
80        ctx_vals[0].set(inp.read(ctx.nframes() - 1));
81    }
82}
83
84/// A simple amplifier
85#[derive(Debug, Clone)]
86pub struct FbRd {
87    fb_rd: Box<SharedFeedbackReader>,
88}
89
90impl FbRd {
91    pub fn new(nid: &NodeId, node_global: &NodeGlobalRef) -> Self {
92        let fb_rd = if let Ok(mut node_global) = node_global.lock() {
93            node_global.get_feedback_reader(nid.instance() as usize)
94        } else {
95            // If we can't get the lock, other issues are active and I would
96            // rather not crash, so I just make a dummy feedback buffer:
97            let sfb = SharedFeedback::new(44100.0);
98            Box::new(SharedFeedbackReader::new(&sfb))
99        };
100        Self { fb_rd }
101    }
102    pub const vol: &'static str = "Volume of the input.\n\
103         Use this to adjust the feedback amount.";
104    pub const sig: &'static str = "Feedback signal output.";
105
106    pub const DESC: &'static str = "Feedback Delay Reader\n\n\
107HexoSynth does not allow direct feedback cycles in it's graph.\n\
108To make feedback possible anyways the `FbWr` and `FbRd` nodes are provided.\n\
109This node allows you to tap into the corresponding `FbWr` signal delay \
110for feedback. The delay is **3.14ms**.";
111    pub const HELP: &'static str = r#"Feedback Delay Reader
112
113HexoSynth does not allow direct feedback cycles in it's graph.
114To make feedback possible anyways the `FbWr` and `FbRd` nodes are provided.
115This node allows you to tap into the corresponding `FbWr` signal delay for
116feedback.
117
118The instance id of the node defines which `FbWr` and `FbRd` are connected.
119That means `FbRd 0` is connected to the corresponding `FbWr 0`. You can use
120the signal multiple times by connecting the `FbRd 0` ~~sig~~ port to multiple
121inputs.
122
123The delay is always **3.14ms**, regardless of the sampling rate the synthesizer
124is running at.
125
126The ~~vol~~ parameter is a convenience parameter to allow to control the
127volume of the feedback.
128"#;
129
130    pub fn graph_fun() -> Option<GraphFun> {
131        None
132    }
133}
134
135impl DspNode for FbRd {
136    fn set_sample_rate(&mut self, _srate: f32) {}
137    fn reset(&mut self) {}
138
139    #[inline]
140    fn process(
141        &mut self,
142        ctx: &mut dyn NodeAudioContext,
143        _ectx: &mut NodeExecContext,
144        _nctx: &NodeContext,
145        _atoms: &[SAtom],
146        inputs: &[ProcBuf],
147        outputs: &mut [ProcBuf],
148        ctx_vals: LedPhaseVals,
149    ) {
150        use crate::dsp::{denorm, inp, out};
151
152        let vol = inp::FbRd::vol(inputs);
153        let sig = out::FbRd::sig(outputs);
154
155        let mut last_val = 0.0;
156        for frame in 0..ctx.nframes() {
157            last_val = self.fb_rd.read();
158            last_val *= denorm::FbRd::vol(vol, frame);
159            sig.write(frame, last_val);
160        }
161
162        ctx_vals[0].set(last_val);
163    }
164}