@@ -137,7 +137,10 @@ struct QuadAD : modules::XTModule
137
137
{
138
138
processors[i][p] = std::make_unique<dsp::envelopes::ADAREnvelope>(storage.get ());
139
139
accumulatedOutputs[i][p] = 0 .f ;
140
+ gated[i][p] = false ;
141
+ eocFromAway[i][p] = 0 ;
140
142
}
143
+ pushEOCOnto[i] = -1 ;
141
144
isTriggerLinked[i] = false ;
142
145
isEnvLinked[i] = false ;
143
146
adPoly[i] = 1 ;
@@ -151,10 +154,10 @@ struct QuadAD : modules::XTModule
151
154
configSwitch (A_SHAPE_0 + i, 0 , 2 , 1 , " Attack Curve" , {" A <" , " A -" , " A >" });
152
155
configSwitch (D_SHAPE_0 + i, 0 , 2 , 1 , " Decay Curve" , {" D <" , " D -" , " D >" });
153
156
configSwitch (ADAR_0 + i, 0 , 1 , 0 , " AD vs AR" , {" AD Trig" , " AR Gate" });
154
- configSwitch (LINK_TRIGGER_0 + i, 0 , 1 , 0 ,
157
+ configSwitch (LINK_TRIGGER_0 + i, - 1 , 1 , 0 ,
155
158
" Link " + std::to_string (i + 1 ) + " EOC to " +
156
159
std::to_string ((i + 1 ) % n_ads + 1 ) + " Attack" ,
157
- {" Off" , " On" });
160
+ {" Cycle " , " Off" , " On" });
158
161
configSwitch (LINK_ENV_0 + i, 0 , 1 , 0 ,
159
162
" Sum " + std::to_string (i + 1 ) + " ENV to " +
160
163
std::to_string ((i + 1 ) % n_ads + 1 ) + " Output" ,
@@ -222,12 +225,19 @@ struct QuadAD : modules::XTModule
222
225
223
226
std::atomic<bool > attackFromZero{false };
224
227
int processCount{BLOCK_SIZE};
225
- rack::dsp::SchmittTrigger inputTriggers[n_ads][MAX_POLY];
228
+ rack::dsp::SchmittTrigger inputTriggers[n_ads][MAX_POLY], linkTriggers[n_ads][MAX_POLY];
229
+ bool gated[n_ads][MAX_POLY];
226
230
float accumulatedOutputs[n_ads][MAX_POLY];
227
231
228
- bool isTriggerLinked[n_ads], isEnvLinked[n_ads];
232
+ bool isEnvLinked[n_ads];
229
233
int adPoly[n_ads];
230
234
235
+ bool isTriggerLinked[n_ads]; // this target is linked from away
236
+ int pushEOCOnto[n_ads]; // this target pushes to that source
237
+ bool anyLinked{false };
238
+ float eocFromAway[n_ads][MAX_POLY];
239
+ int lastTriggerLinkParaams[n_ads]{-1 , -1 , -1 , -1 };
240
+
231
241
void process (const typename rack::Module::ProcessArgs &args) override
232
242
{
233
243
if (processCount == BLOCK_SIZE)
@@ -236,36 +246,82 @@ struct QuadAD : modules::XTModule
236
246
237
247
for (int i = 0 ; i < n_ads; ++i)
238
248
{
239
- int linkIdx = (i + n_ads - 1 ) & (n_ads - 1 );
240
- isTriggerLinked[i] = params[LINK_TRIGGER_0 + linkIdx].getValue () > 0.5 ;
241
- isEnvLinked[i] = params[LINK_ENV_0 + linkIdx].getValue () > 0.5 ;
249
+ int envLinkIdx = (i + n_ads - 1 ) & (n_ads - 1 );
250
+ isEnvLinked[i] = params[LINK_ENV_0 + envLinkIdx].getValue () > 0.5 ;
242
251
}
243
252
244
- nChan = 1 ;
253
+ // Only do this if the triggers have changed
254
+ bool recalc{false };
245
255
for (int i = 0 ; i < n_ads; ++i)
246
256
{
247
- if (isTriggerLinked[i])
257
+ auto tn = (int )std::round (params[LINK_TRIGGER_0 + i].getValue ());
258
+ if (tn != lastTriggerLinkParaams[i])
259
+ recalc = true ;
260
+ lastTriggerLinkParaams[i] = tn;
261
+ }
262
+ if (recalc)
263
+ {
264
+ // burn it down and start again
265
+ for (int i = 0 ; i < n_ads; ++i)
266
+ {
267
+ pushEOCOnto[i] = -1 ;
268
+ isTriggerLinked[i] = false ;
269
+ anyLinked = false ;
270
+ }
271
+ for (int i = 0 ; i < n_ads; ++i)
248
272
{
249
- int chl = inputs[TRIGGER_0 + i].getChannels ( );
250
- int j = (i + n_ads - 1 ) & (n_ads - 1 );
251
- while (j != i )
273
+ auto tn = ( int ) std::round (params[LINK_TRIGGER_0 + i].getValue () );
274
+ anyLinked = anyLinked || (tn != 0 );
275
+ if (tn == 1 )
252
276
{
253
- if (!isTriggerLinked[j])
277
+ pushEOCOnto[i] = (i + 1 ) & (n_ads - 1 );
278
+ isTriggerLinked[pushEOCOnto[i]] = true ;
279
+ }
280
+ if (tn == -1 )
281
+ {
282
+ // Single cycle for now
283
+ int q = (i - 1 + n_ads) & (n_ads - 1 );
284
+ int loopFrom{i};
285
+ while (q != i)
254
286
{
255
- chl = std::max (chl, inputs[TRIGGER_0 + j].getChannels ());
256
- break ;
287
+ auto qn = (int )std::round (params[LINK_TRIGGER_0 + q].getValue ());
288
+ if (qn != 1 )
289
+ break ;
290
+ loopFrom = q;
291
+ q = (q - 1 + n_ads) & (n_ads - 1 );
257
292
}
258
- j = (j + n_ads - 1 ) & (n_ads - 1 );
293
+ pushEOCOnto[i] = loopFrom;
294
+ isTriggerLinked[i] = true ;
295
+ isTriggerLinked[loopFrom] = true ;
259
296
}
260
- adPoly[i] = std::max (1 , chl);
261
297
}
262
- else
298
+ }
299
+ // TRIGGER POLY TODO
300
+ nChan = 1 ;
301
+ for (int i = 0 ; i < n_ads; ++i)
302
+ {
303
+ adPoly[i] =
304
+ inputs[TRIGGER_0 + i].isConnected () ? inputs[TRIGGER_0 + i].getChannels () : 1 ;
305
+ nChan = std::max (nChan, adPoly[i]);
306
+ }
307
+ if (anyLinked)
308
+ {
309
+ for (int i = 0 ; i < n_ads; ++i)
310
+ {
311
+ adPoly[i] = nChan;
312
+ }
313
+ }
314
+
315
+ memset (eocFromAway, 0 , n_ads * MAX_POLY * sizeof (float ));
316
+ for (int i = 0 ; i < n_ads; ++i)
317
+ {
318
+ if (pushEOCOnto[i] >= 0 )
263
319
{
264
- adPoly[i] = inputs[TRIGGER_0 + i].isConnected ()
265
- ? inputs[TRIGGER_0 + i].getChannels ()
266
- : 1 ;
320
+ for (int c = 0 ; c < MAX_POLY; ++c)
321
+ {
322
+ eocFromAway[pushEOCOnto[i]][c] += processors[i][c]->eoc_output ;
323
+ }
267
324
}
268
- nChan = std::max (nChan, adPoly[i]);
269
325
}
270
326
271
327
modAssist.setupMatrix (this );
@@ -275,11 +331,11 @@ struct QuadAD : modules::XTModule
275
331
int tnc = 1 ;
276
332
for (int i = 0 ; i < n_ads; ++i)
277
333
{
278
- // who is my trigger neighbor?
279
- int linkIdx = (i + n_ads - 1 ) & (n_ads - 1 );
280
- float linkMul = isTriggerLinked[i] * 10 .f ;
334
+ // who is my env neighbor?
335
+ int envLinkIdx = (i + n_ads - 1 ) & (n_ads - 1 );
281
336
282
- if (inputs[TRIGGER_0 + i].isConnected () || isTriggerLinked[i] || isEnvLinked[i])
337
+ if (inputs[TRIGGER_0 + i].isConnected () || isTriggerLinked[i] || isEnvLinked[i] ||
338
+ pushEOCOnto[i] >= 0 )
283
339
{
284
340
int ch = adPoly[i];
285
341
outputs[OUTPUT_0 + i].setChannels (ch);
@@ -288,21 +344,30 @@ struct QuadAD : modules::XTModule
288
344
for (int c = 0 ; c < ch; ++c)
289
345
{
290
346
auto iv = inputs[TRIGGER_0 + i].getVoltage (c);
291
- auto lv = processors[linkIdx][c]-> eoc_output * linkMul ;
347
+ auto lv = (isTriggerLinked[i] && (eocFromAway[i][c] > 0 )) ? 10 . f : 0 . f ;
292
348
293
- if (inputTriggers[i][c].process (iv + lv))
349
+ auto gl = std::clamp (iv + lv, 0 .f , 10 .f );
350
+
351
+ if (inputTriggers[i][c].process (iv) || linkTriggers[i][c].process (lv))
294
352
{
353
+ gated[i][c] = true ;
295
354
processors[i][c]->attackFrom (attackFromZero ? 0 : processors[i][c]->output ,
296
355
as, params[MODE_0 + i].getValue () < 0.5 ,
297
356
params[ADAR_0 + i].getValue () > 0.5 );
298
357
}
358
+
359
+ if (gated[i][c] && gl < 1 .f ) // that's the default trigger threshold
360
+ {
361
+ gated[i][c] = false ;
362
+ }
363
+
299
364
processors[i][c]->process (modAssist.values [ATTACK_0 + i][c],
300
365
modAssist.values [DECAY_0 + i][c], as, ds,
301
- (lv + iv) * 0.1 );
366
+ gated[i][c] );
302
367
303
368
auto ov = processors[i][c]->output * 10 ;
304
369
if (isEnvLinked[i])
305
- ov += accumulatedOutputs[linkIdx ][c];
370
+ ov += accumulatedOutputs[envLinkIdx ][c];
306
371
307
372
outputs[OUTPUT_0 + i].setVoltage (ov, c);
308
373
accumulatedOutputs[i][c] = ov;
0 commit comments