| @@ -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 | |||