From 0f204c3aa94e05cb5de478af0d5bc3762eb0ec8f Mon Sep 17 00:00:00 2001
From: Stefano D'Angelo <stefano.dangelo@orastron.com>
Date: Tue, 24 Jan 2023 17:27:56 +0100
Subject: [PATCH] new fx pan example + fix bw_pan, copyright, web code (vst3
 broken)

---
 examples/common/web/index-fx.html       |   8 +-
 examples/common/web/index-synth.html    |   8 +-
 examples/common/web/memset.c            |   2 +-
 examples/common/web/wrapper.c           |  16 ++--
 examples/fx_pan/src/bw_example_fx_pan.c | 112 ++++++++++++++++++++++++
 examples/fx_pan/src/bw_example_fx_pan.h |  42 +++++++++
 examples/fx_pan/src/config.h            |  90 +++++++++++++++++++
 examples/fx_pan/vst3/Makefile           |   6 ++
 examples/fx_pan/vst3/config_vst3.h      |  36 ++++++++
 examples/fx_pan/web/Makefile            |   4 +
 examples/fx_pan/web/config.js           |  48 ++++++++++
 include/bw_pan.h                        |  14 +--
 include/bw_ppm.h                        |   2 +-
 13 files changed, 366 insertions(+), 22 deletions(-)
 create mode 100644 examples/fx_pan/src/bw_example_fx_pan.c
 create mode 100644 examples/fx_pan/src/bw_example_fx_pan.h
 create mode 100644 examples/fx_pan/src/config.h
 create mode 100644 examples/fx_pan/vst3/Makefile
 create mode 100644 examples/fx_pan/vst3/config_vst3.h
 create mode 100644 examples/fx_pan/web/Makefile
 create mode 100644 examples/fx_pan/web/config.js

diff --git a/examples/common/web/index-fx.html b/examples/common/web/index-fx.html
index a8874e5..74a01a6 100644
--- a/examples/common/web/index-fx.html
+++ b/examples/common/web/index-fx.html
@@ -3,7 +3,7 @@
 
   Brickworks
 
