1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 7 нояб. 2016 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <core/debug.h>
23 #include <core/colors.h>
24 #include <core/util/Color.h>
25 #include <plugins/gate.h>
26 
27 #define GATE_BUF_SIZE           0x1000
28 #define TRACE_PORT(p)           lsp_trace("  port id=%s", (p)->metadata()->id);
29 
30 namespace lsp
31 {
32     //-------------------------------------------------------------------------
33     // Gate base class
34 
gate_base(const plugin_metadata_t & metadata,bool sc,size_t mode)35     gate_base::gate_base(const plugin_metadata_t &metadata, bool sc, size_t mode): plugin_t(metadata)
36     {
37         nMode           = mode;
38         bSidechain      = sc;
39         vChannels       = NULL;
40         vCurve          = NULL;
41         vTime           = NULL;
42         bPause          = false;
43         bClear          = false;
44         bMSListen       = false;
45         fInGain         = 1.0f;
46         bUISync         = true;
47 
48         pBypass         = NULL;
49         pInGain         = NULL;
50         pOutGain        = NULL;
51         pPause          = NULL;
52         pClear          = NULL;
53         pMSListen       = NULL;
54 
55         pData           = NULL;
56         pIDisplay       = NULL;
57     }
58 
~gate_base()59     gate_base::~gate_base()
60     {
61     }
62 
init(IWrapper * wrapper)63     void gate_base::init(IWrapper *wrapper)
64     {
65         plugin_t::init(wrapper);
66         size_t channels = (nMode == GM_MONO) ? 1 : 2;
67 
68         // Allocate channels
69         vChannels       = new channel_t[channels];
70         if (vChannels == NULL)
71             return;
72 
73         // Allocate temporary buffers
74         size_t buf_size         = GATE_BUF_SIZE * sizeof(float);
75         size_t curve_size       = (gate_base_metadata::CURVE_MESH_SIZE) * sizeof(float);
76         size_t history_size     = (gate_base_metadata::TIME_MESH_SIZE) * sizeof(float);
77         size_t allocate         = buf_size * channels * 5 + curve_size + history_size + DEFAULT_ALIGN;
78         uint8_t *ptr            = new uint8_t[allocate];
79         if (ptr == NULL)
80             return;
81         pData                   = ptr;
82         ptr                     = ALIGN_PTR(ptr, DEFAULT_ALIGN);
83         vCurve                  = reinterpret_cast<float *>(ptr);
84         ptr                    += curve_size;
85         vTime                   = reinterpret_cast<float *>(ptr);
86         ptr                    += history_size;
87 
88         // Initialize channels
89         for (size_t i=0; i<channels; ++i)
90         {
91             channel_t *c = &vChannels[i];
92 
93             if (!c->sSC.init(channels, gate_base_metadata::REACTIVITY_MAX))
94                 return;
95             if (!c->sSCEq.init(2, 12))
96                 return;
97             c->sSCEq.set_mode(EQM_IIR);
98             c->sSC.set_pre_equalizer(&c->sSCEq);
99 
100             c->vIn              = reinterpret_cast<float *>(ptr);
101             ptr                += buf_size;
102             c->vOut             = reinterpret_cast<float *>(ptr);
103             ptr                += buf_size;
104             c->vSc              = reinterpret_cast<float *>(ptr);
105             ptr                += buf_size;
106             c->vEnv             = reinterpret_cast<float *>(ptr);
107             ptr                += buf_size;
108             c->vGain            = reinterpret_cast<float *>(ptr);
109             ptr                += buf_size;
110             c->bScListen        = false;
111             c->nSync            = S_ALL;
112             c->nScType          = SCT_INTERNAL;
113             c->fMakeup          = 1.0f;
114             c->fDryGain         = 1.0f;
115             c->fWetGain         = 0.0f;
116             c->fDotIn           = 0.0f;
117             c->fDotOut          = 0.0f;
118 
119             c->pIn              = NULL;
120             c->pOut             = NULL;
121             c->pSC              = NULL;
122 
123             for (size_t j=0; j<G_TOTAL; ++j)
124                 c->pGraph[j]        = NULL;
125 
126             for (size_t j=0; j<M_TOTAL; ++j)
127                 c->pMeter[j]        = NULL;
128 
129             c->pScType          = NULL;
130             c->pScMode          = NULL;
131             c->pScLookahead     = NULL;
132             c->pScListen        = NULL;
133             c->pScSource        = NULL;
134             c->pScReactivity    = NULL;
135             c->pScPreamp        = NULL;
136             c->pScHpfMode       = NULL;
137             c->pScHpfFreq       = NULL;
138             c->pScLpfMode       = NULL;
139             c->pScLpfFreq       = NULL;
140 
141             c->pHyst            = NULL;
142             c->pThresh[0]       = NULL;
143             c->pThresh[1]       = NULL;
144             c->pZone[0]         = NULL;
145             c->pZone[1]         = NULL;
146             c->pAttack          = NULL;
147             c->pRelease         = NULL;
148             c->pReduction       = NULL;
149             c->pMakeup          = NULL;
150 
151             c->pDryGain         = NULL;
152             c->pWetGain         = NULL;
153             c->pCurve[0]        = NULL;
154             c->pCurve[1]        = NULL;
155             c->pZoneStart[0]    = NULL;
156             c->pZoneStart[1]    = NULL;
157             c->pHystStart       = NULL;
158         }
159 
160         lsp_assert(ptr < &pData[allocate]);
161 
162         // Bind ports
163         size_t port_id              = 0;
164 
165         // Input ports
166         lsp_trace("Binding input ports");
167         for (size_t i=0; i<channels; ++i)
168         {
169             TRACE_PORT(vPorts[port_id]);
170             vChannels[i].pIn        =   vPorts[port_id++];
171         }
172 
173         // Input ports
174         lsp_trace("Binding output ports");
175         for (size_t i=0; i<channels; ++i)
176         {
177             TRACE_PORT(vPorts[port_id]);
178             vChannels[i].pOut       =   vPorts[port_id++];
179         }
180 
181         // Input ports
182         if (bSidechain)
183         {
184             lsp_trace("Binding sidechain ports");
185             for (size_t i=0; i<channels; ++i)
186             {
187                 TRACE_PORT(vPorts[port_id]);
188                 vChannels[i].pSC        =   vPorts[port_id++];
189             }
190         }
191 
192         // Common ports
193         lsp_trace("Binding common ports");
194         TRACE_PORT(vPorts[port_id]);
195         pBypass                 =   vPorts[port_id++];
196         TRACE_PORT(vPorts[port_id]);
197         pInGain                 =   vPorts[port_id++];
198         TRACE_PORT(vPorts[port_id]);
199         pOutGain                =   vPorts[port_id++];
200         TRACE_PORT(vPorts[port_id]);
201         pPause                  =   vPorts[port_id++];
202         TRACE_PORT(vPorts[port_id]);
203         pClear                  =   vPorts[port_id++];
204         if (nMode == GM_MS)
205         {
206             TRACE_PORT(vPorts[port_id]);
207             pMSListen               =   vPorts[port_id++];
208         }
209 
210         // Sidechain ports
211         lsp_trace("Binding sidechain ports");
212         for (size_t i=0; i<channels; ++i)
213         {
214             channel_t *c = &vChannels[i];
215 
216             if ((i > 0) && (nMode == GM_STEREO))
217             {
218                 channel_t *sc       = &vChannels[0];
219                 c->pScType          = sc->pScType;
220                 c->pScSource        = sc->pScSource;
221                 c->pScMode          = sc->pScMode;
222                 c->pScLookahead     = sc->pScLookahead;
223                 c->pScListen        = sc->pScListen;
224                 c->pScReactivity    = sc->pScReactivity;
225                 c->pScPreamp        = sc->pScPreamp;
226                 c->pScHpfMode       = sc->pScHpfMode;
227                 c->pScHpfFreq       = sc->pScHpfFreq;
228                 c->pScLpfMode       = sc->pScLpfMode;
229                 c->pScLpfFreq       = sc->pScLpfFreq;
230             }
231             else
232             {
233                 if (bSidechain)
234                 {
235                     TRACE_PORT(vPorts[port_id]);
236                     c->pScType          =   vPorts[port_id++];
237                 }
238                 TRACE_PORT(vPorts[port_id]);
239                 c->pScMode          =   vPorts[port_id++];
240                 TRACE_PORT(vPorts[port_id]);
241                 c->pScLookahead     =   vPorts[port_id++];
242                 TRACE_PORT(vPorts[port_id]);
243                 c->pScListen        =   vPorts[port_id++];
244                 if (nMode != GM_MONO)
245                 {
246                     TRACE_PORT(vPorts[port_id]);
247                     c->pScSource        =   vPorts[port_id++];
248                 }
249                 TRACE_PORT(vPorts[port_id]);
250                 c->pScReactivity    =   vPorts[port_id++];
251                 TRACE_PORT(vPorts[port_id]);
252                 c->pScPreamp        =   vPorts[port_id++];
253                 TRACE_PORT(vPorts[port_id]);
254                 c->pScHpfMode       =   vPorts[port_id++];
255                 TRACE_PORT(vPorts[port_id]);
256                 c->pScHpfFreq       =   vPorts[port_id++];
257                 TRACE_PORT(vPorts[port_id]);
258                 c->pScLpfMode       =   vPorts[port_id++];
259                 TRACE_PORT(vPorts[port_id]);
260                 c->pScLpfFreq       =   vPorts[port_id++];
261             }
262         }
263 
264         // Gate ports
265         lsp_trace("Binding gate ports");
266         for (size_t i=0; i<channels; ++i)
267         {
268             channel_t *c = &vChannels[i];
269 
270             if ((i > 0) && (nMode == GM_STEREO))
271             {
272                 channel_t *sc       = &vChannels[0];
273 
274                 c->pHyst            = sc->pHyst;
275                 c->pThresh[0]       = sc->pThresh[0];
276                 c->pThresh[1]       = sc->pThresh[1];
277                 c->pZone[0]         = sc->pZone[0];
278                 c->pZone[1]         = sc->pZone[1];
279                 c->pAttack          = sc->pAttack;
280                 c->pRelease         = sc->pRelease;
281                 c->pReduction       = sc->pReduction;
282                 c->pMakeup          = sc->pMakeup;
283 
284                 c->pDryGain         = sc->pDryGain;
285                 c->pWetGain         = sc->pWetGain;
286                 c->pZoneStart[0]    = sc->pZoneStart[0];
287                 c->pZoneStart[1]    = sc->pZoneStart[1];
288                 c->pHystStart       = sc->pHystStart;
289             }
290             else
291             {
292                 TRACE_PORT(vPorts[port_id]);
293                 c->pHyst            =   vPorts[port_id++];
294                 TRACE_PORT(vPorts[port_id]);
295                 c->pThresh[0]       =   vPorts[port_id++];
296                 TRACE_PORT(vPorts[port_id]);
297                 c->pZone[0]        =   vPorts[port_id++];
298                 TRACE_PORT(vPorts[port_id]);
299                 c->pThresh[1]       =   vPorts[port_id++];
300                 TRACE_PORT(vPorts[port_id]);
301                 c->pZone[1]        =   vPorts[port_id++];
302                 TRACE_PORT(vPorts[port_id]);
303                 c->pAttack          =   vPorts[port_id++];
304                 TRACE_PORT(vPorts[port_id]);
305                 c->pRelease         =   vPorts[port_id++];
306                 TRACE_PORT(vPorts[port_id]);
307                 c->pReduction       =   vPorts[port_id++];
308                 TRACE_PORT(vPorts[port_id]);
309                 c->pMakeup          =   vPorts[port_id++];
310 
311                 TRACE_PORT(vPorts[port_id]);
312                 c->pDryGain         =   vPorts[port_id++];
313                 TRACE_PORT(vPorts[port_id]);
314                 c->pWetGain         =   vPorts[port_id++];
315 
316                 // Skip meters visibility controls
317                 TRACE_PORT(vPorts[port_id]);
318                 port_id++;
319                 TRACE_PORT(vPorts[port_id]);
320                 port_id++;
321                 TRACE_PORT(vPorts[port_id]);
322                 port_id++;
323 
324                 TRACE_PORT(vPorts[port_id]);
325                 c->pZoneStart[0]    =   vPorts[port_id++];
326                 TRACE_PORT(vPorts[port_id]);
327                 c->pHystStart       =   vPorts[port_id++];
328                 TRACE_PORT(vPorts[port_id]);
329                 c->pZoneStart[1]    =   vPorts[port_id++];
330 
331                 TRACE_PORT(vPorts[port_id]);
332                 c->pCurve[0]        =   vPorts[port_id++];
333                 TRACE_PORT(vPorts[port_id]);
334                 c->pCurve[1]        =   vPorts[port_id++];
335                 TRACE_PORT(vPorts[port_id]);
336                 c->pGraph[G_SC]     =   vPorts[port_id++];
337                 TRACE_PORT(vPorts[port_id]);
338                 c->pGraph[G_ENV]    =   vPorts[port_id++];
339                 TRACE_PORT(vPorts[port_id]);
340                 c->pGraph[G_GAIN]   =   vPorts[port_id++];
341                 TRACE_PORT(vPorts[port_id]);
342                 c->pMeter[M_SC]     =   vPorts[port_id++];
343                 TRACE_PORT(vPorts[port_id]);
344                 c->pMeter[M_CURVE]  =   vPorts[port_id++];
345                 TRACE_PORT(vPorts[port_id]);
346                 c->pMeter[M_ENV]    =   vPorts[port_id++];
347                 TRACE_PORT(vPorts[port_id]);
348                 c->pMeter[M_GAIN]   =   vPorts[port_id++];
349             }
350         }
351 
352         // Bind history
353         lsp_trace("Binding history ports");
354         for (size_t i=0; i<channels; ++i)
355         {
356             channel_t *c = &vChannels[i];
357 
358             // Skip meters visibility controls
359             TRACE_PORT(vPorts[port_id]);
360             port_id++;
361             TRACE_PORT(vPorts[port_id]);
362             port_id++;
363 
364             // Bind ports
365             TRACE_PORT(vPorts[port_id]);
366             c->pGraph[G_IN]     =   vPorts[port_id++];
367             TRACE_PORT(vPorts[port_id]);
368             c->pGraph[G_OUT]    =   vPorts[port_id++];
369             TRACE_PORT(vPorts[port_id]);
370             c->pMeter[M_IN]     =   vPorts[port_id++];
371             TRACE_PORT(vPorts[port_id]);
372             c->pMeter[M_OUT]    =   vPorts[port_id++];
373         }
374 
375         // Initialize curve (logarithmic) in range of -72 .. +24 db
376         float delta = (gate_base_metadata::CURVE_DB_MAX - gate_base_metadata::CURVE_DB_MIN) / (gate_base_metadata::CURVE_MESH_SIZE-1);
377         for (size_t i=0; i<gate_base_metadata::CURVE_MESH_SIZE; ++i)
378             vCurve[i]   = db_to_gain(gate_base_metadata::CURVE_DB_MIN + delta * i);
379 
380         // Initialize time points
381         delta       = gate_base_metadata::TIME_HISTORY_MAX / (gate_base_metadata::TIME_MESH_SIZE - 1);
382         for (size_t i=0; i<gate_base_metadata::TIME_MESH_SIZE; ++i)
383             vTime[i]    = gate_base_metadata::TIME_HISTORY_MAX - i*delta;
384     }
385 
destroy()386     void gate_base::destroy()
387     {
388         if (vChannels != NULL)
389         {
390             size_t channels = (nMode == GM_MONO) ? 1 : 2;
391             for (size_t i=0; i<channels; ++i)
392             {
393                 vChannels[i].sSC.destroy();
394                 vChannels[i].sSCEq.destroy();
395                 vChannels[i].sDelay.destroy();
396                 vChannels[i].sCompDelay.destroy();
397                 vChannels[i].sDryDelay.destroy();
398             }
399 
400             delete [] vChannels;
401             vChannels = NULL;
402         }
403 
404         if (pData != NULL)
405         {
406             delete [] pData;
407             pData = NULL;
408         }
409 
410         if (pIDisplay != NULL)
411         {
412             pIDisplay->detroy();
413             pIDisplay   = NULL;
414         }
415     }
416 
update_sample_rate(long sr)417     void gate_base::update_sample_rate(long sr)
418     {
419         size_t samples_per_dot  = seconds_to_samples(sr, gate_base_metadata::TIME_HISTORY_MAX / gate_base_metadata::TIME_MESH_SIZE);
420         size_t channels = (nMode == GM_MONO) ? 1 : 2;
421         size_t max_delay    = millis_to_samples(fSampleRate, compressor_base_metadata::LOOKAHEAD_MAX);
422 
423         for (size_t i=0; i<channels; ++i)
424         {
425             channel_t *c = &vChannels[i];
426             c->sBypass.init(sr);
427             c->sGate.set_sample_rate(sr);
428             c->sSC.set_sample_rate(sr);
429             c->sSCEq.set_sample_rate(sr);
430             c->sDelay.init(max_delay);
431             c->sCompDelay.init(max_delay);
432             c->sDryDelay.init(max_delay);
433 
434             for (size_t j=0; j<G_TOTAL; ++j)
435                 c->sGraph[j].init(gate_base_metadata::TIME_MESH_SIZE, samples_per_dot);
436 
437             c->sGraph[G_GAIN].fill(gate_base_metadata::REDUCTION_DFL);
438             c->sGraph[G_GAIN].set_method(MM_MINIMUM);
439         }
440     }
441 
update_settings()442     void gate_base::update_settings()
443     {
444         filter_params_t fp;
445         size_t channels = (nMode == GM_MONO) ? 1 : 2;
446         bool bypass     = pBypass->getValue() >= 0.5f;
447 
448         // Global parameters
449         bPause          = pPause->getValue() >= 0.5f;
450         bClear          = pClear->getValue() >= 0.5f;
451         bMSListen       = (pMSListen != NULL) ? pMSListen->getValue() >= 0.5f : false;
452         fInGain         = pInGain->getValue();
453         float out_gain  = pOutGain->getValue();
454         size_t latency  = 0;
455 
456         for (size_t i=0; i<channels; ++i)
457         {
458             channel_t *c    = &vChannels[i];
459 
460             // Update bypass settings
461             c->sBypass.set_bypass(bypass);
462 
463             // Update sidechain settings
464             c->nScType      = (c->pScType != NULL) ? c->pScType->getValue() : SCT_INTERNAL;
465             c->bScListen    = c->pScListen->getValue() >= 0.5f;
466 
467             c->sSC.set_gain(c->pScPreamp->getValue());
468             c->sSC.set_mode((c->pScMode != NULL) ? c->pScMode->getValue() : SCM_RMS);
469             c->sSC.set_source((c->pScSource != NULL) ? c->pScSource->getValue() : SCS_MIDDLE);
470             c->sSC.set_reactivity(c->pScReactivity->getValue());
471             c->sSC.set_stereo_mode(((nMode == GM_MS) && (c->nScType != SCT_EXTERNAL)) ? SCSM_MIDSIDE : SCSM_STEREO);
472 
473             // Setup hi-pass filter for sidechain
474             size_t hp_slope = c->pScHpfMode->getValue() * 2;
475             fp.nType        = (hp_slope > 0) ? FLT_BT_BWC_HIPASS : FLT_NONE;
476             fp.fFreq        = c->pScHpfFreq->getValue();
477             fp.fFreq2       = fp.fFreq;
478             fp.fGain        = 1.0f;
479             fp.nSlope       = hp_slope;
480             fp.fQuality     = 0.0f;
481             c->sSCEq.set_params(0, &fp);
482 
483             // Setup low-pass filter for sidechain
484             size_t lp_slope = c->pScLpfMode->getValue() * 2;
485             fp.nType        = (lp_slope > 0) ? FLT_BT_BWC_LOPASS : FLT_NONE;
486             fp.fFreq        = c->pScLpfFreq->getValue();
487             fp.fFreq2       = fp.fFreq;
488             fp.fGain        = 1.0f;
489             fp.nSlope       = lp_slope;
490             fp.fQuality     = 0.0f;
491             c->sSCEq.set_params(1, &fp);
492 
493             // Update delay
494             size_t delay    = millis_to_samples(fSampleRate, (c->pScLookahead != NULL) ? c->pScLookahead->getValue() : 0);
495             c->sDelay.set_delay(delay);
496             if (delay > latency)
497                 latency         = delay;
498 
499             // Update Gate settings
500             bool hyst       = (c->pHyst != NULL) ? (c->pHyst->getValue() >= 0.5f) : false;
501             float thresh    = c->pThresh[0]->getValue();
502             float hthresh   = (hyst) ? (thresh * c->pThresh[1]->getValue()) : thresh;
503             float zone      = c->pZone[0]->getValue();
504             float hzone     = (hyst) ? (c->pZone[1]->getValue()) : zone;
505             float makeup    = c->pMakeup->getValue();
506 
507             c->sGate.set_threshold(thresh, hthresh);
508             c->sGate.set_zone(zone, hzone);
509             c->sGate.set_timings(c->pAttack->getValue(), c->pRelease->getValue());
510             c->sGate.set_reduction(c->pReduction->getValue());
511 
512             if (c->pZoneStart[0] != NULL)
513                 c->pZoneStart[0]->setValue(thresh * zone);
514             if (c->pZoneStart[1] != NULL)
515                 c->pZoneStart[1]->setValue(hthresh * hzone);
516             if (c->pHystStart != NULL)
517                 c->pHystStart->setValue(hthresh);
518 
519             // Check modification flag
520             if (c->sGate.modified())
521             {
522                 c->sGate.update_settings();
523                 c->nSync           |= S_ALL;
524             }
525 
526             // Update gains
527             c->fDryGain         = c->pDryGain->getValue() * out_gain;
528             c->fWetGain         = c->pWetGain->getValue() * out_gain;
529             if (c->fMakeup != makeup)
530             {
531                 c->fMakeup          = makeup;
532                 c->nSync           |= S_ALL;
533             }
534         }
535 
536         // Tune compensation delays
537         for (size_t i=0; i<channels; ++i)
538         {
539             channel_t *c    = &vChannels[i];
540             c->sCompDelay.set_delay(latency - c->sDelay.get_delay());
541             c->sDryDelay.set_delay(latency);
542         }
543 
544         // Report latency
545         set_latency(latency);
546     }
547 
ui_activated()548     void gate_base::ui_activated()
549     {
550         size_t channels     = (nMode == GM_MONO) ? 1 : 2;
551         for (size_t i=0; i<channels; ++i)
552             vChannels[i].nSync     = S_ALL;
553         bUISync             = true;
554     }
555 
process(size_t samples)556     void gate_base::process(size_t samples)
557     {
558         size_t channels = (nMode == GM_MONO) ? 1 : 2;
559 
560         float *in_buf[2];   // Input buffer
561         float *out_buf[2];  // Output buffer
562         float *sc_buf[2];   // Sidechain source
563         const float *in[2]; // Buffet to pass to sidechain
564 
565         // Prepare audio channels
566         for (size_t i=0; i<channels; ++i)
567         {
568             channel_t *c        = &vChannels[i];
569 
570             // Initialize pointers
571             in_buf[i]           = c->pIn->getBuffer<float>();
572             out_buf[i]          = c->pOut->getBuffer<float>();
573             sc_buf[i]           = (c->pSC != NULL) ? c->pSC->getBuffer<float>() : in_buf[i];
574             c->fDotIn           = 0.0f;
575             c->fDotOut          = 0.0f;
576         }
577 
578         // Perform gating
579         size_t left = samples;
580         while (left > 0)
581         {
582             // Detemine number of samples to process
583             size_t to_process = (left > GATE_BUF_SIZE) ? GATE_BUF_SIZE : left;
584 
585             // Prepare audio channels
586             if (nMode == GM_MONO)
587                 dsp::mul_k3(vChannels[0].vIn, in_buf[0], fInGain, to_process);
588             else if (nMode == GM_MS)
589             {
590                 dsp::lr_to_ms(vChannels[0].vIn, vChannels[1].vIn, in_buf[0], in_buf[1], to_process);
591                 dsp::mul_k2(vChannels[0].vIn, fInGain, to_process);
592                 dsp::mul_k2(vChannels[1].vIn, fInGain, to_process);
593             }
594             else
595             {
596                 dsp::mul_k3(vChannels[0].vIn, in_buf[0], fInGain, to_process);
597                 dsp::mul_k3(vChannels[1].vIn, in_buf[1], fInGain, to_process);
598             }
599 
600             // Perform sidechain processing for each channel
601             for (size_t i=0; i<channels; ++i)
602             {
603                 channel_t *c        = &vChannels[i];
604 
605                 // Update input graph
606                 c->sGraph[G_IN].process(c->vIn, to_process);
607                 c->pMeter[M_IN]->setValue(dsp::abs_max(c->vIn, to_process));
608 
609                 // Do gating
610                 in[0]   = (c->nScType == SCT_EXTERNAL) ? sc_buf[0] : vChannels[0].vIn;
611                 if (channels > 1)
612                     in[1]   = (c->nScType == SCT_EXTERNAL) ? sc_buf[1] : vChannels[1].vIn;
613                 c->sSC.process(c->vSc, in, to_process);
614                 c->sGate.process(c->vGain, c->vEnv, c->vSc, to_process);
615 
616                 // Update gating dot
617                 size_t idx = dsp::max_index(c->vEnv, to_process);
618                 if (c->vEnv[idx] > c->fDotIn)
619                 {
620                     c->fDotIn   = c->vEnv[idx];
621                     c->fDotOut  = c->vGain[idx] * c->fDotIn * c->fMakeup;
622                 }
623             }
624 
625             // Apply gain to each channel
626             for (size_t i=0; i<channels; ++i)
627             {
628                 channel_t *c        = &vChannels[i];
629 
630                 // Add delay to original signal and apply gain
631                 c->sDelay.process(c->vOut, c->vIn, c->vGain, to_process);
632 
633                 // Apply latency compensation delay
634                 c->sCompDelay.process(c->vOut, c->vOut, to_process);
635 
636                 // Process graph outputs
637                 if ((i == 0) || (nMode != GM_STEREO))
638                 {
639                     c->sGraph[G_SC].process(c->vSc, to_process);                        // Sidechain signal
640                     c->pMeter[M_SC]->setValue(dsp::abs_max(c->vSc, to_process));
641 
642                     c->sGraph[G_GAIN].process(c->vGain, to_process);                    // Gain reduction signal
643                     c->pMeter[M_GAIN]->setValue(dsp::abs_max(c->vGain, to_process));
644 
645                     c->sGraph[G_ENV].process(c->vEnv, to_process);                      // Envelope signal
646                     c->pMeter[M_ENV]->setValue(dsp::abs_max(c->vEnv, to_process));
647                 }
648             }
649 
650             // Form output signal
651             if (nMode == GM_MS)
652             {
653                 channel_t *cm       = &vChannels[0];
654                 channel_t *cs       = &vChannels[1];
655 
656                 dsp::mix2(cm->vOut, cm->vIn, cm->fMakeup * cm->fWetGain, cm->fDryGain, to_process);
657                 dsp::mix2(cs->vOut, cs->vIn, cs->fMakeup * cs->fWetGain, cs->fDryGain, to_process);
658 
659                 cm->sGraph[G_OUT].process(cm->vOut, to_process);
660                 cm->pMeter[M_OUT]->setValue(dsp::abs_max(cm->vOut, to_process));
661                 cs->sGraph[G_OUT].process(cs->vOut, to_process);
662                 cs->pMeter[M_OUT]->setValue(dsp::abs_max(cs->vOut, to_process));
663 
664                 if (!bMSListen)
665                     dsp::ms_to_lr(cm->vOut, cs->vOut, cm->vOut, cs->vOut, to_process);
666                 if (cm->bScListen)
667                     dsp::copy(cm->vOut, cm->vSc, to_process);
668                 if (cs->bScListen)
669                     dsp::copy(cs->vOut, cs->vSc, to_process);
670             }
671             else
672             {
673                 for (size_t i=0; i<channels; ++i)
674                 {
675                     // Mix dry/wet signal or copy sidechain signal
676                     channel_t *c        = &vChannels[i];
677                     if (c->bScListen)
678                         dsp::copy(c->vOut, c->vSc, to_process);
679                     else
680                         dsp::mix2(c->vOut, c->vIn, c->fMakeup * c->fWetGain, c->fDryGain, to_process);
681 
682                     c->sGraph[G_OUT].process(c->vOut, to_process);                      // Output signal
683                     c->pMeter[M_OUT]->setValue(dsp::abs_max(c->vOut, to_process));
684                 }
685             }
686 
687             // Final metering
688             for (size_t i=0; i<channels; ++i)
689             {
690                 // Apply bypass
691                 channel_t *c        = &vChannels[i];
692                 c->sDryDelay.process(c->vIn, in_buf[i], to_process);
693                 c->sBypass.process(out_buf[i], c->vIn, c->vOut, to_process);
694 
695                 in_buf[i]          += to_process;
696                 out_buf[i]         += to_process;
697                 sc_buf[i]          += to_process;
698             }
699 
700             left       -= to_process;
701         }
702 
703         if ((!bPause) || (bClear) || (bUISync))
704         {
705             // Process mesh requests
706             for (size_t i=0; i<channels; ++i)
707             {
708                 // Get channel
709                 channel_t *c        = &vChannels[i];
710 
711                 for (size_t j=0; j<G_TOTAL; ++j)
712                 {
713                     // Check that port is bound
714                     if (c->pGraph[j] == NULL)
715                         continue;
716 
717                     // Clear data if requested
718                     if (bClear)
719                         dsp::fill_zero(c->sGraph[j].data(), gate_base_metadata::TIME_MESH_SIZE);
720 
721                     // Get mesh
722                     mesh_t *mesh    = c->pGraph[j]->getBuffer<mesh_t>();
723                     if ((mesh != NULL) && (mesh->isEmpty()))
724                     {
725                         // Fill mesh with new values
726                         dsp::copy(mesh->pvData[0], vTime, gate_base_metadata::TIME_MESH_SIZE);
727                         dsp::copy(mesh->pvData[1], c->sGraph[j].data(), gate_base_metadata::TIME_MESH_SIZE);
728                         mesh->data(2, gate_base_metadata::TIME_MESH_SIZE);
729                     }
730                 } // for j
731             }
732             bUISync = false;
733         }
734 
735         // Output gate curves for each channel
736         for (size_t i=0; i<channels; ++i)
737         {
738             channel_t *c       = &vChannels[i];
739 
740             // Output gate curves
741             for (size_t j=0; j<2; ++j)
742             {
743                 if (c->pCurve[j] == NULL)
744                     continue;
745 
746                 mesh_t *mesh            = c->pCurve[j]->getBuffer<mesh_t>();
747                 if ((c->nSync & (S_CURVE << j)) && (mesh != NULL) && (mesh->isEmpty()))
748                 {
749                     // Copy frequency points
750                     dsp::copy(mesh->pvData[0], vCurve, gate_base_metadata::CURVE_MESH_SIZE);
751                     c->sGate.curve(mesh->pvData[1], vCurve, gate_base_metadata::CURVE_MESH_SIZE, j > 0);
752                     if (c->fMakeup != 1.0f)
753                         dsp::mul_k2(mesh->pvData[1], c->fMakeup, gate_base_metadata::CURVE_MESH_SIZE);
754 
755                     // Mark mesh containing data
756                     mesh->data(2, gate_base_metadata::CURVE_MESH_SIZE);
757                     c->nSync &= ~(S_CURVE << j);
758                 }
759             }
760 
761             // Update meter
762             if ((c->pMeter[M_ENV] != NULL) && (c->pMeter[M_CURVE] != NULL))
763             {
764                 c->pMeter[M_ENV]->setValue(c->fDotIn);
765                 c->pMeter[M_CURVE]->setValue(c->fDotOut);
766             }
767         }
768 
769         // Request for redraw
770         if (pWrapper != NULL)
771             pWrapper->query_display_draw();
772     }
773 
inline_display(ICanvas * cv,size_t width,size_t height)774     bool gate_base::inline_display(ICanvas *cv, size_t width, size_t height)
775     {
776         // Check proportions
777         if (height > width)
778             height  = width;
779 
780         // Init canvas
781         if (!cv->init(width, height))
782             return false;
783         width   = cv->width();
784         height  = cv->height();
785 
786         // Clear background
787         bool bypassing = vChannels[0].sBypass.bypassing();
788         cv->set_color_rgb((bypassing) ? CV_DISABLED : CV_BACKGROUND);
789         cv->paint();
790 
791         float zx    = 1.0f/GAIN_AMP_M_72_DB;
792         float zy    = 1.0f/GAIN_AMP_M_72_DB;
793         float dx    = width/(logf(GAIN_AMP_P_24_DB)-logf(GAIN_AMP_M_72_DB));
794         float dy    = height/(logf(GAIN_AMP_M_72_DB)-logf(GAIN_AMP_P_24_DB));
795 
796         // Draw horizontal and vertical lines
797         cv->set_line_width(1.0);
798         cv->set_color_rgb((bypassing) ? CV_SILVER: CV_YELLOW, 0.5f);
799         for (float i=GAIN_AMP_M_72_DB; i<GAIN_AMP_P_24_DB; i *= GAIN_AMP_P_24_DB)
800         {
801             float ax = dx*(logf(i*zx));
802             float ay = height + dy*(logf(i*zy));
803             cv->line(ax, 0, ax, height);
804             cv->line(0, ay, width, ay);
805         }
806 
807         // Draw 1:1 line
808         cv->set_line_width(2.0);
809         cv->set_color_rgb(CV_GRAY);
810         {
811             float ax1 = dx*(logf(GAIN_AMP_M_72_DB*zx));
812             float ax2 = dx*(logf(GAIN_AMP_P_24_DB*zx));
813             float ay1 = height + dy*(logf(GAIN_AMP_M_72_DB*zy));
814             float ay2 = height + dy*(logf(GAIN_AMP_P_24_DB*zy));
815             cv->line(ax1, ay1, ax2, ay2);
816         }
817 
818         // Draw axis
819         cv->set_color_rgb((bypassing) ? CV_SILVER : CV_WHITE);
820         {
821             float ax = dx*(logf(GAIN_AMP_0_DB*zx));
822             float ay = height + dy*(logf(GAIN_AMP_0_DB*zy));
823             cv->line(ax, 0, ax, height);
824             cv->line(0, ay, width, ay);
825         }
826 
827         // Reuse display
828         pIDisplay           = float_buffer_t::reuse(pIDisplay, 4, width);
829         float_buffer_t *b   = pIDisplay;
830         if (b == NULL)
831             return false;
832 
833         size_t channels = ((nMode == GM_MONO) || (nMode == GM_STEREO)) ? 1 : 2;
834         static uint32_t c_colors[] = {
835                 CV_MIDDLE_CHANNEL, CV_MIDDLE_CHANNEL,
836                 CV_MIDDLE_CHANNEL, CV_MIDDLE_CHANNEL,
837                 CV_LEFT_CHANNEL, CV_RIGHT_CHANNEL,
838                 CV_MIDDLE_CHANNEL, CV_SIDE_CHANNEL
839                };
840 
841         bool aa = cv->set_anti_aliasing(true);
842         cv->set_line_width(2);
843 
844         for (size_t i=0; i<channels; ++i)
845         {
846             channel_t *c    = &vChannels[i];
847 
848             for (size_t j=0; j<2; ++j)
849             {
850                 for (size_t k=0; k<width; ++k)
851                 {
852                     size_t n        = (k*gate_base_metadata::CURVE_MESH_SIZE)/width;
853                     b->v[0][k]      = vCurve[n];
854                 }
855                 c->sGate.curve(b->v[1], b->v[0], width, j > 0);
856                 if (c->fMakeup != 1.0f)
857                     dsp::mul_k2(b->v[1], c->fMakeup, width);
858 
859                 dsp::fill(b->v[2], 0.0f, width);
860                 dsp::fill(b->v[3], height, width);
861                 dsp::axis_apply_log1(b->v[2], b->v[0], zx, dx, width);
862                 dsp::axis_apply_log1(b->v[3], b->v[1], zy, dy, width);
863 
864                 // Draw mesh
865                 uint32_t color = (bypassing || !(active())) ? CV_SILVER : c_colors[nMode*2 + i];
866                 cv->set_color_rgb(color);
867                 cv->draw_lines(b->v[2], b->v[3], width);
868             }
869         }
870 
871         // Draw dot
872         if (active())
873         {
874             for (size_t i=0; i<channels; ++i)
875             {
876                 channel_t *c    = &vChannels[i];
877 
878                 uint32_t color = (bypassing) ? CV_SILVER : c_colors[nMode*2 + i];
879                 Color c1(color), c2(color);
880                 c2.alpha(0.9);
881 
882                 float ax = dx*(logf(c->fDotIn*zx));
883                 float ay = height + dy*(logf(c->fDotOut*zy));
884 
885                 cv->radial_gradient(ax, ay, c1, c2, 12);
886                 cv->set_color_rgb(0);
887                 cv->circle(ax, ay, 4);
888                 cv->set_color_rgb(color);
889                 cv->circle(ax, ay, 3);
890             }
891         }
892 
893         cv->set_anti_aliasing(aa);
894 
895         return true;
896     }
897 
898     //-------------------------------------------------------------------------
899     // Gate derivatives
gate_mono()900     gate_mono::gate_mono() : gate_base(metadata, false, GM_MONO)
901     {
902     }
903 
gate_stereo()904     gate_stereo::gate_stereo() : gate_base(metadata, false, GM_STEREO)
905     {
906     }
907 
gate_lr()908     gate_lr::gate_lr() : gate_base(metadata, false, GM_LR)
909     {
910     }
911 
gate_ms()912     gate_ms::gate_ms() : gate_base(metadata, false, GM_MS)
913     {
914     }
915 
sc_gate_mono()916     sc_gate_mono::sc_gate_mono() : gate_base(metadata, true, GM_MONO)
917     {
918     }
919 
sc_gate_stereo()920     sc_gate_stereo::sc_gate_stereo() : gate_base(metadata, true, GM_STEREO)
921     {
922     }
923 
sc_gate_lr()924     sc_gate_lr::sc_gate_lr() : gate_base(metadata, true, GM_LR)
925     {
926     }
927 
sc_gate_ms()928     sc_gate_ms::sc_gate_ms() : gate_base(metadata, true, GM_MS)
929     {
930     }
931 }
932 
933 
934 
935