Skip to content

Commit

Permalink
Initial swh vocoder
Browse files Browse the repository at this point in the history
Code is untested

Still need inputs and outputs mapped
  • Loading branch information
tresf committed Oct 20, 2015
1 parent a44f8d7 commit ed8f9fe
Showing 1 changed file with 214 additions and 0 deletions.
214 changes: 214 additions & 0 deletions vocoder_1337.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
<?xml version="1.0"?>
<!DOCTYPE ladspa SYSTEM "ladspa-swh.dtd">
<?xml-stylesheet href="ladspa.css" type="text/css"?>

<ladspa>
<global>
<meta name="maker" value="Achim Settelmeier &lt;settel-linux@sirlab.de&gt;"/>
<meta name="copyright" value="GPL"/>
<code><![CDATA[
#include <string.h>
#include "config.h"
#include "util/iir.h"
#define MAX_BANDS 16 /* max 16 bands should be increased */
#define AMPLIFIER 16.0
struct bandpasses
{
LADSPA_Data c[MAX_BANDS], f[MAX_BANDS], att[MAX_BANDS];
LADSPA_Data freq[MAX_BANDS];
LADSPA_Data low1[MAX_BANDS], low2[MAX_BANDS];
LADSPA_Data mid1[MAX_BANDS], mid2[MAX_BANDS];
LADSPA_Data high1[MAX_BANDS], high2[MAX_BANDS];
LADSPA_Data y[MAX_BANDS];
};
struct bands_out{
LADSPA_Data decay[MAX_BANDS];
LADSPA_Data oldval[MAX_BANDS];
LADSPA_Data level[MAX_BANDS]; /* 0.0 - 1.0 level of this output band */
};
const LADSPA_Data decay_table[] =
{
1/100.0,
1/100.0, 1/100.0, 1/100.0,
1/125.0, 1/125.0, 1/125.0,
1/166.0, 1/166.0, 1/166.0,
1/200.0, 1/200.0, 1/200.0,
1/250.0, 1/250.0, 1/250.0
};
/* The port numbers for the plugin: */
#define PORT_FORMANT 0 /* the track to "vocodify */
#define PORT_CARRIER 1 /* the track to control 1st track */
#define PORT_OUTPUT 2 /* left output */
#define PORT_OUTPUT2 3 /* right output */
#define CTRL_BANDCOUNT 4 /* selected # of bands to use */
#define CTRL_PAN 5 /* stereo balance for outputs */
#define CTRL_BAND1LVL 6 /* start of bands level */
#define PORT_COUNT 6 + MAX_BANDS /* bands level */
// vocoder_do_bandpasses /*fold00*/
void inline vocoder_do_bandpasses(struct bandpasses *bands, LADSPA_Data sample)
{
int i;
for (i=0; i < vocoder->num_bands; i++)
{
bands->high1[i] = sample - bands->f[i] * bands->mid1[i] - bands->low1[i];
bands->mid1[i] += bands->high1[i] * bands->c[i];
bands->low1[i] += bands->mid1[i];
bands->high2[i] = bands->low1[i] - bands->f[i] * bands->mid2[i] - bands->low2[i];
bands->mid2[i] += bands->high2[i] * bands->c[i];
bands->low2[i] += bands->mid2[i];
bands->y[i] = bands->high2[i] * bands->att[i];
}
}
]]></code>
</global>

<plugin label="vocoder" id="1337" class="DistortionPlugin,GeneratorPlugin">
<name>Vocoder</name>
<p><![CDATA[
Makes a human voice sound synthetic; often used to speak like a robot, with a metallic and monotonous voice. Using a human voice as the formant and an instrument as the carrier, creates an effect of an instrument speaking.
]]></p>

<callback event="instantiate"><![CDATA[
ctrlBandLevels = malloc(MAX_BANDS);
num_bands = -1;
]]></callback>

<callback event="activate"><![CDATA[
int i;
mainvol = 1.0 * AMPLIFIER;
for (i = 0; i < MAX_BANDS; i++)
bands_out.oldval[i] = 0.0f;
]]></callback>

