@@ -0,0 +1,75 @@ | |||||
/* | |||||
* Carla Native Plugins | |||||
* Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||||
* | |||||
* 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 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. | |||||
* | |||||
* For a full copy of the GNU General Public License see the GPL.txt file | |||||
*/ | |||||
#include "CarlaNative.h" | |||||
// Plugin Code | |||||
#include "nekofilter/nekofilter.c" | |||||
#include "nekofilter/filter.c" | |||||
#include "nekofilter/log.c" | |||||
// ----------------------------------------------------------------------- | |||||
static const PluginDescriptor nekofilterDesc = { | |||||
.category = PLUGIN_CATEGORY_FILTER, | |||||
.hints = PLUGIN_IS_RTSAFE|PLUGIN_HAS_GUI, | |||||
.audioIns = 1, | |||||
.audioOuts = 1, | |||||
.midiIns = 0, | |||||
.midiOuts = 0, | |||||
.parameterIns = GLOBAL_PARAMETERS_COUNT + BAND_PARAMETERS_COUNT*BANDS_COUNT, | |||||
.parameterOuts = 0, | |||||
.name = "NekoFilter", | |||||
.label = "nekofilter", | |||||
.maker = "falkTX, Nedko, Fons Adriaensen", | |||||
.copyright = "GNU GPL v2+", | |||||
.instantiate = nekofilter_instantiate, | |||||
.cleanup = nekofilter_cleanup, | |||||
.get_parameter_count = nekofilter_get_parameter_count, | |||||
.get_parameter_info = nekofilter_get_parameter_info, | |||||
.get_parameter_value = nekofilter_get_parameter_value, | |||||
.get_parameter_text = NULL, | |||||
.get_midi_program_count = NULL, | |||||
.get_midi_program_info = NULL, | |||||
.set_parameter_value = nekofilter_set_parameter_value, | |||||
.set_midi_program = NULL, | |||||
.set_custom_data = NULL, | |||||
.ui_show = nekofilter_ui_show, | |||||
.ui_idle = nekofilter_ui_idle, | |||||
.ui_set_parameter_value = nekofilter_ui_set_parameter_value, | |||||
.ui_set_midi_program = NULL, | |||||
.ui_set_custom_data = NULL, | |||||
.activate = NULL, | |||||
.deactivate = NULL, | |||||
.process = nekofilter_process | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
void carla_register_native_plugin_nekofilter() | |||||
{ | |||||
carla_register_native_plugin(&nekofilterDesc); | |||||
} | |||||
// ----------------------------------------------------------------------- |
@@ -0,0 +1,350 @@ | |||||
/* -*- Mode: C ; c-basic-offset: 2 -*- */ | |||||
/* | |||||
Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name> | |||||
The DSP code is based on ladspa:1970 by Fons Adriaensen | |||||
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. | |||||
*/ | |||||
/* if NDEBUG is defined, assert checks are disabled */ | |||||
//#define NDEBUG | |||||
#include <stdbool.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <math.h> | |||||
#include <assert.h> | |||||
#include "filter.h" | |||||
static float exp2ap(float x) | |||||
{ | |||||
int i; | |||||
i = (int)(floor(x)); | |||||
x -= i; | |||||
// return ldexp(1 + x * (0.66 + 0.34 * x), i); | |||||
return ldexp(1 + x * (0.6930 + x * (0.2416 + x * (0.0517 + x * 0.0137))), i); | |||||
} | |||||
struct param_sect | |||||
{ | |||||
float f, b, g; | |||||
float s1, s2, a; | |||||
float z1, z2; | |||||
}; | |||||
void | |||||
param_sect_init( | |||||
struct param_sect * sect_ptr) | |||||
{ | |||||
sect_ptr->f = 0.25f; | |||||
sect_ptr->b = sect_ptr->g = 1.0f; | |||||
sect_ptr->a = sect_ptr->s1 = sect_ptr->s2 = sect_ptr->z1 = sect_ptr->z2 = 0.0f; | |||||
} | |||||
void | |||||
param_sect_proc( | |||||
struct param_sect * sect_ptr, | |||||
int k, | |||||
float * sig, | |||||
float f, | |||||
float b, | |||||
float g) | |||||
{ | |||||
float s1, s2, d1, d2, a, da, x, y; | |||||
bool u2 = false; | |||||
s1 = sect_ptr->s1; | |||||
s2 = sect_ptr->s2; | |||||
a = sect_ptr->a; | |||||
d1 = 0; | |||||
d2 = 0; | |||||
da = 0; | |||||
if (f != sect_ptr->f) | |||||
{ | |||||
if (f < 0.5f * sect_ptr->f) f = 0.5f * sect_ptr->f; | |||||
else if (f > 2.0f * sect_ptr->f) f = 2.0f * sect_ptr->f; | |||||
sect_ptr->f = f; | |||||
sect_ptr->s1 = -cosf(6.283185f * f); | |||||
d1 = (sect_ptr->s1 - s1) / k; | |||||
u2 = true; | |||||
} | |||||
if (g != sect_ptr->g) | |||||
{ | |||||
if (g < 0.5f * sect_ptr->g) g = 0.5f * sect_ptr->g; | |||||
else if (g > 2.0f * sect_ptr->g) g = 2.0f * sect_ptr->g; | |||||
sect_ptr->g = g; | |||||
sect_ptr->a = 0.5f * (g - 1.0f); | |||||
da = (sect_ptr->a - a) / k; | |||||
u2 = true; | |||||
} | |||||
if (b != sect_ptr->b) | |||||
{ | |||||
if (b < 0.5f * sect_ptr->b) b = 0.5f * sect_ptr->b; | |||||
else if (b > 2.0f * sect_ptr->b) b = 2.0f * sect_ptr->b; | |||||
sect_ptr->b = b; | |||||
u2 = true; | |||||
} | |||||
if (u2) | |||||
{ | |||||
b *= 7 * f / sqrtf(g); | |||||
sect_ptr->s2 = (1 - b) / (1 + b); | |||||
d2 = (sect_ptr->s2 - s2) / k; | |||||
} | |||||
while (k--) | |||||
{ | |||||
s1 += d1; | |||||
s2 += d2; | |||||
a += da; | |||||
x = *sig; | |||||
y = x - s2 * sect_ptr->z2; | |||||
*sig++ -= a * (sect_ptr->z2 + s2 * y - x); | |||||
y -= s1 * sect_ptr->z1; | |||||
sect_ptr->z2 = sect_ptr->z1 + s1 * y; | |||||
sect_ptr->z1 = y + 1e-10f; | |||||
} | |||||
} | |||||
struct filter | |||||
{ | |||||
float sample_rate; | |||||
const float * global_parameters[GLOBAL_PARAMETERS_COUNT]; | |||||
unsigned int bands_count; | |||||
const float ** band_parameters; /* [band_index * BAND_PARAMETERS_COUNT + parameter_index] */ | |||||
float gain; | |||||
int fade; | |||||
struct param_sect * sect; /* [band_index] */ | |||||
}; | |||||
bool | |||||
filter_create( | |||||
float sample_rate, | |||||
unsigned int bands_count, | |||||
filter_handle * handle_ptr) | |||||
{ | |||||
struct filter * filter_ptr; | |||||
unsigned int j; | |||||
assert(bands_count > 0); | |||||
filter_ptr = calloc(1, sizeof(struct filter)); | |||||
if (filter_ptr == NULL) | |||||
{ | |||||
goto fail; | |||||
} | |||||
filter_ptr->band_parameters = calloc(bands_count, sizeof(float *) * BAND_PARAMETERS_COUNT); | |||||
if (filter_ptr->band_parameters == NULL) | |||||
{ | |||||
goto free_filter; | |||||
} | |||||
filter_ptr->sect = malloc(sizeof(struct param_sect) * bands_count); | |||||
if (filter_ptr->sect == NULL) | |||||
{ | |||||
goto free_band_params; | |||||
} | |||||
filter_ptr->sample_rate = sample_rate; | |||||
filter_ptr->bands_count = bands_count; | |||||
filter_ptr->fade = 0; | |||||
filter_ptr->gain = 1.0; | |||||
for (j = 0; j < bands_count; j++) | |||||
{ | |||||
param_sect_init(filter_ptr->sect + j); | |||||
} | |||||
*handle_ptr = (filter_handle)filter_ptr; | |||||
return true; | |||||
free_band_params: | |||||
free(filter_ptr->band_parameters); | |||||
free_filter: | |||||
free(filter_ptr); | |||||
fail: | |||||
return false; | |||||
} | |||||
#define filter_ptr ((struct filter *)handle) | |||||
void | |||||
filter_destroy( | |||||
filter_handle handle) | |||||
{ | |||||
free(filter_ptr->sect); | |||||
free(filter_ptr->band_parameters); | |||||
free(filter_ptr); | |||||
} | |||||
void | |||||
filter_connect_global_parameter( | |||||
filter_handle handle, | |||||
unsigned int global_parameter, | |||||
const float * value_ptr) | |||||
{ | |||||
assert(global_parameter < GLOBAL_PARAMETERS_COUNT); | |||||
filter_ptr->global_parameters[global_parameter] = value_ptr; | |||||
} | |||||
void | |||||
filter_connect_band_parameter( | |||||
filter_handle handle, | |||||
unsigned int band_index, | |||||
unsigned int band_parameter, | |||||
const float * value_ptr) | |||||
{ | |||||
assert(band_index < filter_ptr->bands_count); | |||||
assert(band_parameter < BAND_PARAMETERS_COUNT); | |||||
filter_ptr->band_parameters[band_index * BAND_PARAMETERS_COUNT + band_parameter] = value_ptr; | |||||
} | |||||
void | |||||
filter_run( | |||||
filter_handle handle, | |||||
const float * input_buffer, | |||||
float * output_buffer, | |||||
unsigned long samples_count) | |||||
{ | |||||
int i, j, k; | |||||
const float * p; | |||||
float sig[48]; | |||||
float t, g, d; | |||||
float fgain; | |||||
float sfreq[filter_ptr->bands_count]; | |||||
float sband[filter_ptr->bands_count]; | |||||
float sgain[filter_ptr->bands_count]; | |||||
float bands_count; | |||||
bands_count = filter_ptr->bands_count; | |||||
fgain = exp2ap(0.1661 * *filter_ptr->global_parameters[GLOBAL_PARAMETER_GAIN]); | |||||
for (j = 0; j < bands_count; j++) | |||||
{ | |||||
t = *filter_ptr->band_parameters[BAND_PARAMETERS_COUNT * j + BAND_PARAMETER_FREQUENCY] / filter_ptr->sample_rate; | |||||
if (t < 0.0002) | |||||
{ | |||||
t = 0.0002; | |||||
} | |||||
else if (t > 0.4998) | |||||
{ | |||||
t = 0.4998; | |||||
} | |||||
sfreq[j] = t; | |||||
sband[j] = *filter_ptr->band_parameters[BAND_PARAMETERS_COUNT * j + BAND_PARAMETER_BANDWIDTH]; | |||||
if (*filter_ptr->band_parameters[BAND_PARAMETERS_COUNT * j + BAND_PARAMETER_ACTIVE] > 0.0) | |||||
{ | |||||
sgain[j] = exp2ap(0.1661 * *filter_ptr->band_parameters[BAND_PARAMETERS_COUNT * j + BAND_PARAMETER_GAIN]); | |||||
} | |||||
else | |||||
{ | |||||
sgain[j] = 1.0; | |||||
} | |||||
} | |||||
while (samples_count) | |||||
{ | |||||
k = (samples_count > 48) ? 32 : samples_count; | |||||
t = fgain; | |||||
g = filter_ptr->gain; | |||||
if (t > 1.25 * g) | |||||
{ | |||||
t = 1.25 * g; | |||||
} | |||||
else if (t < 0.80 * g) | |||||
{ | |||||
t = 0.80 * g; | |||||
} | |||||
filter_ptr->gain = t; | |||||
d = (t - g) / k; | |||||
for (i = 0; i < k; i++) | |||||
{ | |||||
g += d; | |||||
sig[i] = g * input_buffer[i]; | |||||
} | |||||
for (j = 0; j < bands_count; j++) | |||||
{ | |||||
param_sect_proc(filter_ptr->sect + j, k, sig, sfreq[j], sband[j], sgain[j]); | |||||
} | |||||
j = filter_ptr->fade; | |||||
g = j / 16.0; | |||||
p = 0; | |||||
if (*filter_ptr->global_parameters[GLOBAL_PARAMETER_ACTIVE] > 0.0) | |||||
{ | |||||
if (j == 16) | |||||
{ | |||||
p = sig; | |||||
} | |||||
else | |||||
{ | |||||
++j; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (j == 0) | |||||
{ | |||||
p = input_buffer; | |||||
} | |||||
else | |||||
{ | |||||
--j; | |||||
} | |||||
} | |||||
filter_ptr->fade = j; | |||||
if (p) | |||||
{ | |||||
memcpy(output_buffer, p, k * sizeof(float)); | |||||
} | |||||
else | |||||
{ | |||||
d = (j / 16.0 - g) / k; | |||||
for (i = 0; i < k; i++) | |||||
{ | |||||
g += d; | |||||
output_buffer[i] = g * sig[i] + (1 - g) * input_buffer[i]; | |||||
} | |||||
} | |||||
input_buffer += k; | |||||
output_buffer += k; | |||||
samples_count -= k; | |||||
} | |||||
} |
@@ -0,0 +1,65 @@ | |||||
/* -*- Mode: C ; c-basic-offset: 2 -*- */ | |||||
/* | |||||
Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name> | |||||
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. | |||||
*/ | |||||
#ifndef FILTER_H__D5DC5ADF_211A_48F6_93A5_68CD3B73D6C5__INCLUDED | |||||
#define FILTER_H__D5DC5ADF_211A_48F6_93A5_68CD3B73D6C5__INCLUDED | |||||
typedef struct {int unused; } * filter_handle; | |||||
#define GLOBAL_PARAMETER_ACTIVE 0 | |||||
#define GLOBAL_PARAMETER_GAIN 1 | |||||
#define GLOBAL_PARAMETERS_COUNT 2 | |||||
#define BAND_PARAMETER_ACTIVE 0 | |||||
#define BAND_PARAMETER_FREQUENCY 1 | |||||
#define BAND_PARAMETER_BANDWIDTH 2 | |||||
#define BAND_PARAMETER_GAIN 3 | |||||
#define BAND_PARAMETERS_COUNT 4 | |||||
bool | |||||
filter_create( | |||||
float sample_rate, | |||||
unsigned int bands_count, | |||||
filter_handle * handle_ptr); | |||||
void | |||||
filter_connect_global_parameter( | |||||
filter_handle handle, | |||||
unsigned int global_parameter, | |||||
const float * value_ptr); | |||||
void | |||||
filter_connect_band_parameter( | |||||
filter_handle handle, | |||||
unsigned int band_index, | |||||
unsigned int band_parameter, | |||||
const float * value_ptr); | |||||
void | |||||
filter_run( | |||||
filter_handle handle, | |||||
const float * input_buffer, | |||||
float * output_buffer, | |||||
unsigned long samples_count); | |||||
void | |||||
filter_destroy( | |||||
filter_handle handle); | |||||
#endif /* #ifndef FILTER_H__D5DC5ADF_211A_48F6_93A5_68CD3B73D6C5__INCLUDED */ |
@@ -0,0 +1,40 @@ | |||||
/* -*- Mode: C ; c-basic-offset: 2 -*- */ | |||||
/***************************************************************************** | |||||
* | |||||
* Copyright (C) 2006,2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name> | |||||
* | |||||
* 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; version 2 of the License | |||||
* | |||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |||||
* | |||||
*****************************************************************************/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <stdarg.h> | |||||
#include <string.h> | |||||
#include "log.h" | |||||
void nekolog(int level, const char * format, ...) | |||||
{ | |||||
va_list arglist; | |||||
va_start(arglist, format); | |||||
vprintf(format, arglist); | |||||
va_end(arglist); | |||||
return; | |||||
//unused | |||||
(void)level; | |||||
} |
@@ -0,0 +1,86 @@ | |||||
/* -*- Mode: C ; c-basic-offset: 2 -*- */ | |||||
/***************************************************************************** | |||||
* | |||||
* Copyright (C) 2006,2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name> | |||||
* | |||||
* 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; version 2 of the License | |||||
* | |||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |||||
* | |||||
*****************************************************************************/ | |||||
#ifndef LOG_H__7097F6FE_4FEE_4962_9542_60375961F567__INCLUDED | |||||
#define LOG_H__7097F6FE_4FEE_4962_9542_60375961F567__INCLUDED | |||||
void nekolog(int level, const char * format, ...); | |||||
#define LOG_LEVEL_DEBUG 0 | |||||
#define LOG_LEVEL_INFO 1 | |||||
#define LOG_LEVEL_WARNING 2 | |||||
#define LOG_LEVEL_NOTICE 3 | |||||
#define LOG_LEVEL_ERROR 4 | |||||
#define LOG_LEVEL_FATAL 5 | |||||
#define LOG_LEVEL_BLACK_HOLE 6 | |||||
#if !defined(LOG_LEVEL) | |||||
#define LOG_LEVEL LOG_LEVEL_WARNING | |||||
#endif | |||||
#if LOG_LEVEL <= LOG_LEVEL_DEBUG | |||||
# define LOG_DEBUG(format, arg...) \ | |||||
nekolog(LOG_LEVEL_DEBUG, \ | |||||
format "\n", ## arg) | |||||
#else | |||||
# define LOG_DEBUG(format, arg...) | |||||
#endif | |||||
#if LOG_LEVEL <= LOG_LEVEL_INFO | |||||
# define LOG_INFO(format, arg...) \ | |||||
nekolog(LOG_LEVEL_INFO, \ | |||||
format "\n", ## arg) | |||||
#else | |||||
# define LOG_INFO(format, arg...) | |||||
#endif | |||||
#if LOG_LEVEL <= LOG_LEVEL_WARNING | |||||
# define LOG_WARNING(format, arg...) \ | |||||
nekolog(LOG_LEVEL_WARNING, \ | |||||
format "\n", ## arg) | |||||
#else | |||||
# define LOG_WARNING(format, arg...) | |||||
#endif | |||||
#if LOG_LEVEL <= LOG_LEVEL_NOTICE | |||||
# define LOG_NOTICE(format, arg...) \ | |||||
nekolog(LOG_LEVEL_NOTICE, \ | |||||
format "\n", ## arg) | |||||
#else | |||||
# define LOG_NOTICE(format, arg...) | |||||
#endif | |||||
#if LOG_LEVEL <= LOG_LEVEL_ERROR | |||||
# define LOG_ERROR(format, arg...) \ | |||||
nekolog(LOG_LEVEL_ERROR, \ | |||||
format "\n", ## arg) | |||||
#else | |||||
# define LOG_ERROR(format, arg...) | |||||
#endif | |||||
#if LOG_LEVEL <= LOG_LEVEL_FATAL | |||||
# define LOG_FATAL(format, arg...) \ | |||||
nekolog(LOG_LEVEL_FATAL, \ | |||||
format "\n", ## arg) | |||||
#else | |||||
# define LOG_FATAL(format, arg...) | |||||
#endif | |||||
#endif /* #ifndef LOG_H__7097F6FE_4FEE_4962_9542_60375961F567__INCLUDED */ |
@@ -0,0 +1,396 @@ | |||||
/* -*- Mode: C ; c-basic-offset: 2 -*- */ | |||||
/***************************************************************************** | |||||
* | |||||
* Copyright (C) 2006,2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name> | |||||
* Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||||
* | |||||
* 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; version 2 of the License | |||||
* | |||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |||||
* | |||||
*****************************************************************************/ | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <stdio.h> | |||||
#include <assert.h> | |||||
#include <math.h> | |||||
#include <stdbool.h> | |||||
#include "CarlaNative.h" | |||||
//#include "nekofilter.h" | |||||
#include "filter.h" | |||||
#define LOG_LEVEL LOG_LEVEL_ERROR | |||||
#include "log.h" | |||||
#include "ui.c" | |||||
#define BANDS_COUNT 4 | |||||
struct nekofilter | |||||
{ | |||||
filter_handle filter; | |||||
float params_global[GLOBAL_PARAMETERS_COUNT]; | |||||
float params_bands[BAND_PARAMETERS_COUNT*BANDS_COUNT]; | |||||
HostDescriptor* host; | |||||
struct control* ui; | |||||
}; | |||||
PluginHandle | |||||
nekofilter_instantiate( | |||||
const struct _PluginDescriptor* _this_, | |||||
HostDescriptor* host) | |||||
{ | |||||
struct nekofilter * nekofilter_ptr; | |||||
unsigned int i; | |||||
LOG_DEBUG("nekofilter_create_plugin_instance() called."); | |||||
nekofilter_ptr = malloc(sizeof(struct nekofilter)); | |||||
if (nekofilter_ptr == NULL) | |||||
{ | |||||
goto fail; | |||||
} | |||||
nekofilter_ptr->host = host; | |||||
nekofilter_ptr->ui = NULL; | |||||
if (!filter_create(host->get_sample_rate(host->handle), BANDS_COUNT, &nekofilter_ptr->filter)) | |||||
{ | |||||
goto fail_destroy_filter; | |||||
} | |||||
nekofilter_ptr->params_global[GLOBAL_PARAMETER_ACTIVE] = 0.0f; | |||||
nekofilter_ptr->params_global[GLOBAL_PARAMETER_GAIN] = 0.0f; | |||||
filter_connect_global_parameter(nekofilter_ptr->filter, | |||||
GLOBAL_PARAMETER_ACTIVE, | |||||
&nekofilter_ptr->params_global[GLOBAL_PARAMETER_ACTIVE]); | |||||
filter_connect_global_parameter(nekofilter_ptr->filter, | |||||
GLOBAL_PARAMETER_GAIN, | |||||
&nekofilter_ptr->params_global[GLOBAL_PARAMETER_GAIN]); | |||||
for (i=0; i < BANDS_COUNT; i++) | |||||
{ | |||||
nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_ACTIVE] = 0.0f; | |||||
nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_FREQUENCY] = 0.0f; | |||||
nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_BANDWIDTH] = 1.0f; | |||||
nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_GAIN] = 0.0f; | |||||
switch (i) | |||||
{ | |||||
case 0: | |||||
nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_FREQUENCY] = 200.0f; | |||||
break; | |||||
case 1: | |||||
nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_FREQUENCY] = 400.0f; | |||||
break; | |||||
case 2: | |||||
nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_FREQUENCY] = 1000.0f; | |||||
break; | |||||
case 3: | |||||
nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_FREQUENCY] = 2000.0f; | |||||
break; | |||||
} | |||||
filter_connect_band_parameter(nekofilter_ptr->filter, | |||||
i, | |||||
BAND_PARAMETER_ACTIVE, | |||||
&nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_ACTIVE]); | |||||
filter_connect_band_parameter(nekofilter_ptr->filter, | |||||
i, | |||||
BAND_PARAMETER_FREQUENCY, | |||||
&nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_FREQUENCY]); | |||||
filter_connect_band_parameter(nekofilter_ptr->filter, | |||||
i, | |||||
BAND_PARAMETER_BANDWIDTH, | |||||
&nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_BANDWIDTH]); | |||||
filter_connect_band_parameter(nekofilter_ptr->filter, | |||||
i, | |||||
BAND_PARAMETER_GAIN, | |||||
&nekofilter_ptr->params_bands[i*BAND_PARAMETERS_COUNT + BAND_PARAMETER_GAIN]); | |||||
} | |||||
return (PluginHandle)nekofilter_ptr; | |||||
fail_destroy_filter: | |||||
filter_destroy(nekofilter_ptr->filter); | |||||
free(nekofilter_ptr); | |||||
fail: | |||||
return NULL; | |||||
// unused | |||||
(void)_this_; | |||||
} | |||||
#define nekofilter_ptr ((struct nekofilter *)handle) | |||||
uint32_t | |||||
nekofilter_get_parameter_count( | |||||
PluginHandle handle) | |||||
{ | |||||
return GLOBAL_PARAMETERS_COUNT + BAND_PARAMETERS_COUNT*BANDS_COUNT; | |||||
// unused | |||||
(void)handle; | |||||
} | |||||
const Parameter* | |||||
nekofilter_get_parameter_info( | |||||
PluginHandle handle, | |||||
uint32_t index) | |||||
{ | |||||
static Parameter param; | |||||
static bool first_init = true; | |||||
uint32_t band; | |||||
char strBuf[32]; | |||||
if (first_init) | |||||
{ | |||||
first_init = false; | |||||
param.name = NULL; | |||||
param.unit = NULL; | |||||
} | |||||
else | |||||
{ | |||||
if (param.name != NULL) | |||||
{ | |||||
free((void*)param.name); | |||||
param.name = NULL; | |||||
} | |||||
if (param.unit != NULL) | |||||
{ | |||||
free((void*)param.unit); | |||||
param.unit = NULL; | |||||
} | |||||
} | |||||
if (handle == NULL && index == 0xf00baa) | |||||
// internal cleanup call | |||||
return NULL; | |||||
param.hints = PARAMETER_IS_ENABLED|PARAMETER_IS_AUTOMABLE; | |||||
param.ranges.def = 0.0f; | |||||
param.ranges.min = 0.0f; | |||||
param.ranges.max = 0.0f; | |||||
param.scalePointCount = 0; | |||||
param.scalePoints = NULL; | |||||
switch (index) | |||||
{ | |||||
case GLOBAL_PARAMETER_ACTIVE: | |||||
param.hints |= PARAMETER_IS_BOOLEAN; | |||||
param.name = strdup("Active"); | |||||
param.ranges.max = 1.0f; | |||||
goto ready; | |||||
break; | |||||
case GLOBAL_PARAMETER_GAIN: | |||||
param.name = strdup("Gain"); | |||||
param.unit = strdup("dB"); | |||||
param.ranges.min = -20.0f; | |||||
param.ranges.max = 20.0f; | |||||
goto ready; | |||||
break; | |||||
} | |||||
index -= GLOBAL_PARAMETERS_COUNT; | |||||
band = index / BANDS_COUNT; | |||||
index %= BANDS_COUNT; | |||||
sprintf(strBuf, "%i:", band); | |||||
switch (index) | |||||
{ | |||||
case BAND_PARAMETER_ACTIVE: | |||||
strcat(strBuf, "Active"); | |||||
param.hints |= PARAMETER_IS_BOOLEAN; | |||||
param.name = strdup(strBuf); | |||||
param.ranges.max = 1.0f; | |||||
break; | |||||
case BAND_PARAMETER_FREQUENCY: | |||||
strcat(strBuf, "Frequency"); | |||||
param.hints |= PARAMETER_IS_LOGARITHMIC; | |||||
param.name = strdup(strBuf); | |||||
param.unit = strdup("Hz"); | |||||
switch (band) | |||||
{ | |||||
case 0: | |||||
param.ranges.min = 20.0f; | |||||
param.ranges.max = 2000.0f; | |||||
break; | |||||
case 1: | |||||
param.ranges.min = 40.0f; | |||||
param.ranges.max = 4000.0f; | |||||
break; | |||||
case 2: | |||||
param.ranges.min = 100.0f; | |||||
param.ranges.max = 10000.0f; | |||||
break; | |||||
case 3: | |||||
param.ranges.min = 200.0f; | |||||
param.ranges.max = 20000.0f; | |||||
break; | |||||
} | |||||
break; | |||||
case BAND_PARAMETER_BANDWIDTH: | |||||
strcat(strBuf, "Bandwidth"); | |||||
param.hints |= PARAMETER_IS_LOGARITHMIC; | |||||
param.name = strdup(strBuf); | |||||
param.ranges.min = 0.125f; | |||||
param.ranges.max = 8.0f; | |||||
break; | |||||
case BAND_PARAMETER_GAIN: | |||||
strcat(strBuf, "Gain"); | |||||
param.name = strdup(strBuf); | |||||
param.unit = strdup("dB"); | |||||
param.ranges.min = -20.0f; | |||||
param.ranges.max = 20.0f; | |||||
break; | |||||
} | |||||
ready: | |||||
if (param.hints & PARAMETER_IS_BOOLEAN) | |||||
{ | |||||
param.ranges.step = 1.0f; | |||||
param.ranges.stepSmall = 1.0f; | |||||
param.ranges.stepLarge = 1.0f; | |||||
} | |||||
else | |||||
{ | |||||
float range = param.ranges.max - param.ranges.min; | |||||
param.ranges.step = range/100.0f; | |||||
param.ranges.stepSmall = range/1000.0f; | |||||
param.ranges.stepLarge = range/10.0f; | |||||
} | |||||
return ¶m; | |||||
} | |||||
float | |||||
nekofilter_get_parameter_value( | |||||
PluginHandle handle, | |||||
uint32_t index) | |||||
{ | |||||
if (index < GLOBAL_PARAMETERS_COUNT) | |||||
{ | |||||
return nekofilter_ptr->params_global[index]; | |||||
} | |||||
else | |||||
{ | |||||
assert(index >= GLOBAL_PARAMETERS_COUNT); | |||||
index -= GLOBAL_PARAMETERS_COUNT; | |||||
return nekofilter_ptr->params_bands[index]; | |||||
} | |||||
} | |||||
void | |||||
nekofilter_set_parameter_value( | |||||
PluginHandle handle, | |||||
uint32_t index, | |||||
float value) | |||||
{ | |||||
if (index < GLOBAL_PARAMETERS_COUNT) | |||||
{ | |||||
nekofilter_ptr->params_global[index] = value; | |||||
} | |||||
else | |||||
{ | |||||
assert(index >= GLOBAL_PARAMETERS_COUNT); | |||||
index -= GLOBAL_PARAMETERS_COUNT; | |||||
nekofilter_ptr->params_bands[index] = value; | |||||
} | |||||
} | |||||
void | |||||
nekofilter_process( | |||||
PluginHandle handle, | |||||
float** inBuffer, | |||||
float** outBuffer, | |||||
uint32_t frames, | |||||
uint32_t midiEventCount, | |||||
const MidiEvent* midiEvents) | |||||
{ | |||||
LOG_DEBUG("nekofilter_run"); | |||||
filter_run( | |||||
nekofilter_ptr->filter, | |||||
inBuffer[0], | |||||
outBuffer[0], | |||||
frames); | |||||
return; | |||||
// unused | |||||
(void)midiEventCount; | |||||
(void)midiEvents; | |||||
} | |||||
void nekofilter_ui_show( | |||||
PluginHandle handle, | |||||
bool show) | |||||
{ | |||||
if (show) | |||||
{ | |||||
if (nekofilter_ptr->ui == NULL) | |||||
nekofilter_ptr->ui = nekoui_instantiate(nekofilter_ptr->host); | |||||
nekoui_show(nekofilter_ptr->ui); | |||||
} | |||||
else if (nekofilter_ptr->ui != NULL) | |||||
nekoui_hide(nekofilter_ptr->ui); | |||||
} | |||||
void nekofilter_ui_idle( | |||||
PluginHandle handle) | |||||
{ | |||||
if (nekofilter_ptr->ui != NULL) | |||||
nekoui_run(nekofilter_ptr->ui); | |||||
} | |||||
void nekofilter_ui_set_parameter_value( | |||||
PluginHandle handle, | |||||
uint32_t index, | |||||
float value) | |||||
{ | |||||
if (nekofilter_ptr->ui != NULL) | |||||
nekoui_set_parameter_value(nekofilter_ptr->ui, index, value); | |||||
} | |||||
void | |||||
nekofilter_cleanup( | |||||
PluginHandle handle) | |||||
{ | |||||
if (nekofilter_ptr->ui != NULL) | |||||
{ | |||||
nekoui_quit(nekofilter_ptr->ui); | |||||
nekoui_cleanup(nekofilter_ptr->ui); | |||||
} | |||||
filter_destroy(nekofilter_ptr->filter); | |||||
free(nekofilter_ptr); | |||||
// cleanup static data | |||||
nekofilter_get_parameter_info(NULL, 0xf00baa); | |||||
} | |||||
#undef nekofilter_ptr |
@@ -0,0 +1,522 @@ | |||||
/* -*- Mode: C ; c-basic-offset: 2 -*- */ | |||||
/***************************************************************************** | |||||
* | |||||
* Copyright (C) 2009 Nedko Arnaudov <nedko@arnaudov.name> | |||||
* Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||||
* | |||||
* LV2 UI bundle shared library for communicating with a DSSI UI | |||||
* | |||||
* 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., 59 Temple Place, Suite 330, Boston, | |||||
* MA 02111-1307, USA. | |||||
* | |||||
*****************************************************************************/ | |||||
#define UI_EXECUTABLE "ui.py" | |||||
#define WAIT_START_TIMEOUT 3000 /* ms */ | |||||
#define WAIT_ZOMBIE_TIMEOUT 3000 /* ms */ | |||||
#define WAIT_STEP 100 /* ms */ | |||||
//#define FORK_TIME_MEASURE | |||||
#define USE_VFORK | |||||
//#define USE_CLONE | |||||
//#define USE_CLONE2 | |||||
#if defined(USE_VFORK) | |||||
#define FORK vfork | |||||
#define FORK_STR "vfork" | |||||
#elif defined(USE_CLONE) | |||||
#define FORK_STR "clone" | |||||
#elif defined(USE_CLONE2) | |||||
#define FORK_STR "clone2" | |||||
#else | |||||
#define FORK fork | |||||
#define FORK_STR "fork" | |||||
#endif | |||||
#include <stdbool.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <sys/types.h> | |||||
#include <sys/wait.h> | |||||
#if defined(FORK_TIME_MEASURE) | |||||
# include <sys/time.h> | |||||
#endif | |||||
#include <unistd.h> | |||||
#if defined(USE_CLONE) || defined(USE_CLONE2) | |||||
# include <sched.h> | |||||
#endif | |||||
#include <fcntl.h> | |||||
#include <locale.h> | |||||
#include <errno.h> | |||||
#include "CarlaNative.h" | |||||
struct control | |||||
{ | |||||
HostDescriptor* host; | |||||
bool running; /* true if UI launched and 'exiting' not received */ | |||||
bool visible; /* true if 'show' sent */ | |||||
int send_pipe; /* the pipe end that is used for sending messages to UI */ | |||||
int recv_pipe; /* the pipe end that is used for receiving messages from UI */ | |||||
pid_t pid; | |||||
}; | |||||
static | |||||
char * | |||||
read_line( | |||||
struct control * control_ptr) | |||||
{ | |||||
ssize_t ret; | |||||
char ch; | |||||
char buf[100]; | |||||
char * ptr; | |||||
ptr = buf; | |||||
loop: | |||||
ret = read(control_ptr->recv_pipe, &ch, 1); | |||||
if (ret == 1 && ch != '\n') | |||||
{ | |||||
*ptr++ = ch; | |||||
goto loop; | |||||
} | |||||
if (ptr != buf) | |||||
{ | |||||
*ptr = 0; | |||||
//printf("recv: \"%s\"\n", buf); | |||||
return strdup(buf); | |||||
} | |||||
return NULL; | |||||
} | |||||
static | |||||
bool | |||||
wait_child( | |||||
pid_t pid) | |||||
{ | |||||
pid_t ret; | |||||
int i; | |||||
if (pid == -1) | |||||
{ | |||||
fprintf(stderr, "Can't wait for pid -1\n"); | |||||
return false; | |||||
} | |||||
for (i = 0; i < WAIT_ZOMBIE_TIMEOUT / WAIT_STEP; i++) | |||||
{ | |||||
//printf("waitpid(%d): %d\n", (int)pid, i); | |||||
ret = waitpid(pid, NULL, WNOHANG); | |||||
if (ret != 0) | |||||
{ | |||||
if (ret == pid) | |||||
{ | |||||
//printf("child zombie with pid %d was consumed.\n", (int)pid); | |||||
return true; | |||||
} | |||||
if (ret == -1) | |||||
{ | |||||
fprintf(stderr, "waitpid(%d) failed: %s\n", (int)pid, strerror(errno)); | |||||
return false; | |||||
} | |||||
fprintf(stderr, "we have waited for child pid %d to exit but we got pid %d instead\n", (int)pid, (int)ret); | |||||
return false; | |||||
} | |||||
//printf("zombie wait %d ms ...\n", WAIT_STEP); | |||||
usleep(WAIT_STEP * 1000); /* wait 100 ms */ | |||||
} | |||||
fprintf( | |||||
stderr, | |||||
"we have waited for child with pid %d to exit for %.1f seconds and we are giving up\n", | |||||
(int)pid, | |||||
(float)((float)WAIT_START_TIMEOUT / 1000)); | |||||
return false; | |||||
} | |||||
void | |||||
nekoui_run( | |||||
struct control * control_ptr) | |||||
{ | |||||
char * msg; | |||||
char * port_index_str; | |||||
char * port_value_str; | |||||
int index; | |||||
float value; | |||||
char * locale; | |||||
msg = read_line(control_ptr); | |||||
if (msg == NULL) | |||||
{ | |||||
return; | |||||
} | |||||
locale = strdup(setlocale(LC_NUMERIC, NULL)); | |||||
setlocale(LC_NUMERIC, "POSIX"); | |||||
if (!strcmp(msg, "port_value")) | |||||
{ | |||||
port_index_str = read_line(control_ptr); | |||||
port_value_str = read_line(control_ptr); | |||||
index = atoi(port_index_str); | |||||
if (sscanf(port_value_str, "%f", &value) == 1) | |||||
{ | |||||
//printf("port %d = %f\n", port, value); | |||||
control_ptr->host->ui_parameter_changed(control_ptr->host->handle, index, value); | |||||
} | |||||
else | |||||
{ | |||||
fprintf(stderr, "failed to convert \"%s\" to float\n", port_value_str); | |||||
} | |||||
free(port_index_str); | |||||
free(port_value_str); | |||||
} | |||||
else if (!strcmp(msg, "close")) | |||||
{ | |||||
control_ptr->visible = false; | |||||
control_ptr->host->ui_closed(control_ptr->host->handle); | |||||
} | |||||
else if (!strcmp(msg, "exiting")) | |||||
{ | |||||
/* for a while wait child to exit, we dont like zombie processes */ | |||||
if (!wait_child(control_ptr->pid)) | |||||
{ | |||||
fprintf(stderr, "force killing misbehaved child %d (exit)\n", (int)control_ptr->pid); | |||||
if (kill(control_ptr->pid, SIGKILL) == -1) | |||||
{ | |||||
fprintf(stderr, "kill() failed: %s (exit)\n", strerror(errno)); | |||||
} | |||||
else | |||||
{ | |||||
wait_child(control_ptr->pid); | |||||
} | |||||
} | |||||
control_ptr->running = false; | |||||
control_ptr->visible = false; | |||||
control_ptr->host->ui_closed(control_ptr->host->handle); | |||||
} | |||||
else | |||||
{ | |||||
printf("unknown message: \"%s\"\n", msg); | |||||
} | |||||
setlocale(LC_NUMERIC, locale); | |||||
free(locale); | |||||
free(msg); | |||||
} | |||||
void | |||||
nekoui_show( | |||||
struct control * control_ptr) | |||||
{ | |||||
if (control_ptr->visible) | |||||
{ | |||||
return; | |||||
} | |||||
write(control_ptr->send_pipe, "show\n", 5); | |||||
control_ptr->visible = true; | |||||
} | |||||
void | |||||
nekoui_hide( | |||||
struct control * control_ptr) | |||||
{ | |||||
if (!control_ptr->visible) | |||||
{ | |||||
return; | |||||
} | |||||
write(control_ptr->send_pipe, "hide\n", 5); | |||||
control_ptr->visible = false; | |||||
} | |||||
void | |||||
nekoui_quit( | |||||
struct control * control_ptr) | |||||
{ | |||||
write(control_ptr->send_pipe, "quit\n", 5); | |||||
control_ptr->visible = false; | |||||
} | |||||
#if defined(FORK_TIME_MEASURE) | |||||
static | |||||
uint64_t | |||||
get_current_time() | |||||
{ | |||||
struct timeval time; | |||||
if (gettimeofday(&time, NULL) != 0) | |||||
return 0; | |||||
return (uint64_t)time.tv_sec * 1000000 + (uint64_t)time.tv_usec; | |||||
} | |||||
#define FORK_TIME_MEASURE_VAR_NAME ____t | |||||
#define FORK_TIME_MEASURE_VAR uint64_t FORK_TIME_MEASURE_VAR_NAME | |||||
#define FORK_TIME_MEASURE_BEGIN FORK_TIME_MEASURE_VAR_NAME = get_current_time() | |||||
#define FORK_TIME_MEASURE_END(msg) \ | |||||
{ \ | |||||
FORK_TIME_MEASURE_VAR_NAME = get_current_time() - FORK_TIME_MEASURE_VAR_NAME; \ | |||||
fprintf(stderr, msg ": %llu us\n", (unsigned long long)FORK_TIME_MEASURE_VAR_NAME); \ | |||||
} | |||||
#else | |||||
#define FORK_TIME_MEASURE_VAR | |||||
#define FORK_TIME_MEASURE_BEGIN | |||||
#define FORK_TIME_MEASURE_END(msg) | |||||
#endif | |||||
#if defined(USE_CLONE) || defined(USE_CLONE2) | |||||
static int clone_fn(void * context) | |||||
{ | |||||
execvp(*(const char **)context, (char **)context); | |||||
return -1; | |||||
} | |||||
#endif | |||||
static | |||||
struct control* | |||||
nekoui_instantiate( | |||||
HostDescriptor* host) | |||||
{ | |||||
struct control * control_ptr; | |||||
char * filename; | |||||
int pipe1[2]; /* written by host process, read by plugin UI process */ | |||||
int pipe2[2]; /* written by plugin UI process, read by host process */ | |||||
char ui_recv_pipe[100]; | |||||
char ui_send_pipe[100]; | |||||
int oldflags; | |||||
FORK_TIME_MEASURE_VAR; | |||||
const char * argv[5]; | |||||
int ret; | |||||
int i; | |||||
char ch; | |||||
control_ptr = malloc(sizeof(struct control)); | |||||
if (control_ptr == NULL) | |||||
{ | |||||
goto fail; | |||||
} | |||||
control_ptr->host = host; | |||||
if (pipe(pipe1) != 0) | |||||
{ | |||||
fprintf(stderr, "pipe1 creation failed.\n"); | |||||
} | |||||
if (pipe(pipe2) != 0) | |||||
{ | |||||
fprintf(stderr, "pipe2 creation failed.\n"); | |||||
} | |||||
snprintf(ui_recv_pipe, sizeof(ui_recv_pipe), "%d", pipe1[0]); /* [0] means reading end */ | |||||
snprintf(ui_send_pipe, sizeof(ui_send_pipe), "%d", pipe2[1]); /* [1] means writting end */ | |||||
// FIXME | |||||
const char* bundle_path = "/home/falktx/Personal/FOSS/GIT/Carla/source/backend/native/nekofilter/"; | |||||
filename = malloc(strlen(bundle_path) + strlen(UI_EXECUTABLE) + 1); | |||||
if (filename == NULL) | |||||
{ | |||||
goto fail_free_control; | |||||
} | |||||
strcpy(filename, bundle_path); | |||||
strcat(filename, UI_EXECUTABLE); | |||||
control_ptr->running = false; | |||||
control_ptr->visible = false; | |||||
control_ptr->pid = -1; | |||||
argv[0] = "python"; | |||||
argv[1] = filename; | |||||
argv[2] = ui_recv_pipe; /* reading end */ | |||||
argv[3] = ui_send_pipe; /* writting end */ | |||||
argv[4] = NULL; | |||||
FORK_TIME_MEASURE_BEGIN; | |||||
#if defined(USE_CLONE) | |||||
{ | |||||
int stack[8000]; | |||||
ret = clone(clone_fn, stack + 4000, CLONE_VFORK, argv); | |||||
if (ret == -1) | |||||
{ | |||||
fprintf(stderr, "clone() failed: %s\n", strerror(errno)); | |||||
goto fail_free_control; | |||||
} | |||||
} | |||||
#elif defined(USE_CLONE2) | |||||
fprintf(stderr, "clone2() exec not implemented yet\n"); | |||||
goto fail_free_control; | |||||
#else | |||||
ret = FORK(); | |||||
switch (ret) | |||||
{ | |||||
case 0: /* child process */ | |||||
/* fork duplicated the handles, close pipe ends that are used by parent process */ | |||||
#if !defined(USE_VFORK) | |||||
/* it looks we cant do this for vfork() */ | |||||
close(pipe1[1]); | |||||
close(pipe2[0]); | |||||
#endif | |||||
execvp(argv[0], (char **)argv); | |||||
fprintf(stderr, "exec of UI failed: %s\n", strerror(errno)); | |||||
exit(1); | |||||
case -1: | |||||
fprintf(stderr, "fork() failed to create new process for plugin UI\n"); | |||||
goto fail_free_control; | |||||
} | |||||
#endif | |||||
FORK_TIME_MEASURE_END(FORK_STR "() time"); | |||||
//fprintf(stderr, FORK_STR "()-ed child process: %d\n", ret); | |||||
control_ptr->pid = ret; | |||||
/* fork duplicated the handles, close pipe ends that are used by the child process */ | |||||
close(pipe1[0]); | |||||
close(pipe2[1]); | |||||
control_ptr->send_pipe = pipe1[1]; /* [1] means writting end */ | |||||
control_ptr->recv_pipe = pipe2[0]; /* [0] means reading end */ | |||||
oldflags = fcntl(control_ptr->recv_pipe, F_GETFL); | |||||
fcntl(control_ptr->recv_pipe, F_SETFL, oldflags | O_NONBLOCK); | |||||
/* wait a while for child process to confirm it is alive */ | |||||
//printf("waiting UI start\n"); | |||||
i = 0; | |||||
loop: | |||||
ret = read(control_ptr->recv_pipe, &ch, 1); | |||||
switch (ret) | |||||
{ | |||||
case -1: | |||||
if (errno == EAGAIN) | |||||
{ | |||||
if (i < WAIT_START_TIMEOUT / WAIT_STEP) | |||||
{ | |||||
//printf("start wait %d ms ...\n", WAIT_STEP); | |||||
usleep(WAIT_STEP * 1000); | |||||
i++; | |||||
goto loop; | |||||
} | |||||
fprintf( | |||||
stderr, | |||||
"we have waited for child with pid %d to appear for %.1f seconds and we are giving up\n", | |||||
(int)control_ptr->pid, | |||||
(float)((float)WAIT_START_TIMEOUT / 1000)); | |||||
} | |||||
else | |||||
{ | |||||
fprintf(stderr, "read() failed: %s\n", strerror(errno)); | |||||
} | |||||
break; | |||||
case 1: | |||||
if (ch == '\n') | |||||
{ | |||||
return control_ptr; | |||||
} | |||||
fprintf(stderr, "read() wrong first char '%c'\n", ch); | |||||
break; | |||||
default: | |||||
fprintf(stderr, "read() returned %d\n", ret); | |||||
} | |||||
fprintf(stderr, "force killing misbehaved child %d (start)\n", (int)control_ptr->pid); | |||||
if (kill(control_ptr->pid, SIGKILL) == -1) | |||||
{ | |||||
fprintf(stderr, "kill() failed: %s (start)\n", strerror(errno)); | |||||
} | |||||
/* wait a while child to exit, we dont like zombie processes */ | |||||
wait_child(control_ptr->pid); | |||||
fail_free_control: | |||||
free(control_ptr); | |||||
fail: | |||||
fprintf(stderr, "lv2fil UI launch failed\n"); | |||||
return NULL; | |||||
} | |||||
void | |||||
nekoui_cleanup( | |||||
struct control * control_ptr) | |||||
{ | |||||
//printf("cleanup() called\n"); | |||||
free(control_ptr); | |||||
} | |||||
void nekoui_set_parameter_value( | |||||
struct control * control_ptr, | |||||
uint32_t index, | |||||
float value) | |||||
{ | |||||
char buf[100]; | |||||
int len; | |||||
char * locale; | |||||
//printf("port_event(%u, %f) called\n", (unsigned int)port_index, *(float *)buffer); | |||||
locale = strdup(setlocale(LC_NUMERIC, NULL)); | |||||
setlocale(LC_NUMERIC, "POSIX"); | |||||
write(control_ptr->send_pipe, "port_value\n", 11); | |||||
len = sprintf(buf, "%u\n", (unsigned int)index); | |||||
write(control_ptr->send_pipe, buf, len); | |||||
len = sprintf(buf, "%.10f\n", value); | |||||
write(control_ptr->send_pipe, buf, len); | |||||
fsync(control_ptr->send_pipe); | |||||
setlocale(LC_NUMERIC, locale); | |||||
free(locale); | |||||
} | |||||
#undef control_ptr |