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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright (c) 2021 Weird Constructor <weirdconstructor@gmail.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};
use crate::{SharedFeedback, SharedFeedbackReader, SharedFeedbackWriter};

/// A simple amplifier
#[derive(Debug, Clone)]
pub struct FbWr {
    fb_wr: Box<SharedFeedbackWriter>,
}

impl FbWr {
    pub fn new(nid: &NodeId, node_global: &NodeGlobalRef) -> Self {
        let fb_wr = if let Ok(mut node_global) = node_global.lock() {
            node_global.get_feedback_writer(nid.instance() as usize)
        } else {
            // If we can't get the lock, other issues are active and I would
            // rather not crash, so I just make a dummy feedback buffer:
            let sfb = SharedFeedback::new(44100.0);
            Box::new(SharedFeedbackWriter::new(&sfb))
        };
        Self { fb_wr }
    }
    pub const inp: &'static str = "Signal input";

    pub const DESC: &'static str = "Feedback Delay Writer\n\n\
HexoSynth does not allow direct feedback cycles in it's graph.\n\
To make feedback possible anyways the `FbWr` and `FbRd` nodes are provided.\n\
This node allows you to write a signal into the corresponsing signal delay buffer.\n\
Use `FbRd` for using the signal. The delay is **3.14ms**.";
    pub const HELP: &'static str = r#"Feedback Delay Writer

HexoSynth does not allow direct feedback cycles in it's graph.
To make feedback possible anyways the `FbWr` and `FbRd` nodes are provided.
This node allows you to send a signal into the corresponding `FbWr` signal
delay.

The instance id of the node defines which `FbWr` and `FbRd` are connected.
That means `FbRd 0` is connected to the corresponding `FbWr 0`. You can use
the signal multiple times by connecting the `FbRd 0` ~~sig~~ port to multiple
inputs.

The delay is always **3.14ms**, regardless of the sampling rate the synthesizer
is running at.
"#;

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

impl DspNode for FbWr {
    fn set_sample_rate(&mut self, _srate: f32) {}
    fn reset(&mut self) {}

    #[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::inp;

        let inp = inp::FbWr::inp(inputs);

        for frame in 0..ctx.nframes() {
            self.fb_wr.write(inp.read(frame));
        }

        ctx_vals[0].set(inp.read(ctx.nframes() - 1));
    }
}

/// A simple amplifier
#[derive(Debug, Clone)]
pub struct FbRd {
    fb_rd: Box<SharedFeedbackReader>,
}

impl FbRd {
    pub fn new(nid: &NodeId, node_global: &NodeGlobalRef) -> Self {
        let fb_rd = if let Ok(mut node_global) = node_global.lock() {
            node_global.get_feedback_reader(nid.instance() as usize)
        } else {
            // If we can't get the lock, other issues are active and I would
            // rather not crash, so I just make a dummy feedback buffer:
            let sfb = SharedFeedback::new(44100.0);
            Box::new(SharedFeedbackReader::new(&sfb))
        };
        Self { fb_rd }
    }
    pub const vol: &'static str = "Volume of the input.\n\
         Use this to adjust the feedback amount.";
    pub const sig: &'static str = "Feedback signal output.";

    pub const DESC: &'static str = "Feedback Delay Reader\n\n\
HexoSynth does not allow direct feedback cycles in it's graph.\n\
To make feedback possible anyways the `FbWr` and `FbRd` nodes are provided.\n\
This node allows you to tap into the corresponding `FbWr` signal delay \
for feedback. The delay is **3.14ms**.";
    pub const HELP: &'static str = r#"Feedback Delay Reader

HexoSynth does not allow direct feedback cycles in it's graph.
To make feedback possible anyways the `FbWr` and `FbRd` nodes are provided.
This node allows you to tap into the corresponding `FbWr` signal delay for
feedback.

The instance id of the node defines which `FbWr` and `FbRd` are connected.
That means `FbRd 0` is connected to the corresponding `FbWr 0`. You can use
the signal multiple times by connecting the `FbRd 0` ~~sig~~ port to multiple
inputs.

The delay is always **3.14ms**, regardless of the sampling rate the synthesizer
is running at.

The ~~vol~~ parameter is a convenience parameter to allow to control the
volume of the feedback.
"#;

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

impl DspNode for FbRd {
    fn set_sample_rate(&mut self, _srate: f32) {}
    fn reset(&mut self) {}

    #[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, inp, out};

        let vol = inp::FbRd::vol(inputs);
        let sig = out::FbRd::sig(outputs);

        let mut last_val = 0.0;
        for frame in 0..ctx.nframes() {
            last_val = self.fb_rd.read();
            last_val *= denorm::FbRd::vol(vol, frame);
            sig.write(frame, last_val);
        }

        ctx_vals[0].set(last_val);
    }
}