<callback event="cleanup" />

<callback event="run"><![CDATA[
int i, j, numbands, pan;
float a;
LADSPA_Data x, c;
float fl, fr;
numbands = (int)(*plugin_data->ctrlBandCount);
if (numbands < 1 || numbands > MAX_BANDS) numbands = MAX_BANDS;
/* initialize bandpass information if num_bands control has changed,
or on first run */
if (plugin_data->num_bands != numbands)
{
plugin_data->num_bands = numbands;
memset(&plugin_data->bands_formant, 0, sizeof(struct bandpasses));
for(i=0; i < numbands; i++)
{
a = 16.0 * i/(double)numbands; // stretch existing bands
if (a < 4.0)
plugin_data->bands_formant.freq[i] = 150 + 420 * a / 4.0;
else
plugin_data->bands_formant.freq[i] = 600 * pow (1.23, a - 4.0);
c = plugin_data->bands_formant.freq[i] * 2 * M_PI / plugin_data->SampleRate;
plugin_data->bands_formant.c[i] = c * c;
plugin_data->bands_formant.f[i] = 0.4/c;
plugin_data->bands_formant.att[i] =
1/(6.0 + ((exp (plugin_data->bands_formant.freq[i]
/ plugin_data->SampleRate) - 1) * 10));
plugin_data->bands_out.decay[i] = decay_table[(int)a];
plugin_data->bands_out.level[i] =
CLAMP (*plugin_data->ctrlBandLevels[i], 0.0, 1.0);
}
memcpy(&plugin_data->bands_carrier,
&plugin_data->bands_formant, sizeof(struct bandpasses));
}
else /* get current values of band level controls */
{
for (i = 0; i < numbands; i++)
plugin_data->bands_out.level[i] = CLAMP (*plugin_data->ctrlBandLevels[i],
0.0, 1.0);
}
for (i=0; i < SampleCount; i++)
{
vocoder_do_bandpasses (&(plugin_data->bands_carrier),
plugin_data->portCarrier[i]);
vocoder_do_bandpasses (&(plugin_data->bands_formant),
plugin_data->portFormant[i]);
LADSPA_Data sample = 0.0;
for (j=0; j < numbands; j++)
{
plugin_data->bands_out.oldval[j] = plugin_data->bands_out.oldval[j]
+ (fabs (plugin_data->bands_formant.y[j])
- plugin_data->bands_out.oldval[j])
* plugin_data->bands_out.decay[j];
x = plugin_data->bands_carrier.y[j] * plugin_data->bands_out.oldval[j];
sample += x * plugin_data->bands_out.level[j];
}
/* treat paning + main volume */
pan = (int)(*plugin_data->ctrlPan);
fl = fr = 1.0f;
if (pan != 0) { /* no paning, don't compute useless values */
if (pan > 0) { /* reduce left */
fl = (100.-pan)/100.;
} else {
fr = (100.+pan)/100.;
}
}
/* apply volume and paning */
plugin_data->portOutput[i] = sample * plugin_data->mainvol * fl;
plugin_data->portOutput2[i] = sample * plugin_data->mainvol * fr;
}
]]></callback>

<port label="lorem-in" dir="input" type="audio">
<!-- TODO: ADD INPUTS -->
<name>LoremIn</name>
</port>

<port label="lorem-out" dir="output" type="control">
<!-- TODO: ADD OUTPUTS -->
<name>LoremOut</name>
</port>

<instance-data label="SampleRate" type="LADSPA_Data" />
<!-- Current number of bands -->
<instance-data label="num_bands" type="int" />
<!-- Main volume -->
<instance-data label="mainvol" type="float" />
<!-- All bands in one struct now -->
<instance-data label="bands_formant" type="bandpasses" />
<!-- All bands in one struct now -->
<instance-data label="bands_carrier" type="bandpasses" />
<!-- All bands in one struct now -->
<instance-data label="bands_out" type="bands_out" />
<!-- Formant signal port data location -->
<instance-data label="portFormant" type="LADSPA_Data *" />
<!-- Carrier signal port data location -->
<instance-data label="portCarrier" type="LADSPA_Data *" />
<!-- Output audio port data location -->
<instance-data label="portOutput" type="LADSPA_Data *" />
<!-- Output audio port data location (copy of previous one) -->
<instance-data label="portOutput2" type="LADSPA_Data *" />
<!-- PAN for output -->
<instance-data label="ctrlPan" type="LADSPA_Data *" />
<!-- Band count control -->
<instance-data label="ctrlBandCount" type="LADSPA_Data *" />
<!-- Level controls for each band -->
<instance-data label="ctrlBandLevels" type="LADSPA_Data *" />
</plugin>

