1 /*                                                     -*- linux-c -*-
2     Copyright (C) 2004 Tom Szilagyi
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18     $Id: tap_dynamics_m.c,v 1.2 2004/06/15 14:50:55 tszilagyi Exp $
19 */
20 
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <inttypes.h>
25 #include <string.h>
26 #include <math.h>
27 
28 #include "ladspa.h"
29 #include "tap_utils.h"
30 
31 
32 /* ***** VERY IMPORTANT! *****
33  *
34  * If you enable this, the plugin will use float arithmetics in DSP
35  * calculations.  This usually yields lower average CPU usage, but
36  * occasionaly may result in high CPU peaks which cause trouble to you
37  * and your JACK server.  The default is to use fixpoint arithmetics
38  * (with the following #define commented out).  But (depending on the
39  * processor on which you run the code) you may find floating point
40  * mode usable.
41  */
42 /*#define DYN_CALC_FLOAT*/
43 
44 
45 typedef signed int sample;
46 
47 /* coefficient for float to sample (signed int) conversion */
48 #define F2S 2147483
49 
50 
51 #ifdef DYN_CALC_FLOAT
52 typedef LADSPA_Data dyn_t;
53 typedef float rms_t;
54 #else
55 typedef sample dyn_t;
56 typedef int64_t rms_t;
57 #endif
58 
59 
60 
61 /* The Unique ID of the plugin: */
62 
63 #define ID_MONO         2152
64 
65 /* The port numbers for the plugin: */
66 
67 #define ATTACK          0
68 #define RELEASE         1
69 #define OFFSGAIN        2
70 #define MUGAIN          3
71 #define RMSENV          4
72 #define MODGAIN         5
73 #define MODE            6
74 #define INPUT           7
75 #define OUTPUT          8
76 
77 
78 /* Total number of ports */
79 
80 #define PORTCOUNT_MONO   9
81 
82 
83 #define TABSIZE 256
84 #define RMSSIZE 64
85 
86 
87 typedef struct {
88 	rms_t        buffer[RMSSIZE];
89         unsigned int pos;
90         rms_t        sum;
91 } rms_env;
92 
93 
94 /* max. number of breakpoints on in/out dB graph */
95 #define MAX_POINTS 20
96 
97 typedef struct {
98 	LADSPA_Data x;
99 	LADSPA_Data y;
100 } GRAPH_POINT;
101 
102 typedef struct {
103 	unsigned long num_points;
104 	GRAPH_POINT points[MAX_POINTS];
105 } DYNAMICS_DATA;
instantiate_ChorusFlanger(const LADSPA_Descriptor * Descriptor,unsigned long sample_rate)106 
107 #include "tap_dynamics_presets.h"
108 
109 
110 /* The structure used to hold port connection information and state */
111 
112 typedef struct {
113 	LADSPA_Data * attack;
114 	LADSPA_Data * release;
115 	LADSPA_Data * offsgain;
116 	LADSPA_Data * mugain;
117 	LADSPA_Data * rmsenv;
118 	LADSPA_Data * modgain;
119 	LADSPA_Data * mode;
120 	LADSPA_Data * input;
121 	LADSPA_Data * output;
122 	unsigned long sample_rate;
123 
124 	float * as;
125 	unsigned long count;
126 	dyn_t amp;
127 	dyn_t env;
128 	float gain;
129 	float gain_out;
130 	rms_env * rms;
131 	rms_t sum;
132 
133 	DYNAMICS_DATA graph;
134 
135 	LADSPA_Data run_adding_gain;
136 } Dynamics;
137 
138 
139 
140 /* RMS envelope stuff, grabbed without a second thought from Steve Harris's swh-plugins, util/rms.c */
activate_ChorusFlanger(LADSPA_Handle Instance)141 /* Adapted, though, to be able to use fixed-point arithmetics as well. */
142 
143 rms_env *
144 rms_env_new(void) {
145 
146         rms_env * new = (rms_env *)calloc(1, sizeof(rms_env));
147 
148         return new;
149 }
150 
151 void
152 rms_env_reset(rms_env *r) {
153 
154         unsigned int i;
155 
156         for (i = 0; i < RMSSIZE; i++) {
157                 r->buffer[i] = 0.0f;
158         }
159         r->pos = 0;
connect_port_ChorusFlanger(LADSPA_Handle Instance,unsigned long Port,LADSPA_Data * data)160         r->sum = 0.0f;
161 }
162 
163 inline static
164 dyn_t
165 rms_env_process(rms_env *r, const rms_t x) {
166 
167         r->sum -= r->buffer[r->pos];
168         r->sum += x;
169         r->buffer[r->pos] = x;
170         r->pos = (r->pos + 1) & (RMSSIZE - 1);
171 
172 #ifdef DYN_CALC_FLOAT
173         return sqrt(r->sum / (float)RMSSIZE);
174 #else
175         return sqrt(r->sum / RMSSIZE);
176 #endif
177 }
178 
179 
180 
181 inline
182 LADSPA_Data
183 get_table_gain(int mode, LADSPA_Data level) {
184 
185 	LADSPA_Data x1 = -80.0f;
186 	LADSPA_Data y1 = -80.0f;
187 	LADSPA_Data x2 = 0.0f;
188 	LADSPA_Data y2 = 0.0f;
189 	int i = 0;
190 
191 	if (level <= -80.0f)
192 		return get_table_gain(mode, -79.9f);
193 
194 	while (i < dyn_data[mode].num_points && dyn_data[mode].points[i].x < level) {
195 		x1 = dyn_data[mode].points[i].x;
196 		y1 = dyn_data[mode].points[i].y;
197 		i++;
198 	}
199 	if (i < dyn_data[mode].num_points) {
200 		x2 = dyn_data[mode].points[i].x;
201 		y2 = dyn_data[mode].points[i].y;
202 	} else
203 		return 0.0f;
204 
run_ChorusFlanger(LADSPA_Handle Instance,unsigned long SampleCount)205 	return y1 + ((level - x1) * (y2 - y1) / (x2 - x1)) - level;
206 }
207 
208 
209 /* Construct a new plugin instance. */
210 LADSPA_Handle
211 instantiate_Dynamics(const LADSPA_Descriptor * Descriptor, unsigned long sample_rate) {
212 
213 	LADSPA_Handle * ptr;
214 
215 	float * as = NULL;
216 	unsigned int count = 0;
217         dyn_t amp = 0.0f;
218 	dyn_t env = 0.0f;
219 	float gain = 0.0f;
220 	float gain_out = 0.0f;
221 	rms_env * rms = NULL;
222 	rms_t sum = 0;
223 	int i;
224 
225 	if ((ptr = malloc(sizeof(Dynamics))) == NULL)
226 		return NULL;
227 
228 	((Dynamics *)ptr)->sample_rate = sample_rate;
229 	((Dynamics *)ptr)->run_adding_gain = 1.0;
230 
231         if ((rms = rms_env_new()) == NULL)
232 		return NULL;
233 
234         if ((as = malloc(TABSIZE * sizeof(float))) == NULL)
235 		return NULL;
236 
237         as[0] = 1.0f;
238         for (i = 1; i < TABSIZE; i++) {
239 		as[i] = expf(-1.0f / (sample_rate * (float)i / (float)TABSIZE));
240         }
241 
242         ((Dynamics *)ptr)->as = as;
243         ((Dynamics *)ptr)->count = count;
244         ((Dynamics *)ptr)->amp = amp;
245         ((Dynamics *)ptr)->env = env;
246         ((Dynamics *)ptr)->gain = gain;
247         ((Dynamics *)ptr)->gain_out = gain_out;
248         ((Dynamics *)ptr)->rms = rms;
249         ((Dynamics *)ptr)->sum = sum;
250 
251 	return ptr;
252 }
253 
254 
255 
256 /* Connect a port to a data location. */
257 void
258 connect_port_Dynamics(LADSPA_Handle Instance,
259 		     unsigned long Port,
260 		     LADSPA_Data * DataLocation) {
261 
262 	Dynamics * ptr = (Dynamics *)Instance;
263 
264 	switch (Port) {
265 	case ATTACK:
266 		ptr->attack = DataLocation;
267 		break;
268 	case RELEASE:
269 		ptr->release = DataLocation;
270 		break;
271 	case OFFSGAIN:
272 		ptr->offsgain = DataLocation;
273 		break;
274 	case MUGAIN:
275 		ptr->mugain = DataLocation;
276 		break;
277 	case RMSENV:
278 		ptr->rmsenv = DataLocation;
279 		*(ptr->rmsenv) = -60.0f;
280 		break;
281 	case MODGAIN:
282 		ptr->modgain = DataLocation;
283 		*(ptr->modgain) = 0.0f;
284 		break;
285 	case MODE:
286 		ptr->mode = DataLocation;
287 		break;
288 	case INPUT:
289 		ptr->input = DataLocation;
290 		break;
291 	case OUTPUT:
292 		ptr->output = DataLocation;
293 		break;
294 	}
295 }
296 
297 
298 
299 void
300 run_Dynamics(LADSPA_Handle Instance,
301 	     unsigned long sample_count) {
302 
303 	Dynamics * ptr = (Dynamics *)Instance;
304 	LADSPA_Data * input = ptr->input;
305 	LADSPA_Data * output = ptr->output;
306         const float attack = LIMIT(*(ptr->attack), 4.0f, 500.0f);
307         const float release = LIMIT(*(ptr->release), 4.0f, 1000.0f);
308         const float offsgain = LIMIT(*(ptr->offsgain), -20.0f, 20.0f);
set_run_adding_gain_ChorusFlanger(LADSPA_Handle Instance,LADSPA_Data gain)309         const float mugain = db2lin(LIMIT(*(ptr->mugain), -20.0f, 20.0f));
310 	const int mode = LIMIT(*(ptr->mode), 0, NUM_MODES-1);
311 	unsigned long sample_index;
312 
313         dyn_t amp = ptr->amp;
314         dyn_t env = ptr->env;
315         float * as = ptr->as;
316         unsigned int count = ptr->count;
317         float gain = ptr->gain;
318         float gain_out = ptr->gain_out;
run_adding_ChorusFlanger(LADSPA_Handle Instance,unsigned long SampleCount)319         rms_env * rms = ptr->rms;
320         rms_t sum = ptr->sum;
321 
322         const float ga = as[(unsigned int)(attack * 0.001f * (float)(TABSIZE-1))];
323         const float gr = as[(unsigned int)(release * 0.001f * (float)(TABSIZE-1))];
324         const float ef_a = ga * 0.25f;
325         const float ef_ai = 1.0f - ef_a;
326 
327 	float level = 0.0f;
328 	float adjust = 0.0f;
329 
330         for (sample_index = 0; sample_index < sample_count; sample_index++) {
331 
332 #ifdef DYN_CALC_FLOAT
333 		sum += input[sample_index] * input[sample_index];
334 		if (amp > env) {
335 			env = env * ga + amp * (1.0f - ga);
336 		} else {
337 			env = env * gr + amp * (1.0f - gr);
338 		}
339 #else
340 		sum += (rms_t)(input[sample_index] * F2S * input[sample_index] * F2S);
341 		if (amp) {
342 			if (amp > env) {
343 				env = (double)env * ga + (double)amp * (1.0f - ga);
344 			} else {
345 				env = (double)env * gr + (double)amp * (1.0f - gr);
346 			}
347 		} else
348 			env = 0;
349 #endif
350 
351 		if (count++ % 4 == 3) {
352 #ifdef DYN_CALC_FLOAT
353 			amp = rms_env_process(rms, sum / 4);
354 #else
355 			if (sum)
356 				amp = rms_env_process(rms, sum / 4);
357 			else
358 				amp = 0;
359 #endif
360 
361 #ifdef DYN_CALC_FLOAT
362 			if (isnan(amp))
363 				amp = 0.0f;
364 #endif
365 			sum = 0;
366 
367 			/* set gain_out according to the difference between
368 			   the envelope volume level (env) and the corresponding
369 			   output level (from graph) */
370 #ifdef DYN_CALC_FLOAT
371 			level = 20 * log10f(2 * env);
372 #else
373 			level = 20 * log10f(2 * (double)env / (double)F2S);
374 #endif
375 			adjust = get_table_gain(mode, level + offsgain);
376 			gain_out = db2lin(adjust);
377 
378 		}
379 		gain = gain * ef_a + gain_out * ef_ai;
380 		output[sample_index] = input[sample_index] * gain * mugain;
381         }
382         ptr->sum = sum;
383         ptr->amp = amp;
384         ptr->gain = gain;
385         ptr->gain_out = gain_out;
386         ptr->env = env;
387         ptr->count = count;
388 
389 	*(ptr->rmsenv) = LIMIT(level, -60.0f, 20.0f);
390 	*(ptr->modgain) = LIMIT(adjust, -60.0f, 20.0f);
391 }
392 
393 
394 
395 void
396 set_run_adding_gain_Dynamics(LADSPA_Handle Instance, LADSPA_Data gain) {
397 
398 	Dynamics * ptr = (Dynamics *)Instance;
399 
400 	ptr->run_adding_gain = gain;
401 }
402 
403 
404 
405 void
406 run_adding_Dynamics(LADSPA_Handle Instance,
407 		    unsigned long sample_count) {
408 
409 	Dynamics * ptr = (Dynamics *)Instance;
410 	LADSPA_Data * input = ptr->input;
411 	LADSPA_Data * output = ptr->output;
412         const float attack = LIMIT(*(ptr->attack), 4.0f, 500.0f);
413         const float release = LIMIT(*(ptr->release), 4.0f, 1000.0f);
414         const float offsgain = LIMIT(*(ptr->offsgain), -20.0f, 20.0f);
415         const float mugain = db2lin(LIMIT(*(ptr->mugain), -20.0f, 20.0f));
416 	const int mode = LIMIT(*(ptr->mode), 0, NUM_MODES-1);
417 	unsigned long sample_index;
418 
419         dyn_t amp = ptr->amp;
420         dyn_t env = ptr->env;
421         float * as = ptr->as;
422         unsigned int count = ptr->count;
423         float gain = ptr->gain;
424         float gain_out = ptr->gain_out;
cleanup_ChorusFlanger(LADSPA_Handle Instance)425         rms_env * rms = ptr->rms;
426         rms_t sum = ptr->sum;
427 
428         const float ga = as[(unsigned int)(attack * 0.001f * (float)(TABSIZE-1))];
429         const float gr = as[(unsigned int)(release * 0.001f * (float)(TABSIZE-1))];
430         const float ef_a = ga * 0.25f;
431         const float ef_ai = 1.0f - ef_a;
432 
433 	float level = 0.0f;
434 	float adjust = 0.0f;
435 
436         for (sample_index = 0; sample_index < sample_count; sample_index++) {
437 
438 #ifdef DYN_CALC_FLOAT
439 		sum += input[sample_index] * input[sample_index];
440 		if (amp > env) {
441 			env = env * ga + amp * (1.0f - ga);
_init()442 		} else {
443 			env = env * gr + amp * (1.0f - gr);
444 		}
445 #else
446 		sum += (rms_t)(input[sample_index] * F2S * input[sample_index] * F2S);
447 		if (amp) {
448 			if (amp > env) {
449 				env = (double)env * ga + (double)amp * (1.0f - ga);
450 			} else {
451 				env = (double)env * gr + (double)amp * (1.0f - gr);
452 			}
453 		} else
454 			env = 0;
455 #endif
456 
457 		if (count++ % 4 == 3) {
458 #ifdef DYN_CALC_FLOAT
459 			amp = rms_env_process(rms, sum / 4);
460 #else
461 			if (sum)
462 				amp = rms_env_process(rms, sum / 4);
463 			else
464 				amp = 0;
465 #endif
466 
467 #ifdef DYN_CALC_FLOAT
468 			if (isnan(amp))
469 				amp = 0.0f;
470 #endif
471 			sum = 0;
472 
473 			/* set gain_out according to the difference between
474 			   the envelope volume level (env) and the corresponding
475 			   output level (from graph) */
476 #ifdef DYN_CALC_FLOAT
477 			level = 20 * log10f(2 * env);
478 #else
479 			level = 20 * log10f(2 * (double)env / (double)F2S);
480 #endif
481 			adjust = get_table_gain(mode, level + offsgain);
482 			gain_out = db2lin(adjust);
483 
484 		}
485 		gain = gain * ef_a + gain_out * ef_ai;
486 		output[sample_index] += ptr->run_adding_gain * input[sample_index] * gain * mugain;
487         }
488         ptr->sum = sum;
489         ptr->amp = amp;
490         ptr->gain = gain;
491         ptr->gain_out = gain_out;
492         ptr->env = env;
493         ptr->count = count;
494 
495 	*(ptr->rmsenv) = LIMIT(level, -60.0f, 20.0f);
496 	*(ptr->modgain) = LIMIT(adjust, -60.0f, 20.0f);
497 }
498 
499 
500 
501 
502 /* Throw away a Dynamics effect instance. */
503 void
504 cleanup_Dynamics(LADSPA_Handle Instance) {
505 
506 	Dynamics * ptr = (Dynamics *)Instance;
507 
508 	free(ptr->rms);
509 	free(ptr->as);
510 	free(Instance);
511 }
512 
513 
514 
515 LADSPA_Descriptor * mono_descriptor = NULL;
516 
517 
518 
519 /* _init() is called automatically when the plugin library is first
520    loaded. */
521 void
522 _init() {
523 
524 	char ** port_names;
525 	LADSPA_PortDescriptor * port_descriptors;
526 	LADSPA_PortRangeHint * port_range_hints;
527 
528 	if ((mono_descriptor =
529 	     (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor))) == NULL)
530 		exit(1);
531 
532 	mono_descriptor->UniqueID = ID_MONO;
533 	mono_descriptor->Label = strdup("tap_dynamics_m");
534 	mono_descriptor->Properties = 0;
535 	mono_descriptor->Name = strdup("TAP Dynamics (M)");
536 	mono_descriptor->Maker = strdup("Tom Szilagyi");
537 	mono_descriptor->Copyright = strdup("GPL");
538 	mono_descriptor->PortCount = PORTCOUNT_MONO;
539 
540 	if ((port_descriptors =
541 	     (LADSPA_PortDescriptor *)calloc(PORTCOUNT_MONO, sizeof(LADSPA_PortDescriptor))) == NULL)
542 		exit(1);
543 
544 	mono_descriptor->PortDescriptors = (const LADSPA_PortDescriptor *)port_descriptors;
545 	port_descriptors[ATTACK] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
546 	port_descriptors[RELEASE] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
547 	port_descriptors[OFFSGAIN] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
548 	port_descriptors[MUGAIN] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
549 	port_descriptors[MODE] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
550 	port_descriptors[RMSENV] = LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL;
551 	port_descriptors[MODGAIN] = LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL;
552 	port_descriptors[INPUT] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
553 	port_descriptors[OUTPUT] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
554 
555 	if ((port_names =
556 	     (char **)calloc(PORTCOUNT_MONO, sizeof(char *))) == NULL)
557 		exit(1);
558 
559 	mono_descriptor->PortNames = (const char **)port_names;
560 	port_names[ATTACK] = strdup("Attack [ms]");
delete_descriptor(LADSPA_Descriptor * descriptor)561 	port_names[RELEASE] = strdup("Release [ms]");
562 	port_names[OFFSGAIN] = strdup("Offset Gain [dB]");
563 	port_names[MUGAIN] = strdup("Makeup Gain [dB]");
564 	port_names[MODE] = strdup("Function");
565 	port_names[RMSENV] = strdup("Envelope Volume [dB]");
566 	port_names[MODGAIN] = strdup("Gain Adjustment [dB]");
567 	port_names[INPUT] = strdup("Input");
568 	port_names[OUTPUT] = strdup("Output");
569 
570 	if ((port_range_hints =
571 	     ((LADSPA_PortRangeHint *)calloc(PORTCOUNT_MONO, sizeof(LADSPA_PortRangeHint)))) == NULL)
572 		exit(1);
573 
574 	mono_descriptor->PortRangeHints = (const LADSPA_PortRangeHint *)port_range_hints;
575 	port_range_hints[ATTACK].HintDescriptor =
576 		(LADSPA_HINT_BOUNDED_BELOW |
577 		 LADSPA_HINT_BOUNDED_ABOVE |
578 		 LADSPA_HINT_DEFAULT_LOW);
579 	port_range_hints[RELEASE].HintDescriptor =
_fini()580 		(LADSPA_HINT_BOUNDED_BELOW |
581 		 LADSPA_HINT_BOUNDED_ABOVE |
582 		 LADSPA_HINT_DEFAULT_MIDDLE);
583 	port_range_hints[OFFSGAIN].HintDescriptor =
584 		(LADSPA_HINT_BOUNDED_BELOW |
585 		 LADSPA_HINT_BOUNDED_ABOVE |
586 		 LADSPA_HINT_DEFAULT_0);
ladspa_descriptor(unsigned long Index)587 	port_range_hints[MUGAIN].HintDescriptor =
588 		(LADSPA_HINT_BOUNDED_BELOW |
589 		 LADSPA_HINT_BOUNDED_ABOVE |
590 		 LADSPA_HINT_DEFAULT_0);
591 	port_range_hints[RMSENV].HintDescriptor =
592 		(LADSPA_HINT_BOUNDED_BELOW |
593 		 LADSPA_HINT_BOUNDED_ABOVE |
594 		 LADSPA_HINT_DEFAULT_0);
595 	port_range_hints[MODGAIN].HintDescriptor =
596 		(LADSPA_HINT_BOUNDED_BELOW |
597 		 LADSPA_HINT_BOUNDED_ABOVE |
598 		 LADSPA_HINT_DEFAULT_0);
599 	port_range_hints[MODE].HintDescriptor =
600 		(LADSPA_HINT_BOUNDED_BELOW |
601 		 LADSPA_HINT_BOUNDED_ABOVE |
602 		 LADSPA_HINT_INTEGER |
603 		 LADSPA_HINT_DEFAULT_0);
604 	port_range_hints[ATTACK].LowerBound = 4.0f;
605 	port_range_hints[ATTACK].UpperBound = 500.0f;
606 	port_range_hints[RELEASE].LowerBound = 4.0f;
607 	port_range_hints[RELEASE].UpperBound = 1000.0f;
608 	port_range_hints[OFFSGAIN].LowerBound = -20.0f;
609 	port_range_hints[OFFSGAIN].UpperBound = 20.0f;
610 	port_range_hints[MUGAIN].LowerBound = -20.0f;
611 	port_range_hints[MUGAIN].UpperBound = 20.0f;
612 	port_range_hints[RMSENV].LowerBound = -60.0f;
613 	port_range_hints[RMSENV].UpperBound = 20.0f;
614 	port_range_hints[MODGAIN].LowerBound = -60.0f;
615 	port_range_hints[MODGAIN].UpperBound = 20.0f;
616 	port_range_hints[MODE].LowerBound = 0;
617 	port_range_hints[MODE].UpperBound = NUM_MODES - 0.9f;
618 	port_range_hints[INPUT].HintDescriptor = 0;
619 	port_range_hints[OUTPUT].HintDescriptor = 0;
620 	mono_descriptor->instantiate = instantiate_Dynamics;
621 	mono_descriptor->connect_port = connect_port_Dynamics;
622 	mono_descriptor->activate = NULL;
623 	mono_descriptor->run = run_Dynamics;
624 	mono_descriptor->run_adding = run_adding_Dynamics;
625 	mono_descriptor->set_run_adding_gain = set_run_adding_gain_Dynamics;
626 	mono_descriptor->deactivate = NULL;
627 	mono_descriptor->cleanup = cleanup_Dynamics;
628 }
629 
630 
631 void
632 delete_descriptor(LADSPA_Descriptor * descriptor) {
633 	unsigned long index;
634 	if (descriptor) {
635 		free((char *)descriptor->Label);
636 		free((char *)descriptor->Name);
637 		free((char *)descriptor->Maker);
638 		free((char *)descriptor->Copyright);
639 		free((LADSPA_PortDescriptor *)descriptor->PortDescriptors);
640 		for (index = 0; index < descriptor->PortCount; index++)
641 			free((char *)(descriptor->PortNames[index]));
642 		free((char **)descriptor->PortNames);
643 		free((LADSPA_PortRangeHint *)descriptor->PortRangeHints);
644 		free(descriptor);
645 	}
646 }
647 
648 
649 /* _fini() is called automatically when the library is unloaded. */
650 void
651 _fini() {
652 	delete_descriptor(mono_descriptor);
653 }
654 
655 
656 /* Return a descriptor of the requested plugin type. */
657 const LADSPA_Descriptor *
658 ladspa_descriptor(unsigned long Index) {
659 
660 	switch (Index) {
661 	case 0:
662 		return mono_descriptor;
663 	default:
664 		return NULL;
665 	}
666 }
667