Files
lmms/plugins/LadspaEffect/tap/tap_echo.c
Tobias Doerffel 83c2019611 LadspaEffect/TAP: fix instantiation/destruction
Instead of relying on naming the init functions _init() and _fini()
declare them as constructors/destructors and use a different name so
there's no double destruction

Closes #668.
2014-05-01 23:33:40 +02:00

630 lines
16 KiB
C

/* -*- linux-c -*-
Copyright (C) 2004 Tom Szilagyi
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: tap_echo.c,v 1.7 2004/12/06 09:32:41 tszilagyi Exp $
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ladspa.h>
#include "tap_utils.h"
/* The Unique ID of the plugin: */
#define ID_STEREO 2143
/* The port numbers for the plugin: */
#define DELAYTIME_L 0
#define FEEDBACK_L 1
#define DELAYTIME_R 2
#define FEEDBACK_R 3
#define STRENGTH_L 4
#define STRENGTH_R 5
#define DRYLEVEL 6
#define MODE 7
#define HAAS 8
#define REV_OUTCH 9
#define INPUT_L 10
#define OUTPUT_L 11
#define INPUT_R 12
#define OUTPUT_R 13
/* Total number of ports */
#define PORTCOUNT_STEREO 14
/* Maximum delay (ms) */
#define MAX_DELAY 2000
/* The structure used to hold port connection information and state */
typedef struct {
LADSPA_Data * delaytime_L;
LADSPA_Data * delaytime_R;
LADSPA_Data * feedback_L;
LADSPA_Data * feedback_R;
LADSPA_Data * strength_L;
LADSPA_Data * strength_R;
LADSPA_Data * drylevel;
LADSPA_Data * mode;
LADSPA_Data * haas;
LADSPA_Data * rev_outch;
LADSPA_Data * input_L;
LADSPA_Data * output_L;
LADSPA_Data * input_R;
LADSPA_Data * output_R;
unsigned long sample_rate;
LADSPA_Data mpx_out_L;
LADSPA_Data mpx_out_R;
LADSPA_Data * ringbuffer_L;
LADSPA_Data * ringbuffer_R;
unsigned long * buffer_pos_L;
unsigned long * buffer_pos_R;
LADSPA_Data run_adding_gain;
} Echo;
/* Construct a new plugin instance. */
LADSPA_Handle
instantiate_Echo(const LADSPA_Descriptor * Descriptor,
unsigned long SampleRate) {
LADSPA_Handle * ptr;
if ((ptr = malloc(sizeof(Echo))) != NULL) {
((Echo *)ptr)->sample_rate = SampleRate;
((Echo *)ptr)->run_adding_gain = 1.0f;
/* allocate memory for ringbuffers and related dynamic vars */
if ((((Echo *)ptr)->ringbuffer_L =
calloc(MAX_DELAY * ((Echo *)ptr)->sample_rate / 1000,
sizeof(LADSPA_Data))) == NULL)
exit(1);
if ((((Echo *)ptr)->ringbuffer_R =
calloc(MAX_DELAY * ((Echo *)ptr)->sample_rate / 1000,
sizeof(LADSPA_Data))) == NULL)
exit(1);
if ((((Echo *)ptr)->buffer_pos_L = calloc(1, sizeof(unsigned long))) == NULL)
exit(1);
if ((((Echo *)ptr)->buffer_pos_R = calloc(1, sizeof(unsigned long))) == NULL)
exit(1);
*(((Echo *)ptr)->buffer_pos_L) = 0;
*(((Echo *)ptr)->buffer_pos_R) = 0;
return ptr;
}
return NULL;
}
/* activate a plugin instance */
void
activate_Echo(LADSPA_Handle Instance) {
Echo * ptr = (Echo *)Instance;
unsigned int i;
ptr->mpx_out_L = 0;
ptr->mpx_out_R = 0;
*(ptr->buffer_pos_L) = 0;
*(ptr->buffer_pos_R) = 0;
for (i = 0; i < MAX_DELAY * ptr->sample_rate / 1000; i++) {
ptr->ringbuffer_L[i] = 0.0f;
ptr->ringbuffer_R[i] = 0.0f;
}
}
/* Connect a port to a data location. */
void
connect_port_Echo(LADSPA_Handle Instance,
unsigned long Port,
LADSPA_Data * DataLocation) {
Echo * ptr;
ptr = (Echo *)Instance;
switch (Port) {
case DELAYTIME_L:
ptr->delaytime_L = DataLocation;
break;
case DELAYTIME_R:
ptr->delaytime_R = DataLocation;
break;
case FEEDBACK_L:
ptr->feedback_L = DataLocation;
break;
case FEEDBACK_R:
ptr->feedback_R = DataLocation;
break;
case STRENGTH_L:
ptr->strength_L = DataLocation;
break;
case STRENGTH_R:
ptr->strength_R = DataLocation;
break;
case MODE:
ptr->mode = DataLocation;
break;
case HAAS:
ptr->haas = DataLocation;
break;
case REV_OUTCH:
ptr->rev_outch = DataLocation;
break;
case DRYLEVEL:
ptr->drylevel = DataLocation;
break;
case INPUT_L:
ptr->input_L = DataLocation;
break;
case OUTPUT_L:
ptr->output_L = DataLocation;
break;
case INPUT_R:
ptr->input_R = DataLocation;
break;
case OUTPUT_R:
ptr->output_R = DataLocation;
break;
}
}
#define EPS 0.00000001f
static inline float
M(float x) {
if ((x > EPS) || (x < -EPS))
return x;
else
return 0.0f;
}
void
run_Echo(LADSPA_Handle Instance,
unsigned long SampleCount) {
Echo * ptr;
unsigned long sample_index;
LADSPA_Data delaytime_L;
LADSPA_Data delaytime_R;
LADSPA_Data feedback_L;
LADSPA_Data feedback_R;
LADSPA_Data strength_L;
LADSPA_Data strength_R;
LADSPA_Data drylevel;
LADSPA_Data mode;
LADSPA_Data haas;
LADSPA_Data rev_outch;
LADSPA_Data * input_L;
LADSPA_Data * output_L;
LADSPA_Data * input_R;
LADSPA_Data * output_R;
unsigned long sample_rate;
unsigned long buflen_L;
unsigned long buflen_R;
LADSPA_Data out_L = 0;
LADSPA_Data out_R = 0;
LADSPA_Data in_L = 0;
LADSPA_Data in_R = 0;
ptr = (Echo *)Instance;
delaytime_L = LIMIT(*(ptr->delaytime_L),0.0f,2000.0f);
delaytime_R = LIMIT(*(ptr->delaytime_R),0.0f,2000.0f);
feedback_L = LIMIT(*(ptr->feedback_L) / 100.0, 0.0f, 100.0f);
feedback_R = LIMIT(*(ptr->feedback_R) / 100.0, 0.0f, 100.0f);
strength_L = db2lin(LIMIT(*(ptr->strength_L),-70.0f,10.0f));
strength_R = db2lin(LIMIT(*(ptr->strength_R),-70.0f,10.0f));
drylevel = db2lin(LIMIT(*(ptr->drylevel),-70.0f,10.0f));
mode = LIMIT(*(ptr->mode),-2.0f,2.0f);
haas = LIMIT(*(ptr->haas),-2.0f,2.0f);
rev_outch = LIMIT(*(ptr->rev_outch),-2.0f,2.0f);
input_L = ptr->input_L;
output_L = ptr->output_L;
input_R = ptr->input_R;
output_R = ptr->output_R;
sample_rate = ptr->sample_rate;
buflen_L = delaytime_L * sample_rate / 1000;
buflen_R = delaytime_R * sample_rate / 1000;
for (sample_index = 0; sample_index < SampleCount; sample_index++) {
in_L = *(input_L++);
in_R = *(input_R++);
out_L = in_L * drylevel + ptr->mpx_out_L * strength_L;
out_R = in_R * drylevel + ptr->mpx_out_R * strength_R;
if (haas > 0.0f)
in_R = 0.0f;
if (mode <= 0.0f) {
ptr->mpx_out_L =
M(push_buffer(in_L + ptr->mpx_out_L * feedback_L,
ptr->ringbuffer_L, buflen_L, ptr->buffer_pos_L));
ptr->mpx_out_R =
M(push_buffer(in_R + ptr->mpx_out_R * feedback_R,
ptr->ringbuffer_R, buflen_R, ptr->buffer_pos_R));
} else {
ptr->mpx_out_R =
M(push_buffer(in_L + ptr->mpx_out_L * feedback_L,
ptr->ringbuffer_L, buflen_L, ptr->buffer_pos_L));
ptr->mpx_out_L =
M(push_buffer(in_R + ptr->mpx_out_R * feedback_R,
ptr->ringbuffer_R, buflen_R, ptr->buffer_pos_R));
}
if (rev_outch <= 0.0f) {
*(output_L++) = out_L;
*(output_R++) = out_R;
} else {
*(output_L++) = out_R;
*(output_R++) = out_L;
}
}
}
void
set_run_adding_gain(LADSPA_Handle Instance, LADSPA_Data gain){
Echo * ptr;
ptr = (Echo *)Instance;
ptr->run_adding_gain = gain;
}
void
run_adding_gain_Echo(LADSPA_Handle Instance,
unsigned long SampleCount) {
Echo * ptr;
unsigned long sample_index;
LADSPA_Data delaytime_L;
LADSPA_Data delaytime_R;
LADSPA_Data feedback_L;
LADSPA_Data feedback_R;
LADSPA_Data strength_L;
LADSPA_Data strength_R;
LADSPA_Data drylevel;
LADSPA_Data mode;
LADSPA_Data haas;
LADSPA_Data rev_outch;
LADSPA_Data * input_L;
LADSPA_Data * output_L;
LADSPA_Data * input_R;
LADSPA_Data * output_R;
unsigned long sample_rate;
unsigned long buflen_L;
unsigned long buflen_R;
LADSPA_Data out_L = 0;
LADSPA_Data out_R = 0;
LADSPA_Data in_L = 0;
LADSPA_Data in_R = 0;
ptr = (Echo *)Instance;
delaytime_L = LIMIT(*(ptr->delaytime_L),0.0f,2000.0f);
delaytime_R = LIMIT(*(ptr->delaytime_R),0.0f,2000.0f);
feedback_L = LIMIT(*(ptr->feedback_L) / 100.0, 0.0f, 100.0f);
feedback_R = LIMIT(*(ptr->feedback_R) / 100.0, 0.0f, 100.0f);
strength_L = db2lin(LIMIT(*(ptr->strength_L),-70.0f,10.0f));
strength_R = db2lin(LIMIT(*(ptr->strength_R),-70.0f,10.0f));
drylevel = db2lin(LIMIT(*(ptr->drylevel),-70.0f,10.0f));
mode = LIMIT(*(ptr->mode),-2.0f,2.0f);
haas = LIMIT(*(ptr->haas),-2.0f,2.0f);
rev_outch = LIMIT(*(ptr->rev_outch),-2.0f,2.0f);
input_L = ptr->input_L;
output_L = ptr->output_L;
input_R = ptr->input_R;
output_R = ptr->output_R;
sample_rate = ptr->sample_rate;
buflen_L = delaytime_L * sample_rate / 1000;
buflen_R = delaytime_R * sample_rate / 1000;
for (sample_index = 0; sample_index < SampleCount; sample_index++) {
in_L = *(input_L++);
in_R = *(input_R++);
out_L = in_L * drylevel + ptr->mpx_out_L * strength_L;
out_R = in_R * drylevel + ptr->mpx_out_R * strength_R;
if (haas > 0.0f)
in_R = 0.0f;
if (mode <= 0.0f) {
ptr->mpx_out_L =
M(push_buffer(in_L + ptr->mpx_out_L * feedback_L,
ptr->ringbuffer_L, buflen_L, ptr->buffer_pos_L));
ptr->mpx_out_R =
M(push_buffer(in_R + ptr->mpx_out_R * feedback_R,
ptr->ringbuffer_R, buflen_R, ptr->buffer_pos_R));
} else {
ptr->mpx_out_R =
M(push_buffer(in_L + ptr->mpx_out_L * feedback_L,
ptr->ringbuffer_L, buflen_L, ptr->buffer_pos_L));
ptr->mpx_out_L =
M(push_buffer(in_R + ptr->mpx_out_R * feedback_R,
ptr->ringbuffer_R, buflen_R, ptr->buffer_pos_R));
}
if (rev_outch <= 0.0f) {
*(output_L++) += out_L * ptr->run_adding_gain;
*(output_R++) += out_R * ptr->run_adding_gain;
} else {
*(output_L++) += out_R * ptr->run_adding_gain;
*(output_R++) += out_L * ptr->run_adding_gain;
}
}
}
/* Throw away an Echo effect instance. */
void
cleanup_Echo(LADSPA_Handle Instance) {
Echo * ptr = (Echo *)Instance;
free(ptr->ringbuffer_L);
free(ptr->ringbuffer_R);
free(ptr->buffer_pos_L);
free(ptr->buffer_pos_R);
free(Instance);
}
LADSPA_Descriptor * stereo_descriptor = NULL;
/* __attribute__((constructor)) tap_init() is called automatically when the plugin library is first
loaded. */
void
__attribute__((constructor)) tap_init() {
char ** port_names;
LADSPA_PortDescriptor * port_descriptors;
LADSPA_PortRangeHint * port_range_hints;
if ((stereo_descriptor =
(LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor))) == NULL)
exit(1);
/* init the stereo Echo */
stereo_descriptor->UniqueID = ID_STEREO;
stereo_descriptor->Label = strdup("tap_stereo_echo");
stereo_descriptor->Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
stereo_descriptor->Name = strdup("TAP Stereo Echo");
stereo_descriptor->Maker = strdup("Tom Szilagyi");
stereo_descriptor->Copyright = strdup("GPL");
stereo_descriptor->PortCount = PORTCOUNT_STEREO;
if ((port_descriptors =
(LADSPA_PortDescriptor *)calloc(PORTCOUNT_STEREO, sizeof(LADSPA_PortDescriptor))) == NULL)
exit(1);
stereo_descriptor->PortDescriptors = (const LADSPA_PortDescriptor *)port_descriptors;
port_descriptors[DELAYTIME_L] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[DELAYTIME_R] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[FEEDBACK_L] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[FEEDBACK_R] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[STRENGTH_L] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[STRENGTH_R] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[DRYLEVEL] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[MODE] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[HAAS] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[REV_OUTCH] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
port_descriptors[INPUT_L] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
port_descriptors[OUTPUT_L] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
port_descriptors[INPUT_R] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
port_descriptors[OUTPUT_R] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
if ((port_names =
(char **)calloc(PORTCOUNT_STEREO, sizeof(char *))) == NULL)
exit(1);
stereo_descriptor->PortNames = (const char **)port_names;
port_names[DELAYTIME_L] = strdup("L Delay [ms]");
port_names[DELAYTIME_R] = strdup("R/Haas Delay [ms]");
port_names[FEEDBACK_L] = strdup("L Feedback [%]");
port_names[FEEDBACK_R] = strdup("R/Haas Feedback [%]");
port_names[STRENGTH_L] = strdup("L Echo Level [dB]");
port_names[STRENGTH_R] = strdup("R Echo Level [dB]");
port_names[DRYLEVEL] = strdup("Dry Level [dB]");
port_names[MODE] = strdup("Cross Mode");
port_names[HAAS] = strdup("Haas Effect");
port_names[REV_OUTCH] = strdup("Swap Outputs");
port_names[INPUT_L] = strdup("Input Left");
port_names[OUTPUT_L] = strdup("Output Left");
port_names[INPUT_R] = strdup("Input Right");
port_names[OUTPUT_R] = strdup("Output Right");
if ((port_range_hints =
((LADSPA_PortRangeHint *)calloc(PORTCOUNT_STEREO, sizeof(LADSPA_PortRangeHint)))) == NULL)
exit(1);
stereo_descriptor->PortRangeHints = (const LADSPA_PortRangeHint *)port_range_hints;
port_range_hints[DELAYTIME_L].HintDescriptor =
(LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_DEFAULT_100);
port_range_hints[DELAYTIME_L].LowerBound = 0;
port_range_hints[DELAYTIME_L].UpperBound = MAX_DELAY;
port_range_hints[DELAYTIME_R].HintDescriptor =
(LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_DEFAULT_100);
port_range_hints[DELAYTIME_R].LowerBound = 0;
port_range_hints[DELAYTIME_R].UpperBound = MAX_DELAY;
port_range_hints[FEEDBACK_L].HintDescriptor =
(LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_DEFAULT_0);
port_range_hints[FEEDBACK_L].LowerBound = 0;
port_range_hints[FEEDBACK_L].UpperBound = 100;
port_range_hints[FEEDBACK_R].HintDescriptor =
(LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_DEFAULT_0);
port_range_hints[FEEDBACK_R].LowerBound = 0;
port_range_hints[FEEDBACK_R].UpperBound = 100;
port_range_hints[STRENGTH_L].HintDescriptor =
(LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_DEFAULT_0);
port_range_hints[STRENGTH_L].LowerBound = -70;
port_range_hints[STRENGTH_L].UpperBound = 10;
port_range_hints[STRENGTH_R].HintDescriptor =
(LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_DEFAULT_0);
port_range_hints[STRENGTH_R].LowerBound = -70;
port_range_hints[STRENGTH_R].UpperBound = 10;
port_range_hints[MODE].HintDescriptor =
(LADSPA_HINT_TOGGLED |
LADSPA_HINT_DEFAULT_0);
port_range_hints[HAAS].HintDescriptor =
(LADSPA_HINT_TOGGLED |
LADSPA_HINT_DEFAULT_0);
port_range_hints[REV_OUTCH].HintDescriptor =
(LADSPA_HINT_TOGGLED |
LADSPA_HINT_DEFAULT_0);
port_range_hints[DRYLEVEL].HintDescriptor =
(LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_DEFAULT_0);
port_range_hints[DRYLEVEL].LowerBound = -70;
port_range_hints[DRYLEVEL].UpperBound = 10;
port_range_hints[INPUT_L].HintDescriptor = 0;
port_range_hints[OUTPUT_L].HintDescriptor = 0;
port_range_hints[INPUT_R].HintDescriptor = 0;
port_range_hints[OUTPUT_R].HintDescriptor = 0;
stereo_descriptor->instantiate = instantiate_Echo;
stereo_descriptor->connect_port = connect_port_Echo;
stereo_descriptor->activate = activate_Echo;
stereo_descriptor->run = run_Echo;
stereo_descriptor->run_adding = run_adding_gain_Echo;
stereo_descriptor->set_run_adding_gain = set_run_adding_gain;
stereo_descriptor->deactivate = NULL;
stereo_descriptor->cleanup = cleanup_Echo;
}
void
delete_descriptor(LADSPA_Descriptor * descriptor) {
unsigned long index;
if (descriptor) {
free((char *)descriptor->Label);
free((char *)descriptor->Name);
free((char *)descriptor->Maker);
free((char *)descriptor->Copyright);
free((LADSPA_PortDescriptor *)descriptor->PortDescriptors);
for (index = 0; index < descriptor->PortCount; index++)
free((char *)(descriptor->PortNames[index]));
free((char **)descriptor->PortNames);
free((LADSPA_PortRangeHint *)descriptor->PortRangeHints);
free(descriptor);
}
}
/* __attribute__((destructor)) tap_fini() is called automatically when the library is unloaded. */
void
__attribute__((destructor)) tap_fini() {
delete_descriptor(stereo_descriptor);
}
/* Return a descriptor of the requested plugin type. */
const
LADSPA_Descriptor *
ladspa_descriptor(unsigned long Index) {
switch (Index) {
case 0:
return stereo_descriptor;
default:
return NULL;
}
}