8 comments on commit ed8f9fe

@tresf
Copy link
Owner Author

@tresf tresf commented on ed8f9fe Oct 20, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@swh I know you don't actively code these, but any feedback here is appreciated.

I've attempted to get the c source code and convert it to use the xml building technique.

I haven't even begun to try compile and the input/outputs haven't been mapped yet, but hopefully it is a good start.

@swh
Copy link

@swh swh commented on ed8f9fe Oct 21, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the C code doesn't do anything too funky it should be straight forward.

Try looking at a trivial plugin's XML source and .c output, the mapping should be quite clear.

For more sophisticated things (like instantiation time state and so on) it gets a little more complex, but not much.

@swh
Copy link

@swh swh commented on ed8f9fe Oct 21, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see, sorry, you've already got most of the way there.

I don't think I've got a compiler setup to try it, what happens when you try to build?

It's such a long time since I worked on this codebase I can't really make any recommendations by eye.

@tresf
Copy link
Owner Author

@tresf tresf commented on ed8f9fe Oct 21, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@swh I haven't tried yet, I will try to build once I get the rest of the components mapped.

I'm not a C developer, so that makes this a bit tough on me, but I think I can get most of the code converted despite my little knowledge...

  • The original code instantiated a struct of VocoderInstance in some of the functions. I'm converting based on the assumption that I can switch the VocoderInstance * vocoder to using plugin_data, since the struct declaration appears to be handled well by your <instance-data ... /> tags.
  • One of the data components -- ctrlBandLevels -- is an array, and I couldn't determine a way to supply the dimensions to the <instance-data ../> tag, so instead I chose to use malloc in the instantiate callback. I'm not sure if this is the right technique.
  • I'm a bit confused about the lifecycle of the plugin objects in general and I should probably read up on the LADSPA specification because of the instantiate vs. activate vs. run blocks. Luckily the vocoder already has these defined, so I'm doing almost a line-for line conversion, converting the object names when needed.
  • I have yet to study the port audio/control input/output code. My current technique is to read the XML-code from your repo and comparing that to the C-code from the LMMS repo and trying to infer how the code is generated and what all is needed for each callback. My lack of C experience makes this a bit tougher, so I might need some assistance when I'm getting closer to compilation. 👍

@swh
Copy link

@swh swh commented on ed8f9fe Oct 21, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like you're on the right track, malloc-ing was the right thing to do.

I trust you know about valgrind? That might be essential if it explodes!

@tresf
Copy link
Owner Author

@tresf tresf commented on ed8f9fe Oct 21, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I trust you know about valgrind? That might be essential if it explodes!

I've read about it a lot in the bug reports I follow, but this plugin will be my first time actually using it. Thanks!!

@tresf
Copy link
Owner Author

@tresf tresf commented on ed8f9fe Oct 21, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@swh So one thing that seems a bit unique about this plugin is that it generates more input controls based on a for-loop.

https://github.com/LMMS/lmms/blob/master/plugins/LadspaEffect/swh/vocoder_1337.c#L398

Do you remember if there's a way to do this via the XML template? Should I instead do this manually in the initialize area? Or since this plugin has remained mostly untouched for a long time, should I just hard-code in the 15 <port .../> tags by hand? I fear if I use this technique I'll have hard time with this block: https://github.com/LMMS/lmms/blob/master/plugins/LadspaEffect/swh/vocoder_1337.c#L240

@swh
Copy link

@swh swh commented on ed8f9fe Oct 21, 2015 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.