-  Copyright (C) 2022 Orastron Srl unipersonale
+  Copyright (C) 2022, 2023 Orastron Srl unipersonale
 
   Brickworks is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -50,9 +50,9 @@ var Effect = {
 		if (!this.node)
 			this.node = new AudioWorkletNode(audioCtx, "BWExample",
 				{
-					numberOfInputs: 1,
-					numberOfOutputs: 1,
-					outputChannelCount: [ 1 ],
+					numberOfInputs: buses.filter(function (b) { return !b.output; }).length,
+					numberOfOutputs: buses.filter(function (b) { return b.output; }).length,
+					outputChannelCount: buses.filter(function (b) { return b.output}).map(function (b) { return b.stereo ? 2 : 1 }),
 					processorOptions: {
 						wasmBytes: this.wasmBytes
 					}
diff --git a/examples/common/web/index-synth.html b/examples/common/web/index-synth.html
index 51258f5..7714c55 100644
--- a/examples/common/web/index-synth.html
+++ b/examples/common/web/index-synth.html
@@ -3,7 +3,7 @@
 
   Brickworks
 
-  Copyright (C) 2022 Orastron Srl unipersonale
+  Copyright (C) 2022, 2023 Orastron Srl unipersonale
 
   Brickworks is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -51,9 +51,9 @@ var Synth = {
 		if (!this.node)
 			this.node = new AudioWorkletNode(audioCtx, "BWExample",
 				{
-					numberOfInputs: 0,
-					numberOfOutputs: 1,
-					outputChannelCount: [ 1 ],
+					numberOfInputs: buses.filter(function (b) { return !b.output; }).length,
+					numberOfOutputs: buses.filter(function (b) { return b.output; }).length,
+					outputChannelCount: buses.filter(function (b) { return b.output}).map(function (b) { return b.stereo ? 2 : 1 }),
 					processorOptions: { wasmBytes: this.wasmBytes }
 				});
 		this.node.connect(audioCtx.destination);
diff --git a/examples/common/web/memset.c b/examples/common/web/memset.c
index 991a99a..7adc910 100644
--- a/examples/common/web/memset.c
+++ b/examples/common/web/memset.c
@@ -1,7 +1,7 @@
 /*
  * Brickworks
  *
- * Copyright (C) 2021, 2022 Orastron Srl unipersonale
+ * Copyright (C) 2023 Orastron Srl unipersonale
  *
  * Brickworks is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/examples/common/web/wrapper.c b/examples/common/web/wrapper.c
index 9fb0b95..f785d1c 100644
--- a/examples/common/web/wrapper.c
+++ b/examples/common/web/wrapper.c
@@ -1,7 +1,7 @@
 /*
  * Brickworks
  *
- * Copyright (C) 2022 Orastron Srl unipersonale
+ * Copyright (C) 2022, 2023 Orastron Srl unipersonale
  *
  * Brickworks is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -54,23 +54,29 @@ wrapper wrapper_new(float sample_rate) {
 
 #if NUM_BUSES_IN != 0
 	int dx = 0;
+	int jx = 0;
 	for (int i = 0; i < NUM_BUSES_IN; i++) {
-		ret->x[i] = ret->ins + dx;
+		ret->x[jx] = ret->ins + dx;
 		dx += 128;
+		jx++;
 		if (config_buses_in[i].configs & IO_STEREO) {
-			ret->x[i] = ret->ins + dx;
+			ret->x[jx] = ret->ins + dx;
 			dx += 128;
+			jx++;
 		}
 	}
 #endif
 #if NUM_BUSES_OUT != 0
 	int dy = 0;
+	int jy = 0;
 	for (int i = 0; i < NUM_BUSES_OUT; i++) {
-		ret->y[i] = ret->outs + dy;
+		ret->y[jy] = ret->outs + dy;
 		dy += 128;
+		jy++;
 		if (config_buses_out[i].configs & IO_STEREO) {
-			ret->y[i] = ret->outs + dy;
+			ret->y[jy] = ret->outs + dy;
 			dy += 128;
+			jy++;
 		}
 	}
 #endif
diff --git a/examples/fx_pan/src/bw_example_fx_pan.c b/examples/fx_pan/src/bw_example_fx_pan.c
new file mode 100644
index 0000000..d88e8ee
--- /dev/null
+++ b/examples/fx_pan/src/bw_example_fx_pan.c
@@ -0,0 +1,112 @@
+/*
+ * Brickworks
+ *
+ * Copyright (C) 2023 Orastron Srl unipersonale
+ *
+ * Brickworks 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 3 of the License.
+ *
+ * Brickworks 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 Brickworks.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * File author: Stefano D'Angelo
+ */
+
+#include "bw_example_fx_pan.h"
+
+#include <bw_pan.h>
+#include <bw_ppm.h>
+#include <bw_math.h>
+#ifdef __WASM__
+# include "walloc.h"
+#else
+# include <stdlib.h>
+#endif
+
+enum {
+	p_pan,
+	p_n
+};
+
+struct _bw_example_fx_pan {
+	// Sub-components
+	bw_pan_coeffs	pan_coeffs;
+	bw_ppm_coeffs	ppm_coeffs;
+	bw_ppm_state	ppm_l_state;
+	bw_ppm_state	ppm_r_state;
+
+	// Parameters
+	float		params[p_n];
+};
+
+bw_example_fx_pan bw_example_fx_pan_new() {
+	bw_example_fx_pan instance = (bw_example_fx_pan)malloc(sizeof(struct _bw_example_fx_pan));
+	if (instance == NULL)
+		return NULL;
+	bw_pan_init(&instance->pan_coeffs);
+	bw_ppm_init(&instance->ppm_coeffs);
+	return instance;
+}
+
+void bw_example_fx_pan_free(bw_example_fx_pan instance) {
+	free(instance);
+}
+
+void bw_example_fx_pan_set_sample_rate(bw_example_fx_pan instance, float sample_rate) {
+	bw_pan_set_sample_rate(&instance->pan_coeffs, sample_rate);
+	bw_ppm_set_sample_rate(&instance->ppm_coeffs, sample_rate);
+}
+
+void bw_example_fx_pan_reset(bw_example_fx_pan instance) {
+	bw_pan_reset_coeffs(&instance->pan_coeffs);
+	bw_ppm_reset_coeffs(&instance->ppm_coeffs);
+	bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_l_state);
+	bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_r_state);
+}
+
+void bw_example_fx_pan_process(bw_example_fx_pan instance, const float** x, float** y, int n_samples) {
+	bw_pan_process(&instance->pan_coeffs, x[0], y[0], y[1], n_samples);
+	bw_ppm_update_coeffs_ctrl(&instance->ppm_coeffs);
+	for (int i = 0; i < n_samples; i++) {
+		bw_ppm_update_coeffs_audio(&instance->ppm_coeffs);
+		bw_ppm_process1(&instance->ppm_coeffs, &instance->ppm_l_state, y[0][i]);
+		bw_ppm_process1(&instance->ppm_coeffs, &instance->ppm_r_state, y[1][i]);
+	}
+}
+
+void bw_example_fx_pan_set_parameter(bw_example_fx_pan instance, int index, float value) {
+	instance->params[index] = value;
+	switch (index) {
+	case p_pan:
+		bw_pan_set_pan(&instance->pan_coeffs, value + value - 1.f);
+		break;
+	}
+}
+
+float bw_example_fx_pan_get_parameter(bw_example_fx_pan instance, int index) {
+	float r;
+	switch (index) {
+	case p_pan:
+		r = instance->params[p_pan];
+		break;
+	case p_pan + 1:
+	{
+		const float v = bw_ppm_get_y_z1(&instance->ppm_l_state);
+		r = v < -200.f ? 0.f : bw_clipf(0.01666666666666667f * v + 1.f, 0.f, 1.f);
+		break;
+	}
+	case p_pan + 2:
+	{
+		const float v = bw_ppm_get_y_z1(&instance->ppm_r_state);
+		r = v < -200.f ? 0.f : bw_clipf(0.01666666666666667f * v + 1.f, 0.f, 1.f);
+		break;
+	}
+	}
+	return r;
+}
diff --git a/examples/fx_pan/src/bw_example_fx_pan.h b/examples/fx_pan/src/bw_example_fx_pan.h
new file mode 100644
index 0000000..e4cf3ea
--- /dev/null
+++ b/examples/fx_pan/src/bw_example_fx_pan.h
@@ -0,0 +1,42 @@
+/*
+ * Brickworks
+ *
+ * Copyright (C) 2023 Orastron Srl unipersonale
+ *
+ * Brickworks 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 3 of the License.
+ *
+ * Brickworks 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 Brickworks.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * File author: Stefano D'Angelo
+ */
+
+#ifndef _BW_EXAMPLE_FX_PAN_H
+#define _BW_EXAMPLE_FX_PAN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _bw_example_fx_pan* bw_example_fx_pan;
+
+bw_example_fx_pan bw_example_fx_pan_new();
+void bw_example_fx_pan_free(bw_example_fx_pan instance);
+void bw_example_fx_pan_set_sample_rate(bw_example_fx_pan instance, float sample_rate);
+void bw_example_fx_pan_reset(bw_example_fx_pan instance);
+void bw_example_fx_pan_process(bw_example_fx_pan instance, const float** x, float** y, int n_samples);
+void bw_example_fx_pan_set_parameter(bw_example_fx_pan instance, int index, float value);
+float bw_example_fx_pan_get_parameter(bw_example_fx_pan instance, int index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/examples/fx_pan/src/config.h b/examples/fx_pan/src/config.h
new file mode 100644
index 0000000..4af50cc
--- /dev/null
+++ b/examples/fx_pan/src/config.h
@@ -0,0 +1,90 @@
+/*
+ * Brickworks
+ *
+ * Copyright (C) 2023 Orastron Srl unipersonale
+ *
+ * Brickworks 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 3 of the License.
+ *
+ * Brickworks 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 Brickworks.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * File authors: Stefano D'Angelo
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+// Definitions
+
+#define IO_MONO			1
+#define IO_STEREO		(1<<1)
+
+struct config_io_bus {
+	const char	*name;
+	char		 out;
+	char		 aux;
+	char		 cv;
+	char		 configs;
+};
+
+struct config_parameter {
+	const char	*name;
+	const char	*shortName;
+	const char	*units;
+	char		 out;
+	char		 bypass;
+	int		 steps;
+	float		 defaultValueUnmapped;
+};
+
+// Data
+
+#define COMPANY_NAME		"Orastron"
+#define COMPANY_WEBSITE		"https://www.orastron.com/"
+#define COMPANY_MAILTO		"mailto:info@orastron.com"
+
+#define PLUGIN_NAME		"bw_example_fx_pan"
+#define PLUGIN_VERSION		"0.3.0"
+
+#define NUM_BUSES_IN		1
+#define NUM_BUSES_OUT		1
+#define NUM_CHANNELS_IN		1
+#define NUM_CHANNELS_OUT	2
+
+static struct config_io_bus config_buses_in[NUM_BUSES_IN] = {
+	{ "Audio in", 0, 0, 0, IO_MONO }
+};
+
+static struct config_io_bus config_buses_out[NUM_BUSES_OUT] = {
+	{ "Audio out", 1, 0, 0, IO_STEREO }
+};
+
+#define NUM_PARAMETERS		3
+
+static struct config_parameter config_parameters[NUM_PARAMETERS] = {
+	{ "Pan", "Pan", "", 0, 0, 0, 0.5f },
+	{ "Left level", "L Level", "", 1, 0, 0, 0.f },
+	{ "Right level", "R Level", "", 1, 0, 0, 0.f }
+};
+
+// Internal API
+
+#include "bw_example_fx_pan.h"
+
+#define P_TYPE				bw_example_fx_pan
+#define P_NEW				bw_example_fx_pan_new
+#define P_FREE				bw_example_fx_pan_free
+#define P_SET_SAMPLE_RATE		bw_example_fx_pan_set_sample_rate
+#define P_RESET				bw_example_fx_pan_reset
+#define P_PROCESS			bw_example_fx_pan_process
+#define P_SET_PARAMETER			bw_example_fx_pan_set_parameter
+#define P_GET_PARAMETER			bw_example_fx_pan_get_parameter
+
+#endif
diff --git a/examples/fx_pan/vst3/Makefile b/examples/fx_pan/vst3/Makefile
new file mode 100644
index 0000000..5ba8e1e
--- /dev/null
+++ b/examples/fx_pan/vst3/Makefile
@@ -0,0 +1,6 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+
+NAME := bw_example_fx_pan
+SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_pan.c
+
+include ${ROOT_DIR}/../../common/vst3/vst3.mk
diff --git a/examples/fx_pan/vst3/config_vst3.h b/examples/fx_pan/vst3/config_vst3.h
new file mode 100644
index 0000000..ea3d041
--- /dev/null
+++ b/examples/fx_pan/vst3/config_vst3.h
@@ -0,0 +1,36 @@
+/*
+ * Brickworks
+ *
+ * Copyright (C) 2023 Orastron Srl unipersonale
+ *
+ * Brickworks 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 3 of the License.
+ *
+ * Brickworks 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 Brickworks.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * File authors: Stefano D'Angelo, Paolo Marrone
+ */
+
+#ifndef _VST3_CONFIG_H
+#define _VST3_CONFIG_H
+
+#define PLUGIN_SUBCATEGORY	"Fx|Spatial"
+
+#define PLUGIN_GUID_1		0x7ad61d9e
+#define PLUGIN_GUID_2		0x26b44033
+#define PLUGIN_GUID_3		0xbfae229a
+#define PLUGIN_GUID_4		0x3e99719e
+
+#define CTRL_GUID_1		0x59d57809
+#define CTRL_GUID_2		0x6b3d4822
+#define CTRL_GUID_3		0x89272538
+#define CTRL_GUID_4		0x8e8a352c
+
+#endif
diff --git a/examples/fx_pan/web/Makefile b/examples/fx_pan/web/Makefile
new file mode 100644
index 0000000..8e53cab
--- /dev/null
+++ b/examples/fx_pan/web/Makefile
@@ -0,0 +1,4 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_pan.c
+
+include ${ROOT_DIR}/../../common/web/web.mk
diff --git a/examples/fx_pan/web/config.js b/examples/fx_pan/web/config.js
new file mode 100644
index 0000000..8bd233b
--- /dev/null
+++ b/examples/fx_pan/web/config.js
@@ -0,0 +1,48 @@
+/*
+ * Brickworks
+ *
+ * Copyright (C) 2023 Orastron Srl unipersonale
+ *
+ * Brickworks 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 3 of the License.
+ *
+ * Brickworks 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 Brickworks.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * File author: Stefano D'Angelo
+ */
+
+var buses = [
+	{
+		stereo:		false,
+		output:		false
+	},
+	{
+		stereo:		true,
+		output:		true
+	}
+];
+
+var parameters = [
+	{
+		name:		"Pan",
+		output:		false,
+		defaultValue:	0.5
+	},
+	{
+		name:		"Left level",
+		output:		true,
+		defaultValue:	0.0
+	},
+	{
+		name:		"Right level",
+		output:		true,
+		defaultValue:	0.0
+	}
+];
diff --git a/include/bw_pan.h b/include/bw_pan.h
index 3c81ca0..6227dbe 100644
--- a/include/bw_pan.h
+++ b/include/bw_pan.h
@@ -84,7 +84,7 @@ static inline void bw_pan_update_coeffs_audio(bw_pan_coeffs *BW_RESTRICT coeffs)
  *
  *    #### bw_pan_process1()
  *  ```>>> */
-static inline float bw_pan_process1(const bw_pan_coeffs *BW_RESTRICT coeffs, float x, float *BW_RESTRICT y_l, float *BW_RESTRICT y_r);
+static inline void bw_pan_process1(const bw_pan_coeffs *BW_RESTRICT coeffs, float x, float *BW_RESTRICT y_l, float *BW_RESTRICT y_r);
 /*! <<<```
  *    Processes one input sample `x` using `coeffs`, while using and updating
  *    `state`. The left and right output samples are put into `y_l` (left) and
@@ -138,7 +138,7 @@ static inline void bw_pan_set_sample_rate(bw_pan_coeffs *BW_RESTRICT coeffs, flo
 	bw_gain_set_sample_rate(&coeffs->r_coeffs, sample_rate);
 }
 
-static inline void _bw_pan_do_update_coeffs(bw_gain_coeffs *BW_RESTRICT coeffs, char force) {
+static inline void _bw_pan_do_update_coeffs(bw_pan_coeffs *BW_RESTRICT coeffs, char force) {
 	if (force || coeffs->pan != coeffs->pan_prev) {
 		const float k = 0.125f * coeffs->pan + 0.125f;
 		bw_gain_set_gain_lin(&coeffs->l_coeffs, bw_cos2pif_3(k));
@@ -164,7 +164,7 @@ static inline void bw_pan_update_coeffs_audio(bw_pan_coeffs *BW_RESTRICT coeffs)
 	bw_gain_update_coeffs_audio(&coeffs->r_coeffs);
 }
 
-static inline float bw_pan_process1(const bw_pan_coeffs *BW_RESTRICT coeffs, float x, float *BW_RESTRICT y_l, float *BW_RESTRICT y_r) {
+static inline void bw_pan_process1(const bw_pan_coeffs *BW_RESTRICT coeffs, float x, float *BW_RESTRICT y_l, float *BW_RESTRICT y_r) {
 	*y_l = bw_gain_process1(&coeffs->l_coeffs, x);
 	*y_r = bw_gain_process1(&coeffs->r_coeffs, x);
 }
@@ -175,13 +175,13 @@ static inline void bw_pan_process(bw_pan_coeffs *BW_RESTRICT coeffs, const float
 		if (y_r != NULL) {
 			for (int i = 0; i < n_samples; i++) {
 				bw_pan_update_coeffs_audio(coeffs);
-				bw_pan_process1(coeffs, state, x[i], y_l + i, y_r + i);
+				bw_pan_process1(coeffs, x[i], y_l + i, y_r + i);
 			}
 		} else {
 			for (int i = 0; i < n_samples; i++) {
 				bw_pan_update_coeffs_audio(coeffs);
 				float r;
-				bw_pan_process1(coeffs, state, x[i], y_l + i, &r);
+				bw_pan_process1(coeffs, x[i], y_l + i, &r);
 			}
 		}
 	} else {
@@ -189,13 +189,13 @@ static inline void bw_pan_process(bw_pan_coeffs *BW_RESTRICT coeffs, const float
 			for (int i = 0; i < n_samples; i++) {
 				bw_pan_update_coeffs_audio(coeffs);
 				float l;
-				bw_pan_process1(coeffs, state, x[i], &l, y_r + i);
+				bw_pan_process1(coeffs, x[i], &l, y_r + i);
 			}
 		} else {
 			for (int i = 0; i < n_samples; i++) {
 				bw_pan_update_coeffs_audio(coeffs);
 				float l, r;
-				bw_pan_process1(coeffs, state, x[i], &l, &r);
+				bw_pan_process1(coeffs, x[i], &l, &r);
 			}
 		}
 	}
diff --git a/include/bw_ppm.h b/include/bw_ppm.h
index 4ad1036..59459e2 100644
--- a/include/bw_ppm.h
+++ b/include/bw_ppm.h
@@ -20,7 +20,7 @@
 
 /*!
  *  module_type {{{ dsp }}}
- *  version {{{ 0.2.0 }}}
+ *  version {{{ 0.3.0 }}}
  *  requires {{{ bw_config bw_common bw_env_follow bw_math bw_one_pole }}}
  *  description {{{
  *    Digital peak programme meter with adjustable integration time constant.