Browse Source

Add NekoFilter plugin (testing)

tags/1.9.4
falkTX 11 years ago
parent
commit
24cf1cf268
8 changed files with 2770 additions and 0 deletions
  1. +75
    -0
      source/backend/native/nekofilter.c
  2. +350
    -0
      source/backend/native/nekofilter/filter.c
  3. +65
    -0
      source/backend/native/nekofilter/filter.h
  4. +40
    -0
      source/backend/native/nekofilter/log.c
  5. +86
    -0
      source/backend/native/nekofilter/log.h
  6. +396
    -0
      source/backend/native/nekofilter/nekofilter.c
  7. +522
    -0
      source/backend/native/nekofilter/ui.c
  8. +1236
    -0
      source/backend/native/nekofilter/ui.py

+ 75
- 0
source/backend/native/nekofilter.c View File

@@ -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);
}

// -----------------------------------------------------------------------

+ 350
- 0
source/backend/native/nekofilter/filter.c View File

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

+ 65
- 0
source/backend/native/nekofilter/filter.h View File

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

+ 40
- 0
source/backend/native/nekofilter/log.c View File

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

+ 86
- 0
source/backend/native/nekofilter/log.h View File

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

+ 396
- 0
source/backend/native/nekofilter/nekofilter.c View File

@@ -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 &param;
}

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

+ 522
- 0
source/backend/native/nekofilter/ui.c View File

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

+ 1236
- 0
source/backend/native/nekofilter/ui.py
File diff suppressed because it is too large
View File


Loading…
Cancel
Save