Browse Source

use a worker to calculate new values

cache_saws
Alexandros Theodotou 3 years ago
parent
commit
77f72ce2fb
Signed by: alex
GPG Key ID: 022EAE42313D70F3
  1. 3
      .gitignore
  2. 3
      ext/Soundpipe/meson.build
  3. 2
      meson.build
  4. 29
      plugins/meson.build
  5. 36
      plugins/saw/common.h
  6. 2
      plugins/saw/manifest.ttl.in
  7. 459
      plugins/saw/saw.c
  8. 9
      plugins/saw/ttl_gen.c
  9. 22
      scripts/lv2lint_wrap.sh

3
.gitignore vendored

@ -90,3 +90,6 @@ callgrind.* @@ -90,3 +90,6 @@ callgrind.*
# subprojects
subprojects/**/*
!subprojects/*.wrap
# gprof
results

3
ext/Soundpipe/meson.build

@ -37,4 +37,7 @@ soundpipe_lib = static_library ( @@ -37,4 +37,7 @@ soundpipe_lib = static_library (
dependency('sndfile'),
cc.find_library('m'),
],
c_args: [
'-fvisibility=hidden',
],
)

2
meson.build

@ -19,7 +19,7 @@ project ( @@ -19,7 +19,7 @@ project (
'ZPlugins', ['c'],
version: '0.0.1',
license: 'AGPLv3+',
meson_version: '>= 0.43.0',
meson_version: '>= 0.46.0',
default_options: [
'warning_level=2',
'buildtype=debug',

29
plugins/meson.build

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
# name, version
plugins = [
['SuperSaw', '0.0.1'],
['Saw', '0.0.1'],
]
foreach pl : plugins
@ -105,18 +105,22 @@ foreach pl : plugins @@ -105,18 +105,22 @@ foreach pl : plugins
link_with: soundpipe_lib,
install: true,
install_dir: pl_dir,
c_args: common_cflags,
c_args: [
common_cflags,
'-DPLUGIN_CONFIG="../' + pl_lowercase + '_config.h"',
],
)
# create and install manifest ttl
manifest_ttl = configure_file (
input: join_paths (pl_lowercase, 'manifest.ttl.in'),
output: 'manifest.ttl',
output: pl_str + '_manifest.ttl',
configuration: pl_cdata,
)
install_data (
manifest_ttl,
install_dir: pl_dir,
rename: 'manifest.ttl',
)
# create and install ttl
@ -128,7 +132,10 @@ foreach pl : plugins @@ -128,7 +132,10 @@ foreach pl : plugins
],
include_directories: pl_inc_dirs,
dependencies: lv2_dep,
c_args: common_cflags,
c_args: [
common_cflags,
'-DPLUGIN_CONFIG="../' + pl_lowercase + '_config.h"',
],
install: false,
)
pl_ttl = custom_target (
@ -148,13 +155,17 @@ foreach pl : plugins @@ -148,13 +155,17 @@ foreach pl : plugins
'lv2_validate', required: false)
sord_validate = find_program (
'sord_validate', required: false)
if (lv2lint.found())
if lv2lint.found() and (os_linux or os_freebsd)
lv2lint_wrap = find_program (
join_paths ('..', 'scripts', 'lv2lint_wrap.sh'))
test (
'LV2 lint', lv2lint,
env: ['LV2_PATH=' + pl_build_dir + '/'],
'LV2 lint', lv2lint_wrap,
args: [
'-I', pl_build_dir + '/',
pl_cdata.get ('PLUGIN_URI')])
lv2lint.path(),
pl_build_dir,
pl_str,
pl_cdata.get('PLUGIN_URI'),
])
endif
if lv2_validate.found() and sord_validate.found()
test (

36
plugins/supersaw/common.h → plugins/saw/common.h

@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
#ifndef __Z_SUPERSAW_COMMON_H__
#define __Z_SUPERSAW_COMMON_H__
#include "../supersaw_config.h"
#include PLUGIN_CONFIG
#include <string.h>
@ -34,11 +34,13 @@ @@ -34,11 +34,13 @@
#include "lv2/atom/forge.h"
#include "lv2/core/lv2.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include "lv2/time/time.h"
#include "lv2/worker/worker.h"
typedef struct SuperSawUris
typedef struct SawUris
{
LV2_URID atom_eventTransfer;
LV2_URID atom_Blank;
@ -62,8 +64,10 @@ typedef struct SuperSawUris @@ -62,8 +64,10 @@ typedef struct SuperSawUris
LV2_URID time_speed;
/* custom URIs for communication */
LV2_URID saw_calcValues;
LV2_URID saw_freeValues;
} SuperSawUris;
} SawUris;
typedef enum PortIndex
{
@ -103,29 +107,35 @@ typedef enum PortIndex @@ -103,29 +107,35 @@ typedef enum PortIndex
* Group of variables needed by both the DSP and
* the UI.
*/
typedef struct SuperSawCommon
typedef struct SawCommon
{
/** Log feature. */
LV2_Log_Log * log;
LV2_Log_Log * log;
/** Map feature. */
LV2_URID_Map * map;
LV2_URID_Map * map;
/** Logger convenience API. */
LV2_Log_Logger logger;
/** Worker schedule feature. */
LV2_Worker_Schedule* schedule;
/** Atom forge. */
LV2_Atom_Forge forge;
LV2_Atom_Forge forge;
/** URIs. */
SuperSawUris uris;
SawUris uris;
/** Plugin samplerate. */
double samplerate;
double samplerate;
} SuperSawCommon;
} SawCommon;
static inline void
map_uris (
LV2_URID_Map* map,
SuperSawUris* uris)
SawUris* uris)
{
#define MAP(x,uri) \
uris->x = map->map (map->handle, uri)
@ -154,6 +164,8 @@ map_uris ( @@ -154,6 +164,8 @@ map_uris (
MAP (time_speed, LV2_TIME__speed);
/* custom URIs */
MAP (saw_freeValues, PLUGIN_URI "#freeValues");
MAP (saw_calcValues, PLUGIN_URI "#calcValues");
}
/**
@ -162,7 +174,7 @@ map_uris ( @@ -162,7 +174,7 @@ map_uris (
static inline void
log_error (
LV2_Log_Log * log,
SuperSawUris * uris,
SawUris * uris,
const char * _fmt,
...)
{

2
plugins/supersaw/manifest.ttl.in → plugins/saw/manifest.ttl.in

@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
<@PLUGIN_URI@>
a lv2:Plugin,
lv2:OscillatorPlugin ;
lv2:InstrumentPlugin ;
lv2:binary <@PLUGIN_DSP_BINARY@> ;
lv2:minorVersion 0;
lv2:microVersion 1;

459
plugins/supersaw/supersaw.c → plugins/saw/saw.c

@ -1,20 +1,20 @@ @@ -1,20 +1,20 @@
/*
* Copyright (C) 2019-2020 Alexandros Theodotou <alex at zrythm dot org>
*
* This file is part of ZSuperSaw
* This file is part of ZSaw
*
* ZSuperSaw is free software: you can redistribute it and/or modify
* ZSaw is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* ZSuperSaw is distributed in the hope that it will be useful,
* ZSaw 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU General Affero Public License
* along with ZSuperSaw. If not, see <https://www.gnu.org/licenses/>.
* along with ZSaw. If not, see <https://www.gnu.org/licenses/>.
*/
#include <float.h>
@ -53,13 +53,33 @@ typedef struct MidiKey @@ -53,13 +53,33 @@ typedef struct MidiKey
} MidiKey;
/**
* One voice per active key.
* Worker data.
*/
/*typedef struct Voice*/
/*{*/
/*} Voice;*/
typedef struct SawValues
{
float attack;
float decay;
float sustain;
float release;
float saturator_drive;
float saturator_dcoffset;
float distortion_shape1;
float distortion_shape2;
float reverb_mix;
float keyfreqs[128][7];
} SawValues;
/**
* Atom message.
*/
typedef struct SawValuesMessage
{
LV2_Atom atom;
SawValues * values;
} SawValuesMessage;
typedef struct SuperSaw
typedef struct Saw
{
/** Plugin ports. */
const LV2_Atom_Sequence* control;
@ -73,11 +93,11 @@ typedef struct SuperSaw @@ -73,11 +93,11 @@ typedef struct SuperSaw
/** Events in the queue. */
MidiKey keys[128];
/** Current values based on \ref SuperSaw.amount. */
float attack;
float decay;
float sustain;
float release;
/** Current values based on \ref Saw.amount. */
/*float attack;*/
/*float decay;*/
/*float sustain;*/
/*float release;*/
/*int num_voices;*/
sp_saturator * saturator;
@ -85,11 +105,170 @@ typedef struct SuperSaw @@ -85,11 +105,170 @@ typedef struct SuperSaw
sp_zitarev * reverb;
sp_data * sp;
SuperSawCommon common;
SawCommon common;
SawValues * values;
/* cache */
float last_amount;
} SuperSaw;
} Saw;
/**
* To be called by the worker function.
*/
static SawValues *
calc_values (
Saw * self)
{
lv2_log_note (
&self->common.logger, "calculating values\n");
SawValues * values = calloc (1, sizeof (SawValues));
values->attack = 0.02f;
values->decay = 0.04f + *self->amount * 0.4f;
values->sustain = 0.5f;
values->release = 0.04f + *self->amount * 0.4f;
values->saturator_drive = *self->amount * 0.3f;
values->saturator_dcoffset = *self->amount * 0.3f;
values->distortion_shape1 = *self->amount * 0.2f;
values->distortion_shape2 = *self->amount * 0.2f;
values->reverb_mix = *self->amount * 0.5f;
float freq_delta =
(*self->amount + 0.4f * (1.f - *self->amount)) *
2.4f;
for (int i = 0; i < 128; i++)
{
for (int j = 0; j < 7; j++)
{
/* voice spread */
int is_even = (j % 2) == 0;
float freq_apart;
if (is_even)
{
freq_apart = (float) (j / 2) * freq_delta;
}
else
{
freq_apart = (float) (j / 2 + 1) * - freq_delta;
}
values->keyfreqs[i][j] =
self->keys[i].base_freq +
math_round_float_to_int (freq_apart);
}
}
return values;
}
/**
* Cleanup function after work is finished.
*/
static void
free_values (
Saw * self,
SawValues * values)
{
lv2_log_note (
&self->common.logger, "freeing values\n");
free (values);
}
/**
* Sets the values from the SawValues struct on the
* actual plugin.
*/
static void
set_values (
Saw * self,
SawValues * values)
{
lv2_log_note (
&self->common.logger, "setting values\n");
self->saturator->drive = values->saturator_drive;
self->saturator->dcoffset = values->saturator_dcoffset;
self->distortion->shape1 = values->distortion_shape1;
self->distortion->shape2 = values->distortion_shape2;
*self->reverb->mix = values->reverb_mix;
for (int i = 0; i < 128; i++)
{
MidiKey * key = &self->keys[i];
/* adsr */
key->adsr->atk = values->attack;
key->adsr->dec = values->decay;
key->adsr->sus = values->sustain;
key->adsr->rel = values->release;
for (int j = 0; j < 7; j++)
{
/* spread voices */
*key->blsaws[j]->freq = values->keyfreqs[i][j];
}
}
}
static LV2_Worker_Status
work (
LV2_Handle instance,
LV2_Worker_Respond_Function respond,
LV2_Worker_Respond_Handle handle,
uint32_t size,
const void * data)
{
Saw * self = (Saw *) instance;
lv2_log_note (
&self->common.logger, "working\n");
const LV2_Atom * atom =
(const LV2_Atom *) data;
if (atom->type == self->common.uris.saw_freeValues)
{
/* free old values */
const SawValuesMessage * msg =
(const SawValuesMessage *) data;
free_values (self, msg->values);
}
else if (atom->type == self->common.uris.saw_calcValues)
{
/* recalc values */
SawValues * values = calc_values (self);
respond (handle, sizeof (values), &values);
}
return LV2_WORKER_SUCCESS;
}
static LV2_Worker_Status
work_response (
LV2_Handle instance,
uint32_t size,
const void* data)
{
Saw * self = (Saw *) instance;
/* install the new values */
SawValues * values = * (SawValues * const *) data;
set_values (self, values);
/* send a message to the worker to free the
* values */
SawValuesMessage msg = {
{ sizeof (SawValues *),
self->common.uris.saw_freeValues },
values };
self->common.schedule->schedule_work (
self->common.schedule->handle,
sizeof (msg), &msg);
lv2_log_note (
&self->common.logger, "inside work response\n");
return LV2_WORKER_SUCCESS;
}
static LV2_Handle
instantiate (
@ -98,7 +277,7 @@ instantiate ( @@ -98,7 +277,7 @@ instantiate (
const char* bundle_path,
const LV2_Feature* const* features)
{
SuperSaw * self = calloc (1, sizeof (SuperSaw));
Saw * self = calloc (1, sizeof (Saw));
self->common.samplerate = rate;
@ -112,6 +291,11 @@ instantiate ( @@ -112,6 +291,11 @@ instantiate (
self->common.map =
(LV2_URID_Map*) features[i]->data;
}
else if (HAVE_FEATURE (LV2_WORKER__schedule))
{
self->common.schedule =
(LV2_Worker_Schedule *) features[i]->data;
}
else if (HAVE_FEATURE (LV2_LOG__log))
{
self->common.log =
@ -122,8 +306,16 @@ instantiate ( @@ -122,8 +306,16 @@ instantiate (
if (!self->common.map)
{
fprintf (stderr, "Missing feature urid:map\n");
return NULL;
lv2_log_error (
&self->common.logger, "Missing feature urid:map\n");
goto fail;
}
else if (!self->common.schedule)
{
lv2_log_error (
&self->common.logger,
"Missing feature work:schedule\n");
goto fail;
}
/* map uris */
@ -133,97 +325,11 @@ instantiate ( @@ -133,97 +325,11 @@ instantiate (
lv2_atom_forge_init (
&self->common.forge, self->common.map);
return (LV2_Handle) self;
}
static void
connect_port (
LV2_Handle instance,
uint32_t port,
void * data)
{
SuperSaw * self = (SuperSaw*) instance;
switch ((PortIndex) port)
{
case SUPERSAW_CONTROL:
self->control =
(const LV2_Atom_Sequence *) data;
break;
case SUPERSAW_NOTIFY:
self->notify =
(LV2_Atom_Sequence *) data;
break;
case SUPERSAW_AMOUNT:
self->amount = (const float *) data;
break;
case SUPERSAW_STEREO_OUT_L:
self->stereo_out_l = (float *) data;
break;
case SUPERSAW_STEREO_OUT_R:
self->stereo_out_r = (float *) data;
break;
default:
break;
}
}
/**
* To be called when the amount changes.
*/
static void
recalc_values (
SuperSaw * self)
{
self->attack = 0.02f;
self->decay = 0.04f + *self->amount * 0.4f;
self->sustain = 0.5f;
self->release = 0.04f + *self->amount * 0.4f;
self->saturator->drive = *self->amount * 0.3f;
self->saturator->dcoffset = *self->amount * 0.3f;
self->distortion->shape1 = *self->amount * 0.2f;
self->distortion->shape2 = *self->amount * 0.2f;
*self->reverb->mix = *self->amount * 0.5f;
for (int i = 0; i < 128; i++)
{
MidiKey * key = &self->keys[i];
/* adsr */
key->adsr->atk = self->attack;
key->adsr->dec = self->decay;
key->adsr->sus = self->sustain;
key->adsr->rel = self->release;
for (int j = 0; j < 7; j++)
{
/* voice spread */
int is_even = (j % 2) == 0;
float freq_apart;
float freq_delta =
(*self->amount + 0.4f * (1.f - *self->amount)) *
2.4f;
if (is_even)
{
freq_apart = (float) (j / 2) * freq_delta;
}
else
{
freq_apart = (float) (j / 2 + 1) * - freq_delta;
}
*key->blsaws[j]->freq =
key->base_freq +
math_round_float_to_int (freq_apart);
}
}
}
static void
activate (
LV2_Handle instance)
{
SuperSaw * self = (SuperSaw*) instance;
/* init logger */
lv2_log_logger_init (
&self->common.logger, self->common.map, self->common.log);
/* create synth */
srand (time (NULL));
for (int i = 0; i < 128; i++)
{
@ -284,7 +390,55 @@ activate ( @@ -284,7 +390,55 @@ activate (
sp_zitarev_init (self->sp, self->reverb);
*self->reverb->level = 0.f;
recalc_values (self);
return (LV2_Handle) self;
fail:
free (self);
return NULL;
}
static void
connect_port (
LV2_Handle instance,
uint32_t port,
void * data)
{
Saw * self = (Saw*) instance;
switch ((PortIndex) port)
{
case SUPERSAW_CONTROL:
self->control =
(const LV2_Atom_Sequence *) data;
break;
case SUPERSAW_NOTIFY:
self->notify =
(LV2_Atom_Sequence *) data;
break;
case SUPERSAW_AMOUNT:
self->amount = (const float *) data;
break;
case SUPERSAW_STEREO_OUT_L:
self->stereo_out_l = (float *) data;
break;
case SUPERSAW_STEREO_OUT_R:
self->stereo_out_r = (float *) data;
break;
default:
break;
}
}
static void
activate (
LV2_Handle instance)
{
Saw * self = (Saw*) instance;
/* load the default values */
SawValues * values = calc_values (self);
set_values (self, values);
free_values (self, values);
}
/**
@ -292,11 +446,13 @@ activate ( @@ -292,11 +446,13 @@ activate (
*/
static void
process (
SuperSaw * self,
Saw * self,
uint32_t * offset)
{
self->stereo_out_l[*offset] = 0.f;
self->stereo_out_r[*offset] = 0.f;
float * current_l = &self->stereo_out_l[*offset];
float * current_r = &self->stereo_out_r[*offset];
*current_l = 0.f;
*current_r = 0.f;
for (int i = 0; i < 128; i++)
{
@ -333,13 +489,13 @@ process ( @@ -333,13 +489,13 @@ process (
if (j % 2 == 0)
{
self->stereo_out_l[*offset] += val * 0.8f;
self->stereo_out_r[*offset] += val * 0.2f;
*current_l += val * 0.8f;
*current_r += val * 0.2f;
}
else
{
self->stereo_out_l[*offset] += val * 0.2f;
self->stereo_out_r[*offset] += val * 0.8f;
*current_l += val * 0.2f;
*current_r += val * 0.8f;
}
}
@ -347,37 +503,38 @@ process ( @@ -347,37 +503,38 @@ process (
}
}
#if 0
/* saturate */
float saturated = 0;
sp_saturator_compute (
self->sp, self->saturator, &self->stereo_out_l[*offset],
&saturated);
self->stereo_out_l[*offset] += saturated;
sp_saturator_compute (
self->sp, self->saturator, &self->stereo_out_r[*offset],
&saturated);
self->stereo_out_r[*offset] += saturated;
#endif
/* saturate - for some reason it makes noise when it's
* silent */
if (fabsf (*current_l) > 0.001f ||
fabsf (*current_r) > 0.001f)
{
float saturated = 0;
sp_saturator_compute (
self->sp, self->saturator, current_l,
&saturated);
*current_l += saturated;
sp_saturator_compute (
self->sp, self->saturator, current_r,
&saturated);
*current_r += saturated;
}
/* distort */
float distortion = 0;
sp_dist_compute (
self->sp, self->distortion, &self->stereo_out_l[*offset],
self->sp, self->distortion, current_l,
&distortion);
self->stereo_out_l[*offset] += distortion;
*current_l += distortion;
sp_dist_compute (
self->sp, self->distortion, &self->stereo_out_r[*offset],
self->sp, self->distortion, current_r,
&distortion);
self->stereo_out_r[*offset] += distortion;
*current_r += distortion;
/* reverb */
sp_zitarev_compute (
self->sp, self->reverb,
&self->stereo_out_l[*offset],
&self->stereo_out_r[*offset],
&self->stereo_out_l[*offset],
&self->stereo_out_r[*offset]);
current_l, current_r,
current_l, current_r);
(*offset)++;
}
@ -387,13 +544,24 @@ run ( @@ -387,13 +544,24 @@ run (
LV2_Handle instance,
uint32_t n_samples)
{
SuperSaw * self = (SuperSaw *) instance;
Saw * self = (Saw *) instance;
uint32_t processed = 0;
if (!math_floats_equal (self->last_amount, *self->amount))
{
recalc_values (self);
/* send a message to the worker to calculate new
* values */
SawValuesMessage msg = {
{ 0,
self->common.uris.saw_calcValues }
};
self->common.schedule->schedule_work (
self->common.schedule->handle,
sizeof (msg), &msg);
lv2_log_note (
&self->common.logger, "scheduled to recalculate\n");
}
/* read incoming events from host and UI */
@ -450,7 +618,7 @@ static void @@ -450,7 +618,7 @@ static void
deactivate (
LV2_Handle instance)
{
SuperSaw * self = (SuperSaw *) instance;
Saw * self = (Saw *) instance;
for (int i = 0; i < 128; i++)
{
@ -467,13 +635,20 @@ static void @@ -467,13 +635,20 @@ static void
cleanup (
LV2_Handle instance)
{
free (instance);
Saw * self = (Saw *) instance;
free (self);
}
static const void*
extension_data (
const char* uri)
const char * uri)
{
static const LV2_Worker_Interface worker =
{ work, work_response, NULL };
if (!strcmp(uri, LV2_WORKER__interface))
{
return &worker;
}
return NULL;
}

9
plugins/supersaw/ttl_gen.c → plugins/saw/ttl_gen.c

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
* along with ZPlugins. If not, see <https://www.gnu.org/licenses/>.
*/
#include "../supersaw_config.h"
#include PLUGIN_CONFIG
#include <stdio.h>
@ -54,7 +54,8 @@ int main ( @@ -54,7 +54,8 @@ int main (
@prefix rsz: <http://lv2plug.in/ns/ext/resize-port#> .\n\
@prefix time: <http://lv2plug.in/ns/ext/time#> .\n\
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .\n\
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .\n\n");
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .\n\
@prefix work: <http://lv2plug.in/ns/ext/worker#> .\n\n");
fprintf (f,
"<" PROJECT_URI ">\n\
@ -73,9 +74,11 @@ int main ( @@ -73,9 +74,11 @@ int main (
] ;\n\
doap:license <https://www.gnu.org/licenses/agpl-3.0.html> ;\n\
lv2:project <" PROJECT_URI "> ;\n\
lv2:requiredFeature urid:map ;\n\
lv2:requiredFeature urid:map ,\n\
work:schedule ;\n\
lv2:optionalFeature log:log ,\n\
lv2:hardRTCapable ;\n\
lv2:extensionData work:interface ;\n\
lv2:port [\n\
a lv2:InputPort ,\n\
atom:AtomPort ;\n\

22
scripts/lv2lint_wrap.sh

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
#!/usr/bin/env bash
# $1: lv2lint
# $2: plugin build dir
# $3: plugin name
# $4: plugin URI
set -o xtrace
LV2_LINT_BIN=$1
PL_BUILD_DIR=$2
PL_NAME=$3
PL_URI=$4
tmpdir=$(mktemp -d /tmp/lv2lint_wrap.XXXXXXXXX)
tmp_plugin_dir="$tmpdir/$PL_NAME"
mkdir -p "$tmp_plugin_dir"
cp $PL_BUILD_DIR/$PL_NAME.ttl \
$PL_BUILD_DIR/${PL_NAME}_dsp.so $tmp_plugin_dir/
cp $PL_BUILD_DIR/$3_manifest.ttl $tmp_plugin_dir/manifest.ttl
LV2_PATH="$tmpdir" env $LV2_LINT_BIN -d \
-I $tmp_plugin_dir/ $PL_URI
Loading…
Cancel
Save