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: 21 окт. 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/dyna_processor.h>
26 
27 #define DYNA_PROC_BUF_SIZE           0x1000
28 #define TRACE_PORT(p)               lsp_trace("  port id=%s", (p)->metadata()->id);
29 
30 namespace lsp
31 {
32     //-------------------------------------------------------------------------
33     // Compressor base class
34 
dyna_processor_base(const plugin_metadata_t & metadata,bool sc,size_t mode)35     dyna_processor_base::dyna_processor_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 
~dyna_processor_base()59     dyna_processor_base::~dyna_processor_base()
60     {
61     }
62 
init(IWrapper * wrapper)63     void dyna_processor_base::init(IWrapper *wrapper)
64     {
65         plugin_t::init(wrapper);
66         size_t channels = (nMode == DYNA_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         = DYNA_PROC_BUF_SIZE * sizeof(float);
75         size_t curve_size       = (dyna_processor_base_metadata::CURVE_MESH_SIZE) * sizeof(float);
76         size_t history_size     = (dyna_processor_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, dyna_processor_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 
111             c->bScListen        = false;
112             c->nSync            = S_ALL;
113             c->nScType          = SCT_FEED_FORWARD;
114             c->fMakeup          = 1.0f;
115             c->fFeedback        = 0.0f;
116             c->fDryGain         = 1.0f;
117             c->fWetGain         = 0.0f;
118             c->fDotIn           = 0.0f;
119             c->fDotOut          = 0.0f;
120 
121             c->pIn              = NULL;
122             c->pOut             = NULL;
123             c->pSC              = NULL;
124 
125             for (size_t j=0; j<G_TOTAL; ++j)
126                 c->pGraph[j]        = NULL;
127 
128             for (size_t j=0; j<M_TOTAL; ++j)
129                 c->pMeter[j]        = NULL;
130 
131             c->pScType          = NULL;
132             c->pScMode          = NULL;
133             c->pScLookahead     = NULL;
134             c->pScListen        = NULL;
135             c->pScSource        = NULL;
136             c->pScReactivity    = NULL;
137             c->pScPreamp        = NULL;
138             c->pScHpfMode       = NULL;
139             c->pScHpfFreq       = NULL;
140             c->pScLpfMode       = NULL;
141             c->pScLpfFreq       = NULL;
142 
143             for (size_t j=0; j<dyna_processor_base_metadata::DOTS; ++j)
144             {
145                 c->pDotOn[j]        = NULL;
146                 c->pThreshold[j]    = NULL;
147                 c->pGain[j]         = NULL;
148                 c->pKnee[j]         = NULL;
149                 c->pAttackOn[j]     = NULL;
150                 c->pAttackLvl[j]    = NULL;
151                 c->pReleaseOn[j]    = NULL;
152                 c->pReleaseLvl[j]   = NULL;
153             }
154             for (size_t j=0; j<dyna_processor_base_metadata::RANGES; ++j)
155             {
156                 c->pAttackTime[j]   = NULL;
157                 c->pReleaseTime[j]  = NULL;
158             }
159 
160             c->pLowRatio        = NULL;
161             c->pHighRatio       = NULL;
162             c->pMakeup          = NULL;
163 
164             c->pDryGain         = NULL;
165             c->pWetGain         = NULL;
166             c->pCurve           = NULL;
167             c->pModel           = NULL;
168         }
169 
170         lsp_assert(ptr < &pData[allocate]);
171 
172         // Bind ports
173         size_t port_id              = 0;
174 
175         // Input ports
176         lsp_trace("Binding input ports");
177         for (size_t i=0; i<channels; ++i)
178         {
179             TRACE_PORT(vPorts[port_id]);
180             vChannels[i].pIn        =   vPorts[port_id++];
181         }
182 
183         // Input ports
184         lsp_trace("Binding output ports");
185         for (size_t i=0; i<channels; ++i)
186         {
187             TRACE_PORT(vPorts[port_id]);
188             vChannels[i].pOut       =   vPorts[port_id++];
189         }
190 
191         // Input ports
192         if (bSidechain)
193         {
194             lsp_trace("Binding sidechain ports");
195             for (size_t i=0; i<channels; ++i)
196             {
197                 TRACE_PORT(vPorts[port_id]);
198                 vChannels[i].pSC        =   vPorts[port_id++];
199             }
200         }
201 
202         // Common ports
203         lsp_trace("Binding common ports");
204         TRACE_PORT(vPorts[port_id]);
205         pBypass                 =   vPorts[port_id++];
206         TRACE_PORT(vPorts[port_id]);
207         pInGain                 =   vPorts[port_id++];
208         TRACE_PORT(vPorts[port_id]);
209         pOutGain                =   vPorts[port_id++];
210         TRACE_PORT(vPorts[port_id]);
211         pPause                  =   vPorts[port_id++];
212         TRACE_PORT(vPorts[port_id]);
213         pClear                  =   vPorts[port_id++];
214         if ((nMode == DYNA_LR) || (nMode == DYNA_MS))
215         {
216             // Skip processor selector
217             TRACE_PORT(vPorts[port_id]);
218             port_id++;
219 
220             if (nMode == DYNA_MS)
221             {
222                 TRACE_PORT(vPorts[port_id]);
223                 pMSListen               =   vPorts[port_id++];
224             }
225         }
226 
227         // Sidechain ports
228         lsp_trace("Binding sidechain ports");
229         for (size_t i=0; i<channels; ++i)
230         {
231             channel_t *c = &vChannels[i];
232 
233             if ((i > 0) && (nMode == DYNA_STEREO))
234             {
235                 channel_t *sc       = &vChannels[0];
236                 c->pScType          = sc->pScType;
237                 c->pScSource        = sc->pScSource;
238                 c->pScMode          = sc->pScMode;
239                 c->pScLookahead     = sc->pScLookahead;
240                 c->pScListen        = sc->pScListen;
241                 c->pScReactivity    = sc->pScReactivity;
242                 c->pScPreamp        = sc->pScPreamp;
243                 c->pScHpfMode       = sc->pScHpfMode;
244                 c->pScHpfFreq       = sc->pScHpfFreq;
245                 c->pScLpfMode       = sc->pScLpfMode;
246                 c->pScLpfFreq       = sc->pScLpfFreq;
247             }
248             else
249             {
250                 TRACE_PORT(vPorts[port_id]);
251                 c->pScType          =   vPorts[port_id++];
252                 TRACE_PORT(vPorts[port_id]);
253                 c->pScMode          =   vPorts[port_id++];
254                 TRACE_PORT(vPorts[port_id]);
255                 c->pScLookahead     =   vPorts[port_id++];
256                 TRACE_PORT(vPorts[port_id]);
257                 c->pScListen        =   vPorts[port_id++];
258                 if (nMode != DYNA_MONO)
259                 {
260                     TRACE_PORT(vPorts[port_id]);
261                     c->pScSource        =   vPorts[port_id++];
262                 }
263                 TRACE_PORT(vPorts[port_id]);
264                 c->pScReactivity    =   vPorts[port_id++];
265                 TRACE_PORT(vPorts[port_id]);
266                 c->pScPreamp        =   vPorts[port_id++];
267                 TRACE_PORT(vPorts[port_id]);
268                 c->pScHpfMode       =   vPorts[port_id++];
269                 TRACE_PORT(vPorts[port_id]);
270                 c->pScHpfFreq       =   vPorts[port_id++];
271                 TRACE_PORT(vPorts[port_id]);
272                 c->pScLpfMode       =   vPorts[port_id++];
273                 TRACE_PORT(vPorts[port_id]);
274                 c->pScLpfFreq       =   vPorts[port_id++];
275             }
276         }
277 
278         // Compressor ports
279         lsp_trace("Binding processor ports");
280         for (size_t i=0; i<channels; ++i)
281         {
282             channel_t *c = &vChannels[i];
283 
284             if ((i > 0) && (nMode == DYNA_STEREO))
285             {
286                 channel_t *sc       = &vChannels[0];
287 
288                 for (size_t j=0; j<dyna_processor_base_metadata::DOTS; ++j)
289                 {
290                     c->pDotOn[j]        = sc->pDotOn[j];
291                     c->pThreshold[j]    = sc->pThreshold[j];
292                     c->pGain[j]         = sc->pGain[j];
293                     c->pKnee[j]         = sc->pKnee[j];
294                     c->pAttackOn[j]     = sc->pAttackOn[j];
295                     c->pAttackLvl[j]    = sc->pAttackLvl[j];
296                     c->pReleaseOn[j]    = sc->pReleaseOn[j];
297                     c->pReleaseLvl[j]   = sc->pReleaseLvl[j];
298                 }
299                 for (size_t j=0; j<dyna_processor_base_metadata::RANGES; ++j)
300                 {
301                     c->pAttackTime[j]   = sc->pAttackTime[j];
302                     c->pReleaseTime[j]  = sc->pReleaseTime[j];
303                 }
304 
305                 c->pLowRatio        = sc->pLowRatio;
306                 c->pHighRatio       = sc->pHighRatio;
307                 c->pMakeup          = sc->pMakeup;
308                 c->pDryGain         = sc->pDryGain;
309                 c->pWetGain         = sc->pWetGain;
310             }
311             else
312             {
313                 TRACE_PORT(vPorts[port_id]);
314                 c->pAttackTime[0]   =   vPorts[port_id++];
315                 TRACE_PORT(vPorts[port_id]);
316                 c->pReleaseTime[0]  =   vPorts[port_id++];
317 
318                 for (size_t j=0; j<dyna_processor_base_metadata::DOTS; ++j)
319                 {
320                     TRACE_PORT(vPorts[port_id]);
321                     c->pDotOn[j]        = vPorts[port_id++];
322                     TRACE_PORT(vPorts[port_id]);
323                     c->pThreshold[j]    = vPorts[port_id++];
324                     TRACE_PORT(vPorts[port_id]);
325                     c->pGain[j]         = vPorts[port_id++];
326                     TRACE_PORT(vPorts[port_id]);
327                     c->pKnee[j]         = vPorts[port_id++];
328                     TRACE_PORT(vPorts[port_id]);
329                     c->pAttackOn[j]     = vPorts[port_id++];
330                     TRACE_PORT(vPorts[port_id]);
331                     c->pAttackLvl[j]    = vPorts[port_id++];
332                     TRACE_PORT(vPorts[port_id]);
333                     c->pAttackTime[j+1] = vPorts[port_id++];
334                     TRACE_PORT(vPorts[port_id]);
335                     c->pReleaseOn[j]    = vPorts[port_id++];
336                     TRACE_PORT(vPorts[port_id]);
337                     c->pReleaseLvl[j]   = vPorts[port_id++];
338                     TRACE_PORT(vPorts[port_id]);
339                     c->pReleaseTime[j+1]= vPorts[port_id++];
340                 }
341 
342                 TRACE_PORT(vPorts[port_id]);
343                 c->pLowRatio        =   vPorts[port_id++];
344                 TRACE_PORT(vPorts[port_id]);
345                 c->pHighRatio       =   vPorts[port_id++];
346                 TRACE_PORT(vPorts[port_id]);
347                 c->pMakeup          =   vPorts[port_id++];
348                 TRACE_PORT(vPorts[port_id]);
349                 c->pDryGain         =   vPorts[port_id++];
350                 TRACE_PORT(vPorts[port_id]);
351                 c->pWetGain         =   vPorts[port_id++];
352 
353                 // Skip meters visibility controls
354                 TRACE_PORT(vPorts[port_id]);
355                 port_id++;
356                 TRACE_PORT(vPorts[port_id]);
357                 port_id++;
358                 TRACE_PORT(vPorts[port_id]);
359                 port_id++;
360                 TRACE_PORT(vPorts[port_id]);
361                 port_id++;
362 
363                 TRACE_PORT(vPorts[port_id]);
364                 c->pModel           =   vPorts[port_id++];
365                 TRACE_PORT(vPorts[port_id]);
366                 c->pCurve           =   vPorts[port_id++];
367                 TRACE_PORT(vPorts[port_id]);
368                 c->pGraph[G_SC]     =   vPorts[port_id++];
369                 TRACE_PORT(vPorts[port_id]);
370                 c->pGraph[G_ENV]    =   vPorts[port_id++];
371                 TRACE_PORT(vPorts[port_id]);
372                 c->pGraph[G_GAIN]   =   vPorts[port_id++];
373                 TRACE_PORT(vPorts[port_id]);
374                 c->pMeter[M_SC]     =   vPorts[port_id++];
375                 TRACE_PORT(vPorts[port_id]);
376                 c->pMeter[M_CURVE]  =   vPorts[port_id++];
377                 TRACE_PORT(vPorts[port_id]);
378                 c->pMeter[M_ENV]    =   vPorts[port_id++];
379                 TRACE_PORT(vPorts[port_id]);
380                 c->pMeter[M_GAIN]   =   vPorts[port_id++];
381             }
382         }
383 
384         // Bind history
385         lsp_trace("Binding history ports");
386         for (size_t i=0; i<channels; ++i)
387         {
388             channel_t *c = &vChannels[i];
389 
390             // Skip meters visibility controls
391             TRACE_PORT(vPorts[port_id]);
392             port_id++;
393             TRACE_PORT(vPorts[port_id]);
394             port_id++;
395 
396             // Bind ports
397             TRACE_PORT(vPorts[port_id]);
398             c->pGraph[G_IN]     =   vPorts[port_id++];
399             TRACE_PORT(vPorts[port_id]);
400             c->pGraph[G_OUT]    =   vPorts[port_id++];
401             TRACE_PORT(vPorts[port_id]);
402             c->pMeter[M_IN]     =   vPorts[port_id++];
403             TRACE_PORT(vPorts[port_id]);
404             c->pMeter[M_OUT]    =   vPorts[port_id++];
405         }
406 
407         // Initialize curve (logarithmic) in range of -72 .. +24 db
408         float delta = (dyna_processor_base_metadata::CURVE_DB_MAX - dyna_processor_base_metadata::CURVE_DB_MIN) / (dyna_processor_base_metadata::CURVE_MESH_SIZE-1);
409         for (size_t i=0; i<dyna_processor_base_metadata::CURVE_MESH_SIZE; ++i)
410             vCurve[i]   = db_to_gain(dyna_processor_base_metadata::CURVE_DB_MIN + delta * i);
411 
412         // Initialize time points
413         delta       = dyna_processor_base_metadata::TIME_HISTORY_MAX / (dyna_processor_base_metadata::TIME_MESH_SIZE - 1);
414         for (size_t i=0; i<dyna_processor_base_metadata::TIME_MESH_SIZE; ++i)
415             vTime[i]    = dyna_processor_base_metadata::TIME_HISTORY_MAX - i*delta;
416     }
417 
destroy()418     void dyna_processor_base::destroy()
419     {
420         if (vChannels != NULL)
421         {
422             size_t channels = (nMode == DYNA_MONO) ? 1 : 2;
423             for (size_t i=0; i<channels; ++i)
424             {
425                 vChannels[i].sSC.destroy();
426                 vChannels[i].sSCEq.destroy();
427                 vChannels[i].sDelay.destroy();
428                 vChannels[i].sCompDelay.destroy();
429                 vChannels[i].sDryDelay.destroy();
430             }
431 
432             delete [] vChannels;
433             vChannels = NULL;
434         }
435 
436         if (pData != NULL)
437         {
438             delete [] pData;
439             pData = NULL;
440         }
441 
442         if (pIDisplay != NULL)
443         {
444             pIDisplay->detroy();
445             pIDisplay   = NULL;
446         }
447     }
448 
update_sample_rate(long sr)449     void dyna_processor_base::update_sample_rate(long sr)
450     {
451         size_t samples_per_dot  = seconds_to_samples(sr, dyna_processor_base_metadata::TIME_HISTORY_MAX / dyna_processor_base_metadata::TIME_MESH_SIZE);
452         size_t channels         = (nMode == DYNA_MONO) ? 1 : 2;
453         size_t max_delay        = millis_to_samples(fSampleRate, compressor_base_metadata::LOOKAHEAD_MAX);
454 
455         for (size_t i=0; i<channels; ++i)
456         {
457             channel_t *c = &vChannels[i];
458             c->sBypass.init(sr);
459             c->sProc.set_sample_rate(sr);
460             c->sSC.set_sample_rate(sr);
461             c->sSCEq.set_sample_rate(sr);
462             c->sDelay.init(max_delay);
463             c->sCompDelay.init(max_delay);
464             c->sDryDelay.init(max_delay);
465 
466             for (size_t j=0; j<G_TOTAL; ++j)
467                 c->sGraph[j].init(dyna_processor_base_metadata::TIME_MESH_SIZE, samples_per_dot);
468             c->sGraph[G_GAIN].fill(1.0f);
469             c->sGraph[G_GAIN].set_method(MM_MINIMUM);
470         }
471     }
472 
update_settings()473     void dyna_processor_base::update_settings()
474     {
475         filter_params_t fp;
476         size_t channels = (nMode == DYNA_MONO) ? 1 : 2;
477         bool bypass     = pBypass->getValue() >= 0.5f;
478 
479         // Global parameters
480         bPause          = pPause->getValue() >= 0.5f;
481         bClear          = pClear->getValue() >= 0.5f;
482         bMSListen       = (pMSListen != NULL) ? pMSListen->getValue() >= 0.5f : false;
483         fInGain         = pInGain->getValue();
484         float out_gain  = pOutGain->getValue();
485         size_t latency  = 0;
486 
487         for (size_t i=0; i<channels; ++i)
488         {
489             channel_t *c    = &vChannels[i];
490 
491             // Update bypass settings
492             c->sBypass.set_bypass(bypass);
493 
494             // Update sidechain settings
495             c->nScType      = c->pScType->getValue();
496             c->bScListen    = c->pScListen->getValue() >= 0.5f;
497 
498             c->sSC.set_gain(c->pScPreamp->getValue());
499             c->sSC.set_mode((c->pScMode != NULL) ? c->pScMode->getValue() : SCM_RMS);
500             c->sSC.set_source((c->pScSource != NULL) ? c->pScSource->getValue() : SCS_MIDDLE);
501             c->sSC.set_reactivity(c->pScReactivity->getValue());
502             c->sSC.set_stereo_mode(((nMode == DYNA_MS) && (c->nScType != SCT_EXTERNAL)) ? SCSM_MIDSIDE : SCSM_STEREO);
503 
504             // Setup hi-pass filter for sidechain
505             size_t hp_slope = c->pScHpfMode->getValue() * 2;
506             fp.nType        = (hp_slope > 0) ? FLT_BT_BWC_HIPASS : FLT_NONE;
507             fp.fFreq        = c->pScHpfFreq->getValue();
508             fp.fFreq2       = fp.fFreq;
509             fp.fGain        = 1.0f;
510             fp.nSlope       = hp_slope;
511             fp.fQuality     = 0.0f;
512             c->sSCEq.set_params(0, &fp);
513 
514             // Setup low-pass filter for sidechain
515             size_t lp_slope = c->pScLpfMode->getValue() * 2;
516             fp.nType        = (lp_slope > 0) ? FLT_BT_BWC_LOPASS : FLT_NONE;
517             fp.fFreq        = c->pScLpfFreq->getValue();
518             fp.fFreq2       = fp.fFreq;
519             fp.fGain        = 1.0f;
520             fp.nSlope       = lp_slope;
521             fp.fQuality     = 0.0f;
522             c->sSCEq.set_params(1, &fp);
523 
524             // Update delay
525             size_t delay    = millis_to_samples(fSampleRate, (c->pScLookahead != NULL) ? c->pScLookahead->getValue() : 0);
526             c->sDelay.set_delay(delay);
527             if (delay > latency)
528                 latency         = delay;
529 
530             // Update processor settings
531             c->sProc.set_attack_time(0, c->pAttackTime[0]->getValue());
532             c->sProc.set_release_time(0, c->pReleaseTime[0]->getValue());
533 
534             for (size_t j=0; j<dyna_processor_base_metadata::DOTS; ++j)
535             {
536                 c->sProc.set_attack_level(j, (c->pAttackOn[j]->getValue() >= 0.5f) ? c->pAttackLvl[j]->getValue() : -1.0f);
537                 c->sProc.set_attack_time(j+1, c->pAttackTime[j+1]->getValue());
538 
539                 c->sProc.set_release_level(j, (c->pReleaseOn[j]->getValue() >= 0.5f) ? c->pReleaseLvl[j]->getValue() : -1.0f);
540                 c->sProc.set_release_time(j+1, c->pReleaseTime[j+1]->getValue());
541 
542                 if ((c->pDotOn[j] != NULL) && (c->pDotOn[j]->getValue() >= 0.5f))
543                     c->sProc.set_dot(j, c->pThreshold[j]->getValue(), c->pGain[j]->getValue(), c->pKnee[j]->getValue());
544                 else
545                     c->sProc.set_dot(j, -1.0f, -1.0f, -1.0f);
546             }
547 
548             float makeup = c->pMakeup->getValue();
549             float out_ratio = c->pHighRatio->getValue();
550             if ((c->nScType == SCT_FEED_BACK) && (out_ratio >= 1.0f)) // Prevent from infinite feedback
551                 out_ratio = 1.0f;
552             c->sProc.set_in_ratio(c->pLowRatio->getValue());
553             c->sProc.set_out_ratio(out_ratio);
554 
555             if (c->fMakeup != makeup)
556             {
557                 c->fMakeup          = makeup;
558                 c->nSync           |= S_CURVE;
559             }
560 
561             // Update gains
562             c->fDryGain         = c->pDryGain->getValue() * out_gain;
563             c->fWetGain         = c->pWetGain->getValue() * out_gain;
564 
565             // Check modification flag
566             if (c->sProc.modified())
567             {
568                 c->sProc.update_settings();
569                 c->nSync           |= S_CURVE | S_MODEL;
570             }
571         }
572 
573         // Tune compensation delays
574         for (size_t i=0; i<channels; ++i)
575         {
576             channel_t *c    = &vChannels[i];
577             c->sCompDelay.set_delay(latency - c->sDelay.get_delay());
578             c->sDryDelay.set_delay(latency);
579         }
580 
581         // Report latency
582         set_latency(latency);
583     }
584 
ui_activated()585     void dyna_processor_base::ui_activated()
586     {
587         size_t channels     = (nMode == DYNA_MONO) ? 1 : 2;
588         for (size_t i=0; i<channels; ++i)
589             vChannels[i].nSync     = S_CURVE | S_MODEL;
590         bUISync             = true;
591     }
592 
process_feedback(channel_t * c,size_t i,size_t channels)593     float dyna_processor_base::process_feedback(channel_t *c, size_t i, size_t channels)
594     {
595         // Read input samples
596         float in[2];
597         if (channels > 1)
598         {
599             in[0] = vChannels[0].fFeedback;
600             in[1] = vChannels[1].fFeedback;
601         }
602         else
603         {
604             in[0] = c->fFeedback;
605             in[1] = 0.0f;
606         }
607 
608         // Process sidechain
609         float scin      = c->sSC.process(in);
610 
611         // Perform compression routine
612         c->vGain[i]     = c->sProc.process(&c->vEnv[i], scin);
613         c->vOut[i]      = c->vGain[i] * c->vIn[i];
614 
615         return scin;
616     }
617 
process_non_feedback(channel_t * c,float ** in,size_t samples)618     void dyna_processor_base::process_non_feedback(channel_t *c, float **in, size_t samples)
619     {
620         c->sSC.process(c->vSc, const_cast<const float **>(in), samples);
621         c->sProc.process(c->vGain, c->vEnv, c->vSc, samples);
622         dsp::mul3(c->vOut, c->vGain, c->vIn, samples); // Adjust gain for input
623     }
624 
process(size_t samples)625     void dyna_processor_base::process(size_t samples)
626     {
627         size_t channels = (nMode == DYNA_MONO) ? 1 : 2;
628         size_t feedback = 0;
629 
630         float *in_buf[2];   // Input buffer
631         float *out_buf[2];  // Output buffer
632         float *sc_buf[2];   // Sidechain source
633         float *in[2];       // Buffet to pass to sidechain
634 
635         // Prepare audio channels
636         for (size_t i=0; i<channels; ++i)
637         {
638             channel_t *c        = &vChannels[i];
639 
640             // Initialize pointers
641             in_buf[i]           = c->pIn->getBuffer<float>();
642             out_buf[i]          = c->pOut->getBuffer<float>();
643             sc_buf[i]           = (c->pSC != NULL) ? c->pSC->getBuffer<float>() : in_buf[i];
644 
645             // Analyze channel mode
646             if (c->nScType == SCT_FEED_BACK)
647                 feedback |= (1 << i);
648 
649 //            dump_buffer("in_buf", in_buf[i], samples);
650         }
651 
652         // Perform compression
653         size_t left = samples;
654         while (left > 0)
655         {
656             // Detemine number of samples to process
657             size_t to_process = (left > DYNA_PROC_BUF_SIZE) ? DYNA_PROC_BUF_SIZE : left;
658 
659             // Prepare audio channels
660             if (nMode == DYNA_MONO)
661                 dsp::mul_k3(vChannels[0].vIn, in_buf[0], fInGain, to_process);
662             else if (nMode == DYNA_MS)
663             {
664                 dsp::lr_to_ms(vChannels[0].vIn, vChannels[1].vIn, in_buf[0], in_buf[1], to_process);
665                 dsp::mul_k2(vChannels[0].vIn, fInGain, to_process);
666                 dsp::mul_k2(vChannels[1].vIn, fInGain, to_process);
667             }
668             else
669             {
670                 dsp::mul_k3(vChannels[0].vIn, in_buf[0], fInGain, to_process);
671                 dsp::mul_k3(vChannels[1].vIn, in_buf[1], fInGain, to_process);
672             }
673 
674             // Process meters
675             for (size_t i=0; i<channels; ++i)
676             {
677                 channel_t *c        = &vChannels[i];
678                 // Update input graph
679                 c->sGraph[G_IN].process(c->vIn, to_process);
680                 c->pMeter[M_IN]->setValue(dsp::abs_max(c->vIn, to_process));
681             }
682 
683             // Do compression
684             switch (feedback)
685             {
686                 case 0:
687                 {
688                     if (channels > 1) // Process second channel in stereo pair
689                     {
690                         // First channel
691                         in[0]   = (vChannels[0].nScType == SCT_EXTERNAL) ? sc_buf[0] : vChannels[0].vIn;
692                         in[1]   = (vChannels[0].nScType == SCT_EXTERNAL) ? sc_buf[1] : vChannels[1].vIn;
693                         process_non_feedback(&vChannels[0], in, to_process);
694                         vChannels[0].fFeedback      = vChannels[0].vOut[to_process-1];
695 
696                         // Second channel
697                         in[0]   = (vChannels[1].nScType == SCT_EXTERNAL) ? sc_buf[0] : vChannels[0].vIn;
698                         in[1]   = (vChannels[1].nScType == SCT_EXTERNAL) ? sc_buf[1] : vChannels[1].vIn;
699                         process_non_feedback(&vChannels[1], in, to_process);
700                         vChannels[1].fFeedback      = vChannels[1].vOut[to_process-1];
701                     }
702                     else
703                     {
704                         // Only one channel
705                         in[0]   = (vChannels[0].nScType == SCT_EXTERNAL) ? sc_buf[0] : vChannels[0].vIn;
706                         in[1]   = NULL;
707                         process_non_feedback(&vChannels[0], in, to_process);
708                         vChannels[0].fFeedback      = vChannels[0].vOut[to_process-1];
709                     }
710 
711                     break;
712                 }
713 
714                 case 1:
715                 {
716                     // 0=FB [1=FF/EXT]
717                     if (channels > 1)
718                     {
719                         // Second channel
720                         in[0]   = (vChannels[1].nScType == SCT_EXTERNAL) ? sc_buf[0] : vChannels[0].vIn;
721                         in[1]   = (vChannels[1].nScType == SCT_EXTERNAL) ? sc_buf[1] : vChannels[1].vIn;
722                         process_non_feedback(&vChannels[1], in, to_process);
723 //                        vChannels[1].fFeedback      = vChannels[1].vOut[to_process-1]; // do not update feedback at this time
724 
725                         // Process feedback channel
726                         for (size_t i=0; i<to_process; ++i)
727                         {
728                             vChannels[0].vSc[i]     = process_feedback(&vChannels[0], i, channels);
729                             vChannels[0].fFeedback  = vChannels[0].vOut[i];
730                             vChannels[1].fFeedback  = vChannels[1].vOut[i];
731                         }
732                     }
733                     else
734                     {
735                         // Process feedback channel
736                         for (size_t i=0; i<to_process; ++i)
737                         {
738                             vChannels[0].vSc[i]     = process_feedback(&vChannels[0], i, channels);
739                             vChannels[0].fFeedback  = vChannels[0].vOut[i];
740                         }
741                     }
742 
743                     break;
744                 }
745 
746                 case 2:
747                 {
748                     // 0=FF/EXT 1=FB
749                     // First channel
750                     in[0]   = (vChannels[0].nScType == SCT_EXTERNAL) ? sc_buf[0] : vChannels[0].vIn;
751                     in[1]   = (vChannels[0].nScType == SCT_EXTERNAL) ? sc_buf[1] : vChannels[1].vIn;
752                     process_non_feedback(&vChannels[0], in, to_process);
753 //                    vChannels[0].fFeedback      = vChannels[0].vOut[to_process-1]; // do not update feedback at this time
754 
755                     // Process feedback channel
756                     for (size_t i=0; i<to_process; ++i)
757                     {
758                         vChannels[1].vSc[i]     = process_feedback(&vChannels[1], i, channels);
759                         vChannels[1].fFeedback  = vChannels[1].vOut[i];
760                         vChannels[0].fFeedback  = vChannels[0].vOut[i];
761                     }
762 
763                     break;
764                 }
765 
766                 case 3:
767                 {
768                     // 0=FB, 1=FB
769                     for (size_t i=0; i<to_process; ++i)
770                     {
771                         vChannels[0].vSc[i]     = process_feedback(&vChannels[0], i, channels);
772                         vChannels[1].vSc[i]     = process_feedback(&vChannels[1], i, channels);
773                         vChannels[0].fFeedback  = vChannels[0].vOut[i];
774                         vChannels[1].fFeedback  = vChannels[1].vOut[i];
775                     }
776                     break;
777                 }
778                 default:
779                     break;
780             }
781 
782             // Apply gain to each channel and process meters
783             for (size_t i=0; i<channels; ++i)
784             {
785                 channel_t *c        = &vChannels[i];
786 
787                 // Add delay to original signal and apply gain
788                 c->sDelay.process(c->vOut, c->vIn, c->vGain, to_process);
789 
790                 // Apply latency compensation delay
791                 c->sCompDelay.process(c->vOut, c->vOut, to_process);
792 
793                 // Process graph outputs
794                 if ((i == 0) || (nMode != DYNA_STEREO))
795                 {
796                     c->sGraph[G_SC].process(c->vSc, to_process);                        // Sidechain signal
797                     c->pMeter[M_SC]->setValue(dsp::abs_max(c->vSc, to_process));
798 
799                     c->sGraph[G_GAIN].process(c->vGain, to_process);                    // Gain reduction signal
800                     c->pMeter[M_GAIN]->setValue(dsp::abs_max(c->vGain, to_process));
801 
802                     c->sGraph[G_ENV].process(c->vEnv, to_process);                      // Envelope signal
803                     c->pMeter[M_ENV]->setValue(dsp::abs_max(c->vEnv, to_process));
804                 }
805             }
806 
807             // Form output signal
808             if (nMode == DYNA_MS)
809             {
810                 channel_t *cm       = &vChannels[0];
811                 channel_t *cs       = &vChannels[1];
812 
813                 dsp::mix2(cm->vOut, cm->vIn, cm->fMakeup * cm->fWetGain, cm->fDryGain, to_process);
814                 dsp::mix2(cs->vOut, cs->vIn, cs->fMakeup * cs->fWetGain, cs->fDryGain, to_process);
815 
816                 cm->sGraph[G_OUT].process(cm->vOut, to_process);
817                 cm->pMeter[M_OUT]->setValue(dsp::abs_max(cm->vOut, to_process));
818                 cs->sGraph[G_OUT].process(cs->vOut, to_process);
819                 cs->pMeter[M_OUT]->setValue(dsp::abs_max(cs->vOut, to_process));
820 
821                 if (!bMSListen)
822                     dsp::ms_to_lr(cm->vOut, cs->vOut, cm->vOut, cs->vOut, to_process);
823                 if (cm->bScListen)
824                     dsp::copy(cm->vOut, cm->vSc, to_process);
825                 if (cs->bScListen)
826                     dsp::copy(cs->vOut, cs->vSc, to_process);
827             }
828             else
829             {
830                 for (size_t i=0; i<channels; ++i)
831                 {
832                     // Mix dry/wet signal or copy sidechain signal
833                     channel_t *c        = &vChannels[i];
834                     if (c->bScListen)
835                         dsp::copy(c->vOut, c->vSc, to_process);
836                     else
837                         dsp::mix2(c->vOut, c->vIn, c->fMakeup * c->fWetGain, c->fDryGain, to_process);
838 
839                     c->sGraph[G_OUT].process(c->vOut, to_process);                      // Output signal
840                     c->pMeter[M_OUT]->setValue(dsp::abs_max(c->vOut, to_process));
841                 }
842             }
843 
844             // Final metering
845             for (size_t i=0; i<channels; ++i)
846             {
847                 // Apply bypass
848                 channel_t *c        = &vChannels[i];
849                 c->sDryDelay.process(c->vIn, in_buf[i], to_process);            // Apply delay compensation
850                 c->sBypass.process(out_buf[i], c->vIn, c->vOut, to_process);
851 
852 //                dump_buffer("out_buf", out_buf[i], samples);
853 
854                 in_buf[i]          += to_process;
855                 out_buf[i]         += to_process;
856                 sc_buf[i]          += to_process;
857             }
858 
859             left       -= to_process;
860         }
861 
862         if ((!bPause) || (bClear) || (bUISync))
863         {
864             // Process mesh requests
865             for (size_t i=0; i<channels; ++i)
866             {
867                 // Get channel
868                 channel_t *c        = &vChannels[i];
869 
870                 for (size_t j=0; j<G_TOTAL; ++j)
871                 {
872                     // Check that port is bound
873                     if (c->pGraph[j] == NULL)
874                         continue;
875 
876                     // Clear data if requested
877                     if (bClear)
878                         dsp::fill_zero(c->sGraph[j].data(), dyna_processor_base_metadata::TIME_MESH_SIZE);
879 
880                     // Get mesh
881                     mesh_t *mesh    = c->pGraph[j]->getBuffer<mesh_t>();
882                     if ((mesh != NULL) && (mesh->isEmpty()))
883                     {
884                         // Fill mesh with new values
885                         dsp::copy(mesh->pvData[0], vTime, dyna_processor_base_metadata::TIME_MESH_SIZE);
886                         dsp::copy(mesh->pvData[1], c->sGraph[j].data(), dyna_processor_base_metadata::TIME_MESH_SIZE);
887                         mesh->data(2, dyna_processor_base_metadata::TIME_MESH_SIZE);
888                     }
889                 } // for j
890             }
891 
892             bUISync = false;
893         }
894 
895         // Output curves for each channel
896         for (size_t i=0; i<channels; ++i)
897         {
898             channel_t *c       = &vChannels[i];
899 
900             // Output curve model
901             if (c->pModel != NULL)
902             {
903                 mesh_t *mesh            = c->pModel->getBuffer<mesh_t>();
904                 if ((c->nSync & S_CURVE) && (mesh != NULL) && (mesh->isEmpty()))
905                 {
906                     // Copy frequency points
907                     dsp::copy(mesh->pvData[0], vCurve, dyna_processor_base_metadata::CURVE_MESH_SIZE);
908                     c->sProc.model(mesh->pvData[1], vCurve, dyna_processor_base_metadata::CURVE_MESH_SIZE);
909 
910                     // Mark mesh containing data
911                     mesh->data(2, dyna_processor_base_metadata::CURVE_MESH_SIZE);
912                     c->nSync &= ~S_MODEL;
913                 }
914             }
915 
916             // Output curve
917             if (c->pCurve != NULL)
918             {
919                 mesh_t *mesh            = c->pCurve->getBuffer<mesh_t>();
920                 if ((c->nSync & S_CURVE) && (mesh != NULL) && (mesh->isEmpty()))
921                 {
922                     // Copy frequency points
923                     dsp::copy(mesh->pvData[0], vCurve, dyna_processor_base_metadata::CURVE_MESH_SIZE);
924                     c->sProc.curve(mesh->pvData[1], vCurve, dyna_processor_base_metadata::CURVE_MESH_SIZE);
925                     if (c->fMakeup != 1.0f)
926                         dsp::mul_k2(mesh->pvData[1], c->fMakeup, dyna_processor_base_metadata::CURVE_MESH_SIZE);
927 
928                     // Mark mesh containing data
929                     mesh->data(2, dyna_processor_base_metadata::CURVE_MESH_SIZE);
930                     c->nSync &= ~S_CURVE;
931                 }
932             }
933 
934             // Update meter
935             if ((c->pMeter[M_ENV] != NULL) && (c->pMeter[M_CURVE] != NULL))
936             {
937                 c->fDotIn   = c->pMeter[M_ENV]->getValue();
938                 c->fDotOut  = c->sProc.curve(c->fDotIn) * c->fMakeup;
939                 c->pMeter[M_CURVE]->setValue(c->fDotOut);
940             }
941         }
942 
943         // Request for redraw
944         if (pWrapper != NULL)
945             pWrapper->query_display_draw();
946     }
947 
inline_display(ICanvas * cv,size_t width,size_t height)948     bool dyna_processor_base::inline_display(ICanvas *cv, size_t width, size_t height)
949     {
950         // Check proportions
951         if (height > width)
952             height  = width;
953 
954         // Init canvas
955         if (!cv->init(width, height))
956             return false;
957         width   = cv->width();
958         height  = cv->height();
959 
960         // Clear background
961         bool bypassing = vChannels[0].sBypass.bypassing();
962         cv->set_color_rgb((bypassing) ? CV_DISABLED : CV_BACKGROUND);
963         cv->paint();
964 
965         float zx    = 1.0f/GAIN_AMP_M_72_DB;
966         float zy    = 1.0f/GAIN_AMP_M_72_DB;
967         float dx    = width/(logf(GAIN_AMP_P_24_DB)-logf(GAIN_AMP_M_72_DB));
968         float dy    = height/(logf(GAIN_AMP_M_72_DB)-logf(GAIN_AMP_P_24_DB));
969 
970         // Draw horizontal and vertical lines
971         cv->set_line_width(1.0);
972         cv->set_color_rgb((bypassing) ? CV_SILVER: CV_YELLOW, 0.5f);
973         for (float i=GAIN_AMP_M_72_DB; i<GAIN_AMP_P_24_DB; i *= GAIN_AMP_P_24_DB)
974         {
975             float ax = dx*(logf(i*zx));
976             float ay = height + dy*(logf(i*zy));
977             cv->line(ax, 0, ax, height);
978             cv->line(0, ay, width, ay);
979         }
980 
981         // Draw 1:1 line
982         cv->set_line_width(2.0);
983         cv->set_color_rgb(CV_GRAY);
984         {
985             float ax1 = dx*(logf(GAIN_AMP_M_72_DB*zx));
986             float ax2 = dx*(logf(GAIN_AMP_P_24_DB*zx));
987             float ay1 = height + dy*(logf(GAIN_AMP_M_72_DB*zy));
988             float ay2 = height + dy*(logf(GAIN_AMP_P_24_DB*zy));
989             cv->line(ax1, ay1, ax2, ay2);
990         }
991 
992         // Draw axis
993         cv->set_color_rgb((bypassing) ? CV_SILVER : CV_WHITE);
994         {
995             float ax = dx*(logf(GAIN_AMP_0_DB*zx));
996             float ay = height + dy*(logf(GAIN_AMP_0_DB*zy));
997             cv->line(ax, 0, ax, height);
998             cv->line(0, ay, width, ay);
999         }
1000 
1001         // Reuse display
1002         pIDisplay           = float_buffer_t::reuse(pIDisplay, 4, width);
1003         float_buffer_t *b   = pIDisplay;
1004         if (b == NULL)
1005             return false;
1006 
1007         size_t channels = ((nMode == DYNA_MONO) || (nMode == DYNA_STEREO)) ? 1 : 2;
1008         static uint32_t c_colors[] = {
1009                 CV_MIDDLE_CHANNEL, CV_MIDDLE_CHANNEL,
1010                 CV_MIDDLE_CHANNEL, CV_MIDDLE_CHANNEL,
1011                 CV_LEFT_CHANNEL, CV_RIGHT_CHANNEL,
1012                 CV_MIDDLE_CHANNEL, CV_SIDE_CHANNEL
1013                };
1014 
1015         bool aa = cv->set_anti_aliasing(true);
1016         cv->set_line_width(2);
1017 
1018         for (size_t i=0; i<channels; ++i)
1019         {
1020             channel_t *c    = &vChannels[i];
1021 
1022             for (size_t j=0; j<width; ++j)
1023             {
1024                 size_t k        = (j*dyna_processor_base_metadata::CURVE_MESH_SIZE)/width;
1025                 b->v[0][j]      = vCurve[k];
1026             }
1027             c->sProc.curve(b->v[1], b->v[0], width);
1028             if (c->fMakeup != 1.0f)
1029                 dsp::mul_k2(b->v[1], c->fMakeup, width);
1030 
1031             dsp::fill(b->v[2], 0.0f, width);
1032             dsp::fill(b->v[3], height, width);
1033             dsp::axis_apply_log1(b->v[2], b->v[0], zx, dx, width);
1034             dsp::axis_apply_log1(b->v[3], b->v[1], zy, dy, width);
1035 
1036             // Draw mesh
1037             uint32_t color = (bypassing || !(active())) ? CV_SILVER : c_colors[nMode*2 + i];
1038             cv->set_color_rgb(color);
1039             cv->draw_lines(b->v[2], b->v[3], width);
1040         }
1041 
1042         // Draw dot
1043         if (active())
1044         {
1045             for (size_t i=0; i<channels; ++i)
1046             {
1047                 channel_t *c    = &vChannels[i];
1048 
1049                 uint32_t color = (bypassing) ? CV_SILVER : c_colors[nMode*2 + i];
1050                 Color c1(color), c2(color);
1051                 c2.alpha(0.9);
1052 
1053                 float ax = dx*(logf(c->fDotIn*zx));
1054                 float ay = height + dy*(logf(c->fDotOut*zy));
1055 
1056                 cv->radial_gradient(ax, ay, c1, c2, 12);
1057                 cv->set_color_rgb(0);
1058                 cv->circle(ax, ay, 4);
1059                 cv->set_color_rgb(color);
1060                 cv->circle(ax, ay, 3);
1061             }
1062         }
1063 
1064         cv->set_anti_aliasing(aa);
1065 
1066         return true;
1067     }
1068 
1069     //-------------------------------------------------------------------------
1070     // Compressor derivatives
dyna_processor_mono()1071     dyna_processor_mono::dyna_processor_mono() : dyna_processor_base(metadata, false, DYNA_MONO)
1072     {
1073     }
1074 
dyna_processor_stereo()1075     dyna_processor_stereo::dyna_processor_stereo() : dyna_processor_base(metadata, false, DYNA_STEREO)
1076     {
1077     }
1078 
dyna_processor_lr()1079     dyna_processor_lr::dyna_processor_lr() : dyna_processor_base(metadata, false, DYNA_LR)
1080     {
1081     }
1082 
dyna_processor_ms()1083     dyna_processor_ms::dyna_processor_ms() : dyna_processor_base(metadata, false, DYNA_MS)
1084     {
1085     }
1086 
sc_dyna_processor_mono()1087     sc_dyna_processor_mono::sc_dyna_processor_mono() : dyna_processor_base(metadata, true, DYNA_MONO)
1088     {
1089     }
1090 
sc_dyna_processor_stereo()1091     sc_dyna_processor_stereo::sc_dyna_processor_stereo() : dyna_processor_base(metadata, true, DYNA_STEREO)
1092     {
1093     }
1094 
sc_dyna_processor_lr()1095     sc_dyna_processor_lr::sc_dyna_processor_lr() : dyna_processor_base(metadata, true, DYNA_LR)
1096     {
1097     }
1098 
sc_dyna_processor_ms()1099     sc_dyna_processor_ms::sc_dyna_processor_ms() : dyna_processor_base(metadata, true, DYNA_MS)
1100     {
1101     }
1102 }
1103