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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
use crate::dsp::{
DspNode, GraphFun, LedPhaseVals, NodeContext, NodeGlobalRef, NodeId, ProcBuf, SAtom,
};
use crate::nodes::{NodeAudioContext, NodeExecContext};
use synfx_dsp::Trigger;
#[macro_export]
macro_rules! fa_mux9_in_cnt {
($formatter: expr, $v: expr, $denorm_v: expr) => {{
let s = match ($v.round() as usize) {
0 => "1",
1 => "2",
2 => "3",
3 => "4",
4 => "5",
5 => "6",
6 => "7",
7 => "8",
8 => "9",
_ => "?",
};
write!($formatter, "{}", s)
}};
}
#[derive(Debug, Clone)]
pub struct Mux9 {
trig_rst: Trigger,
trig_up: Trigger,
trig_down: Trigger,
idx: u8,
}
impl Mux9 {
pub fn new(_nid: &NodeId, _node_global: &NodeGlobalRef) -> Self {
Self {
trig_rst: Trigger::new(),
trig_up: Trigger::new(),
trig_down: Trigger::new(),
idx: 0,
}
}
pub const slct: &'static str = "Selects the input that is routed to the output ~~sig~~.\
But only if this input is actually connected. If there is no \
connection, the ~~t_rst~~, ~~t_up~~ and ~~t_down~~ trigger inputs are used to \
control the current routing. The maximum routed input is determined \
by the ~~in_cnt~~ setting.";
pub const t_rst: &'static str =
"Trigger resets the internal routing to the first input ~~in_1~~.\
Keep in mind: This input is only used if ~~slct~~ is not connected.\
";
pub const t_up: &'static str = "Trigger increases the internal routing to the next input port.\
If the last input (depending on the ~~in_cnt~~ setting) was selected\
if will wrap around to ~~in_1~~.\
Keep in mind: This input is only used if ~~slct~~ is not connected.\
";
pub const t_down: &'static str =
"Trigger decreases the internal routing to the previous input \
port (eg. ~~in_3~~ => ~~in_2~~). If ~~in_1~~ as selected, then it will \
wrap around to the highest possible input port (depending on the \
~~in_cnt~~ setting).\
Keep in mind: This input is only used if ~~slct~~ is not connected.\
";
pub const in_1: &'static str = "Input port 1.";
pub const in_2: &'static str = "Input port 2.";
pub const in_3: &'static str = "Input port 3.";
pub const in_4: &'static str = "Input port 4.";
pub const in_5: &'static str = "Input port 5.";
pub const in_6: &'static str = "Input port 6.";
pub const in_7: &'static str = "Input port 7.";
pub const in_8: &'static str = "Input port 8.";
pub const in_9: &'static str = "Input port 9.";
pub const in_cnt: &'static str = "The number of inputs that are routed to the output. \
This will limit the number of maximally used inputs.\n";
pub const sig: &'static str = "The currently selected input port will be presented on \
this output port.";
pub const DESC: &'static str = r#"9 Ch. Multiplexer
An up to 9 channel multiplexer aka switch or junction.
You can route one of the 9 (or fewer) inputs to the output.
The opposite of this node is the `Demux9`,
which demultiplexes or routes the one input signal to one of the 9 outputs.
"#;
pub const HELP: &'static str = r#"9 Channel Multiplexer/Switch
This is an up to 9 channel multiplexer, also known as switch or junction.
You can route one of the 9 (or fewer) inputs to the one output.
Selection of the input is done either via a control signal to the
~~slct~~ input (range **0**..**1**) (exclusive) or via the ~~t_rst~~, ~~t_up~~ or
~~t_down~~ triggers.
If the ~~slct~~ input is not connected, the trigger inputs are active.
If you still prefer a knob for manually selecting the input, consider using
some constant signal source like an `Amp` node with an unconnected input.
The ~~in_cnt~~ parameter allows selecting the number of routed input channels.
The opposite of this node is the `Demux9`, which demultiplexes or routes
the one input signal to one of the 9 outputs.
Tip:
An interesting use case for this node is to use it as (up to) 9 step
control signal sequencer. Leave the ~~in_1~~ to ~~in_9~~ ports unconnected
and dial in the desired value via the parameter knobs. This can lead to
interesting results. Even more interesting it can become if you stack
multiple `Demux9` in series and connect just some of the input ports
for slightly changing sequences. Attach a slew limiter node (eg. `LSlew`
or `ESlew`) if less harsh transitions between the input routings is
desired.
"#;
pub fn graph_fun() -> Option<GraphFun> {
None
}
}
impl DspNode for Mux9 {
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::{at, denorm, inp, out};
let in_1 = inp::Mux9::in_1(inputs);
let in_2 = inp::Mux9::in_2(inputs);
let in_3 = inp::Mux9::in_3(inputs);
let in_4 = inp::Mux9::in_4(inputs);
let in_5 = inp::Mux9::in_5(inputs);
let in_6 = inp::Mux9::in_6(inputs);
let in_7 = inp::Mux9::in_7(inputs);
let in_8 = inp::Mux9::in_8(inputs);
let in_9 = inp::Mux9::in_9(inputs);
let slct = inp::Mux9::slct(inputs);
let t_rst = inp::Mux9::t_rst(inputs);
let t_up = inp::Mux9::t_up(inputs);
let t_down = inp::Mux9::t_down(inputs);
let out = out::Mux9::sig(outputs);
let max: u8 = at::Mux9::in_cnt(atoms).i() as u8 + 1;
self.idx %= max;
if nctx.in_connected & 0x1 == 0x1 {
for frame in 0..ctx.nframes() {
self.idx =
(max as f32 * (denorm::Mux9::slct(slct, frame) - 0.00001)).floor() as u8 % max;
out.write(
frame,
match self.idx {
0 => denorm::Mux9::in_1(in_1, frame),
1 => denorm::Mux9::in_2(in_2, frame),
2 => denorm::Mux9::in_3(in_3, frame),
3 => denorm::Mux9::in_4(in_4, frame),
4 => denorm::Mux9::in_5(in_5, frame),
5 => denorm::Mux9::in_6(in_6, frame),
6 => denorm::Mux9::in_7(in_7, frame),
7 => denorm::Mux9::in_8(in_8, frame),
_ => denorm::Mux9::in_9(in_9, frame),
},
);
}
} else {
for frame in 0..ctx.nframes() {
if self.trig_rst.check_trigger(denorm::Mux9::t_rst(t_rst, frame)) {
self.idx = 0;
}
if self.trig_up.check_trigger(denorm::Mux9::t_up(t_up, frame)) {
self.idx = (self.idx + 1) % max;
}
if self.trig_down.check_trigger(denorm::Mux9::t_down(t_down, frame)) {
self.idx = (self.idx + max - 1) % max;
}
out.write(
frame,
match self.idx {
0 => denorm::Mux9::in_1(in_1, frame),
1 => denorm::Mux9::in_2(in_2, frame),
2 => denorm::Mux9::in_3(in_3, frame),
3 => denorm::Mux9::in_4(in_4, frame),
4 => denorm::Mux9::in_5(in_5, frame),
5 => denorm::Mux9::in_6(in_6, frame),
6 => denorm::Mux9::in_7(in_7, frame),
7 => denorm::Mux9::in_8(in_8, frame),
_ => denorm::Mux9::in_9(in_9, frame),
},
);
}
}
ctx_vals[0].set(out.read(ctx.nframes() - 1));
}
}