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
// 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 synfx_dsp::{crossfade, DelayBuffer, TriggerSampleClock};

#[macro_export]
macro_rules! fa_delay_mode {
    ($formatter: expr, $v: expr, $denorm_v: expr) => {{
        let s = match ($v.round() as usize) {
            0 => "Time",
            1 => "Sync",
            _ => "?",
        };
        write!($formatter, "{}", s)
    }};
}

/// A simple amplifier
#[derive(Debug, Clone)]
pub struct Delay {
    buffer: Box<DelayBuffer<f32>>,
    clock: TriggerSampleClock,
}

impl Delay {
    pub fn new(_nid: &NodeId, _node_global: &NodeGlobalRef) -> Self {
        Self { buffer: Box::new(DelayBuffer::new()), clock: TriggerSampleClock::new() }
    }

    pub const inp: &'static str = "The signal input for the delay. You can mix in this \
         input to the output with the ~~mix~~ parameter.";
    pub const trig: &'static str = "If you set ~~mode~~ to **Sync** the delay time will be \
         synchronized to the trigger signals received on this input.";
    pub const time: &'static str = "The delay time. It can be freely modulated to your \
         likings.";
    pub const fb: &'static str = "The feedback amount of the delay output to it's input. \
        ";
    pub const mix: &'static str = "The dry/wet mix of the delay.";
    pub const mode: &'static str = "Allows different operating modes of the delay. \
        **Time** is the default, and means that the ~~time~~ input \
        specifies the delay time. **Sync** will synchronize the delay time \
        with the trigger signals on the ~~trig~~ input.";
    pub const sig: &'static str = "The output of the dry/wet mix.";

    pub const DESC: &'static str = r#"Simple Delay Line

This is a very simple single buffer delay node.
It provides an internal feedback and dry/wet mix.
"#;
    pub const HELP: &'static str = r#"A Simple Delay Line

This node provides a very simple delay line with the bare minimum of
parameters. Most importantly a freely modulateable ~~time~~ parameter
and a feedback ~~fb~~ parameter.

Via the ~~mix~~ parameter you can mix in the input signal to the output.

You can use this node to delay any kind of signal, from a simple control
signal to an audio signal.

For other kinds of delay/feedback please see also the `FbWr`/`FbRd` nodes.
"#;

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

impl DspNode for Delay {
    fn set_sample_rate(&mut self, srate: f32) {
        self.buffer.set_sample_rate(srate);
    }

    fn reset(&mut self) {
        self.buffer.reset();
        self.clock.reset();
    }

    #[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::{at, denorm, inp, out};

        let buffer = &mut *self.buffer;

        let mode = at::Delay::mode(atoms);
        let inp = inp::Delay::inp(inputs);
        let trig = inp::Delay::trig(inputs);
        let time = inp::Delay::time(inputs);
        let fb = inp::Delay::fb(inputs);
        let mix = inp::Delay::mix(inputs);
        let out = out::Delay::sig(outputs);

        if mode.i() == 0 {
            for frame in 0..ctx.nframes() {
                let dry = inp.read(frame);

                let out_sample = buffer.cubic_interpolate_at(denorm::Delay::time(time, frame));

                buffer.feed(dry + out_sample * denorm::Delay::fb(fb, frame));

                out.write(
                    frame,
                    crossfade(dry, out_sample, denorm::Delay::mix(mix, frame).clamp(0.0, 1.0)),
                );
            }
        } else {
            for frame in 0..ctx.nframes() {
                let dry = inp.read(frame);

                let clock_samples = self.clock.next(denorm::Delay::trig(trig, frame));
                let out_sample = buffer.at(clock_samples as usize);

                buffer.feed(dry + out_sample * denorm::Delay::fb(fb, frame));

                out.write(
                    frame,
                    crossfade(dry, out_sample, denorm::Delay::mix(mix, frame).clamp(0.0, 1.0)),
                );
            }
        }

        let last_frame = ctx.nframes() - 1;
        ctx_vals[0].set(out.read(last_frame));
    }
}