aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar anders <anders@b3059339-0415-0410-9bf9-f77b7e298cf2>2002-12-28 13:59:53 +0000
committerGravatar anders <anders@b3059339-0415-0410-9bf9-f77b7e298cf2>2002-12-28 13:59:53 +0000
commit6adaa78ee935ef89439d4b38550165f13e880320 (patch)
tree6612adc09121e661363b1370cb43981007d36b60
parent0e9c0e8aa2aa7df6aad5d78c4b664927a9d2421e (diff)
Changes includes:
- Improved runtime control system - 3 New filter panning, compressor/limiter and a noise gate - The compressor/limiter and the noise gate are not yet finished - The panning filter does combined mixing and channel routing and can be used to down-mix from stereo to mono (for example) - Improvements to volume and channel - volume now has a very good soft clipping using sin() - channel can handle generic routing of audio data - Conversion of all filters to handle floating point data - Cleanup of message printing - Fix for the sig 11 bug reported by Denes git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@8608 b3059339-0415-0410-9bf9-f77b7e298cf2
-rw-r--r--libaf/Makefile2
-rw-r--r--libaf/af.c60
-rw-r--r--libaf/af.h13
-rw-r--r--libaf/af_channels.c168
-rw-r--r--libaf/af_comp.c161
-rw-r--r--libaf/af_delay.c13
-rw-r--r--libaf/af_equalizer.c87
-rw-r--r--libaf/af_format.c74
-rw-r--r--libaf/af_gate.c157
-rw-r--r--libaf/af_pan.c184
-rw-r--r--libaf/af_resample.c277
-rw-r--r--libaf/af_resample.h161
-rw-r--r--libaf/af_tools.c79
-rw-r--r--libaf/af_volume.c266
-rw-r--r--libaf/control.h183
15 files changed, 1459 insertions, 426 deletions
diff --git a/libaf/Makefile b/libaf/Makefile
index 05aac6d3cb..d66aa8b4ee 100644
--- a/libaf/Makefile
+++ b/libaf/Makefile
@@ -2,7 +2,7 @@ include ../config.mak
LIBNAME = libaf.a
-SRCS=af.c af_mp.c af_dummy.c af_delay.c af_channels.c af_format.c af_resample.c window.c filter.c af_volume.c af_equalizer.c
+SRCS=af.c af_mp.c af_dummy.c af_delay.c af_channels.c af_format.c af_resample.c window.c filter.c af_volume.c af_equalizer.c af_tools.c af_comp.c af_gate.c af_pan.c
OBJS=$(SRCS:.c=.o)
diff --git a/libaf/af.c b/libaf/af.c
index 5216c6dcdb..3f86ab9654 100644
--- a/libaf/af.c
+++ b/libaf/af.c
@@ -16,6 +16,9 @@ extern af_info_t af_info_format;
extern af_info_t af_info_resample;
extern af_info_t af_info_volume;
extern af_info_t af_info_equalizer;
+extern af_info_t af_info_gate;
+extern af_info_t af_info_comp;
+extern af_info_t af_info_pan;
static af_info_t* filter_list[]={ \
&af_info_dummy,\
@@ -25,6 +28,9 @@ static af_info_t* filter_list[]={ \
&af_info_resample,\
&af_info_volume,\
&af_info_equalizer,\
+ &af_info_gate,\
+ &af_info_comp,\
+ &af_info_pan,\
NULL \
};
@@ -72,7 +78,7 @@ af_instance_t* af_create(af_stream_t* s, char* name)
// Allocate space for the new filter and reset all pointers
af_instance_t* new=malloc(sizeof(af_instance_t));
if(!new){
- af_msg(AF_MSG_ERROR,"Could not allocate memory\n");
+ af_msg(AF_MSG_ERROR,"[libaf] Could not allocate memory\n");
return NULL;
}
memset(new,0,sizeof(af_instance_t));
@@ -88,13 +94,14 @@ af_instance_t* af_create(af_stream_t* s, char* name)
non-reentrant */
if(new->info->flags & AF_FLAGS_NOT_REENTRANT){
if(af_get(s,name)){
- af_msg(AF_MSG_ERROR,"There can only be one instance of the filter '%s' in each stream\n",name);
+ af_msg(AF_MSG_ERROR,"[libaf] There can only be one instance of"
+ " the filter '%s' in each stream\n",name);
free(new);
return NULL;
}
}
- af_msg(AF_MSG_VERBOSE,"Adding filter %s \n",name);
+ af_msg(AF_MSG_VERBOSE,"[libaf] Adding filter %s \n",name);
// Initialize the new filter
if(AF_OK == new->info->open(new) &&
@@ -108,7 +115,8 @@ af_instance_t* af_create(af_stream_t* s, char* name)
}
free(new);
- af_msg(AF_MSG_ERROR,"Couldn't create or open audio filter '%s'\n",name);
+ af_msg(AF_MSG_ERROR,"[libaf] Couldn't create or open audio filter '%s'\n",
+ name);
return NULL;
}
@@ -165,6 +173,9 @@ void af_remove(af_stream_t* s, af_instance_t* af)
{
if(!af) return;
+ // Print friendly message
+ af_msg(AF_MSG_VERBOSE,"[libaf] Removing filter %s \n",af->info->name);
+
// Notify filter before changing anything
af->control(af,AF_CONTROL_PRE_DESTROY,0);
@@ -234,8 +245,9 @@ int af_reinit(af_stream_t* s, af_instance_t* af)
// Create format filter
if(NULL == (new = af_prepend(s,af,"format")))
return AF_ERROR;
- // Set output format
- if(AF_OK != (rv = new->control(new,AF_CONTROL_FORMAT,&in)))
+ // Set output bits per sample
+ if(AF_OK != (rv = new->control(new,AF_CONTROL_FORMAT_BPS,&in.bps)) ||
+ AF_OK != (rv = new->control(new,AF_CONTROL_FORMAT_FMT,&in.format)))
return rv;
// Initialize format filter
if(!new->prev)
@@ -245,8 +257,11 @@ int af_reinit(af_stream_t* s, af_instance_t* af)
if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in)))
return rv;
}
- if(!new) // Should _never_ happen
+ if(!new){ // Should _never_ happen
+ af_msg(AF_MSG_ERROR,"[libaf] Unable to correct audio format. "
+ "This error should never uccur, please send bugreport.\n");
return AF_ERROR;
+ }
af=new;
}
break;
@@ -264,10 +279,17 @@ int af_reinit(af_stream_t* s, af_instance_t* af)
break;
}
default:
- af_msg(AF_MSG_ERROR,"Reinitialization did not work, audio filter '%s' returned error code %i\n",af->info->name,rv);
+ af_msg(AF_MSG_ERROR,"[libaf] Reinitialization did not work, audio"
+ " filter '%s' returned error code %i\n",af->info->name,rv);
return AF_ERROR;
}
- af=af->next;
+ // Check if there are any filters left in the list
+ if(NULL == af){
+ if(!af_append(s,s->first,"dummy"))
+ return -1;
+ }
+ else
+ af=af->next;
}while(af);
return AF_OK;
}
@@ -315,7 +337,7 @@ int af_init(af_stream_t* s)
}
}
}
-
+
// Init filters
if(AF_OK != af_reinit(s,s->first))
return -1;
@@ -340,7 +362,8 @@ int af_init(af_stream_t* s)
}
}
// Init the new filter
- if(!af || (AF_OK != af->control(af,AF_CONTROL_RESAMPLE,&(s->output.rate))))
+ if(!af || (AF_OK != af->control(af,AF_CONTROL_RESAMPLE_RATE,
+ &(s->output.rate))))
return -1;
if(AF_OK != af_reinit(s,af))
return -1;
@@ -368,7 +391,8 @@ int af_init(af_stream_t* s)
else
af = s->last;
// Init the new filter
- if(!af ||(AF_OK != af->control(af,AF_CONTROL_FORMAT,&(s->output))))
+ if(!af ||(AF_OK != af->control(af,AF_CONTROL_FORMAT_BPS,&(s->output.bps)))
+ || (AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT,&(s->output.format))))
return -1;
if(AF_OK != af_reinit(s,af))
return -1;
@@ -383,7 +407,8 @@ int af_init(af_stream_t* s)
(s->last->data->nch != s->output.nch) ||
(s->last->data->rate != s->output.rate)) {
// Something is stuffed audio out will not work
- af_msg(AF_MSG_ERROR,"Unable to setup filter system can not meet sound-card demands, please report this error on MPlayer development mailing list. \n");
+ af_msg(AF_MSG_ERROR,"[libaf] Unable to setup filter system can not"
+ " meet sound-card demands, please send bugreport. \n");
af_uninit(s);
return -1;
}
@@ -493,6 +518,10 @@ int af_calc_insize_constrained(af_stream_t* s, int len,
mul.d *= af->mul.d;
af=af->next;
}while(af);
+ // Sanity check
+ if(!mul.n || !mul.d)
+ return -1;
+
in = t * (((len/t) * mul.d - 1)/mul.n);
if(in>max_insize) in=t*(max_insize/t);
@@ -531,14 +560,15 @@ inline int af_resize_local_buffer(af_instance_t* af, af_data_t* data)
{
// Calculate new length
register int len = af_lencalc(af->mul,data);
- af_msg(AF_MSG_VERBOSE,"Reallocating memory in module %s, old len = %i, new len = %i\n",af->info->name,af->data->len,len);
+ af_msg(AF_MSG_VERBOSE,"[libaf] Reallocating memory in module %s, "
+ "old len = %i, new len = %i\n",af->info->name,af->data->len,len);
// If there is a buffer free it
if(af->data->audio)
free(af->data->audio);
// Create new buffer and check that it is OK
af->data->audio = malloc(len);
if(!af->data->audio){
- af_msg(AF_MSG_FATAL,"Could not allocate memory \n");
+ af_msg(AF_MSG_FATAL,"[libaf] Could not allocate memory \n");
return AF_ERROR;
}
af->data->len=len;
diff --git a/libaf/af.h b/libaf/af.h
index dc542bb7e0..48dfbc5b17 100644
--- a/libaf/af.h
+++ b/libaf/af.h
@@ -180,6 +180,19 @@ int af_resize_local_buffer(af_instance_t* af, af_data_t* data);
needed */
int af_lencalc(frac_t mul, af_data_t* data);
+/* Helper function used to convert to gain value from dB. Returns
+ AF_OK if of and AF_ERROR if fail */
+int af_from_dB(int n, float* in, float* out, float k, float mi, float ma);
+/* Helper function used to convert from gain value to dB. Returns
+ AF_OK if of and AF_ERROR if fail */
+int af_to_dB(int n, float* in, float* out, float k);
+/* Helper function used to convert from ms to sample time*/
+int af_from_ms(int n, float* in, float* out, int rate, float mi, float ma);
+/* Helper function used to convert from sample time to ms */
+int af_to_ms(int n, float* in, float* out, int rate);
+/* Helper function for testing the output format */
+int af_test_output(struct af_instance_s* af, af_data_t* out);
+
/* Memory reallocation macro: if a local buffer is used (i.e. if the
filter doesn't operate on the incoming buffer this macro must be
called to ensure the buffer is big enough. */
diff --git a/libaf/af_channels.c b/libaf/af_channels.c
index f6cdba172c..e5f54ceb54 100644
--- a/libaf/af_channels.c
+++ b/libaf/af_channels.c
@@ -10,6 +10,15 @@
#include "af.h"
+#define FR 0
+#define TO 1
+
+typedef struct af_channels_s{
+ int route[AF_NCH][2];
+ int nr;
+ int router;
+}af_channels_t;
+
// Local function for copying data
void copy(void* in, void* out, int ins, int inos,int outs, int outos, int len, int bps)
{
@@ -67,41 +76,140 @@ void copy(void* in, void* out, int ins, int inos,int outs, int outos, int len, i
break;
}
default:
- af_msg(AF_MSG_ERROR,"[channels] Unsupported number of bytes/sample: %i please report this error on the MPlayer mailing list. \n",bps);
+ af_msg(AF_MSG_ERROR,"[channels] Unsupported number of bytes/sample: %i"
+ " please report this error on the MPlayer mailing list. \n",bps);
+ }
+}
+
+// Make sure the routes are sane
+static int check_routes(af_channels_t* s, int nin, int nout)
+{
+ int i;
+ if((s->nr < 1) || (s->nr > AF_NCH)){
+ af_msg(AF_MSG_ERROR,"[channels] The number of routing pairs musst be"
+ " between 1 and %i. Current value is %i\n",AF_NCH,s->nr);
+ return AF_ERROR;
+ }
+
+ for(i=0;i<s->nr;i++){
+ if((s->route[i][FR] >= nin) || (s->route[i][TO] >= nout)){
+ af_msg(AF_MSG_ERROR,"[channels] Invalid routing in pair nr. %i.\n", i);
+ return AF_ERROR;
+ }
}
+ return AF_OK;
}
// Initialization and runtime control
static int control(struct af_instance_s* af, int cmd, void* arg)
{
+ af_channels_t* s = af->setup;
switch(cmd){
case AF_CONTROL_REINIT:
- // Make sure this filter isn't redundant
- if(af->data->nch == ((af_data_t*)arg)->nch)
- return AF_DETACH;
+
+ // Set default channel assignment
+ if(!s->router){
+ int i;
+ // Make sure this filter isn't redundant
+ if(af->data->nch == ((af_data_t*)arg)->nch)
+ return AF_DETACH;
+
+ // If mono: fake stereo
+ if(((af_data_t*)arg)->nch == 1){
+ s->nr = min(af->data->nch,2);
+ for(i=0;i<s->nr;i++){
+ s->route[i][FR] = 0;
+ s->route[i][TO] = i;
+ }
+ }
+ else{
+ s->nr = min(af->data->nch, ((af_data_t*)arg)->nch);
+ for(i=0;i<s->nr;i++){
+ s->route[i][FR] = i;
+ s->route[i][TO] = i;
+ }
+ }
+ }
af->data->rate = ((af_data_t*)arg)->rate;
af->data->format = ((af_data_t*)arg)->format;
af->data->bps = ((af_data_t*)arg)->bps;
af->mul.n = af->data->nch;
af->mul.d = ((af_data_t*)arg)->nch;
- return AF_OK;
+ return check_routes(s,((af_data_t*)arg)->nch,af->data->nch);
case AF_CONTROL_COMMAND_LINE:{
int nch = 0;
- sscanf((char*)arg,"%i",&nch);
- return af->control(af,AF_CONTROL_CHANNELS,&nch);
- }
- case AF_CONTROL_CHANNELS:
+ int n = 0;
+ // Check number of channels and number of routing pairs
+ sscanf(arg, "%i:%i%n", &nch, &s->nr, &n);
+
+ // If router scan commandline for routing pairs
+ if(s->nr){
+ char* cp = &((char*)arg)[n];
+ int ch = 0;
+ // Sanity check
+ if((s->nr < 1) || (s->nr > AF_NCH)){
+ af_msg(AF_MSG_ERROR,"[channels] The number of routing pairs musst be"
+ " between 1 and %i. Current value is %i\n",AF_NCH,s->nr);
+ }
+ s->router = 1;
+ // Scan for pairs on commandline
+ while((*cp == ':') && (ch < s->nr)){
+ sscanf(cp, ":%i:%i%n" ,&s->route[ch][FR], &s->route[ch][TO], &n);
+ af_msg(AF_MSG_DEBUG0,"[channels] Routing from channel %i to"
+ " channel %i\n",s->route[ch][FR],s->route[ch][TO]);
+ cp = &cp[n];
+ ch++;
+ }
+ }
+
+ if(AF_OK != af->control(af,AF_CONTROL_CHANNELS | AF_CONTROL_SET ,&nch))
+ return AF_ERROR;
+ return AF_OK;
+ }
+ case AF_CONTROL_CHANNELS | AF_CONTROL_SET:
// Reinit must be called after this function has been called
// Sanity check
if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > 6){
- af_msg(AF_MSG_ERROR,"[channels] The number of output channels must be between 1 and 6. Current value is%i \n",((int*)arg)[0]);
+ af_msg(AF_MSG_ERROR,"[channels] The number of output channels must be"
+ " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
return AF_ERROR;
}
af->data->nch=((int*)arg)[0];
- af_msg(AF_MSG_VERBOSE,"[channels] Changing number of channels to %i\n",af->data->nch);
+ if(!s->router)
+ af_msg(AF_MSG_VERBOSE,"[channels] Changing number of channels"
+ " to %i\n",af->data->nch);
+ return AF_OK;
+ case AF_CONTROL_CHANNELS | AF_CONTROL_GET:
+ *(int*)arg = af->data->nch;
+ return AF_OK;
+ case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_SET:{
+ int ch = ((af_control_ext_t*)arg)->ch;
+ int* route = ((af_control_ext_t*)arg)->arg;
+ s->route[ch][FR] = route[FR];
+ s->route[ch][TO] = route[TO];
+ return AF_OK;
+ }
+ case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_GET:{
+ int ch = ((af_control_ext_t*)arg)->ch;
+ int* route = ((af_control_ext_t*)arg)->arg;
+ route[FR] = s->route[ch][FR];
+ route[TO] = s->route[ch][TO];
+ return AF_OK;
+ }
+ case AF_CONTROL_CHANNELS_NR | AF_CONTROL_SET:
+ s->nr = *(int*)arg;
+ return AF_OK;
+ case AF_CONTROL_CHANNELS_NR | AF_CONTROL_GET:
+ *(int*)arg = s->nr;
+ return AF_OK;
+ case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_SET:
+ s->router = *(int*)arg;
+ return AF_OK;
+ case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_GET:
+ *(int*)arg = s->router;
return AF_OK;
}
return AF_UNKNOWN;
@@ -110,6 +218,8 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
// Deallocate memory
static void uninit(struct af_instance_s* af)
{
+ if(af->setup)
+ free(af->setup);
if(af->data)
free(af->data);
}
@@ -117,32 +227,21 @@ static void uninit(struct af_instance_s* af)
// Filter data through filter
static af_data_t* play(struct af_instance_s* af, af_data_t* data)
{
- af_data_t* c = data; // Current working data
- af_data_t* l = af->data; // Local data
-
+ af_data_t* c = data; // Current working data
+ af_data_t* l = af->data; // Local data
+ af_channels_t* s = af->setup;
+ int i;
+
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
- // Reset unused channels if nch in < nch out
- if(af->mul.n > af->mul.d)
- memset(l->audio,0,(c->len*af->mul.n)/af->mul.d);
+ // Reset unused channels
+ memset(l->audio,0,(c->len*af->mul.n)/af->mul.d);
- // Special case always output L & R
- if(c->nch == 1){
- copy(c->audio,l->audio,1,0,l->nch,0,c->len,c->bps);
- copy(c->audio,l->audio,1,0,l->nch,1,c->len,c->bps);
- }
- else{
- int i;
- if(l->nch < c->nch){
- for(i=0;i<l->nch;i++) // Truncates R if l->nch == 1 not good need mixing
- copy(c->audio,l->audio,c->nch,i,l->nch,i,c->len,c->bps);
- }
- else{
- for(i=0;i<c->nch;i++)
- copy(c->audio,l->audio,c->nch,i,l->nch,i,c->len,c->bps);
- }
- }
+ if(AF_OK == check_routes(s,c->nch,l->nch))
+ for(i=0;i<s->nr;i++)
+ copy(c->audio,l->audio,c->nch,s->route[i][FR],
+ l->nch,s->route[i][TO],c->len,c->bps);
// Set output data
c->audio = l->audio;
@@ -160,7 +259,8 @@ static int open(af_instance_t* af){
af->mul.n=1;
af->mul.d=1;
af->data=calloc(1,sizeof(af_data_t));
- if(af->data == NULL)
+ af->setup=calloc(1,sizeof(af_channels_t));
+ if((af->data == NULL) || (af->setup == NULL))
return AF_ERROR;
return AF_OK;
}
diff --git a/libaf/af_comp.c b/libaf/af_comp.c
new file mode 100644
index 0000000000..0282ff2f89
--- /dev/null
+++ b/libaf/af_comp.c
@@ -0,0 +1,161 @@
+/*=============================================================================
+//
+// This software has been released under the terms of the GNU Public
+// license. See http://www.gnu.org/copyleft/gpl.html for details.
+//
+// Copyright 2002 Anders Johansson ajh@atri.curtin.edu.au
+//
+//=============================================================================
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+typedef struct af_comp_s
+{
+ int enable[AF_NCH]; // Enable/disable / channel
+ float time[AF_NCH]; // Forgetting factor for power estimate
+ float pow[AF_NCH]; // Estimated power level [dB]
+ float tresh[AF_NCH]; // Threshold [dB]
+ float attack[AF_NCH]; // Attack time [ms]
+ float release[AF_NCH]; // Release time [ms]
+ float ratio[AF_NCH]; // Compression ratio
+}af_comp_t;
+
+// Initialization and runtime control
+static int control(struct af_instance_s* af, int cmd, void* arg)
+{
+ af_comp_t* s = (af_comp_t*)af->setup;
+ int i;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((af_data_t*)arg)->rate;
+ af->data->nch = ((af_data_t*)arg)->nch;
+ af->data->format = AF_FORMAT_F | AF_FORMAT_NE;
+ af->data->bps = 4;
+
+ // Time constant set to 0.1s
+ // s->alpha = (1.0/0.2)/(2.0*M_PI*(float)((af_data_t*)arg)->rate);
+ return af_test_output(af,(af_data_t*)arg);
+ case AF_CONTROL_COMMAND_LINE:{
+/* float v=-10.0; */
+/* float vol[AF_NCH]; */
+/* float s=0.0; */
+/* float clipp[AF_NCH]; */
+/* int i; */
+/* sscanf((char*)arg,"%f:%f", &v, &s); */
+/* for(i=0;i<AF_NCH;i++){ */
+/* vol[i]=v; */
+/* clipp[i]=s; */
+/* } */
+/* if(AF_OK != control(af,AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_SET, clipp)) */
+/* return AF_ERROR; */
+/* return control(af,AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vol); */
+ }
+ case AF_CONTROL_COMP_ON_OFF | AF_CONTROL_SET:
+ memcpy(s->enable,(int*)arg,AF_NCH*sizeof(int));
+ return AF_OK;
+ case AF_CONTROL_COMP_ON_OFF | AF_CONTROL_GET:
+ memcpy((int*)arg,s->enable,AF_NCH*sizeof(int));
+ return AF_OK;
+ case AF_CONTROL_COMP_THRESH | AF_CONTROL_SET:
+ return af_from_dB(AF_NCH,(float*)arg,s->tresh,20.0,-60.0,-1.0);
+ case AF_CONTROL_COMP_THRESH | AF_CONTROL_GET:
+ return af_to_dB(AF_NCH,s->tresh,(float*)arg,10.0);
+ case AF_CONTROL_COMP_ATTACK | AF_CONTROL_SET:
+ return af_from_ms(AF_NCH,(float*)arg,s->attack,af->data->rate,500.0,0.1);
+ case AF_CONTROL_COMP_ATTACK | AF_CONTROL_GET:
+ return af_to_ms(AF_NCH,s->attack,(float*)arg,af->data->rate);
+ case AF_CONTROL_COMP_RELEASE | AF_CONTROL_SET:
+ return af_from_ms(AF_NCH,(float*)arg,s->release,af->data->rate,3000.0,10.0);
+ case AF_CONTROL_COMP_RELEASE | AF_CONTROL_GET:
+ return af_to_ms(AF_NCH,s->release,(float*)arg,af->data->rate);
+ case AF_CONTROL_COMP_RATIO | AF_CONTROL_SET:
+ for(i=0;i<AF_NCH;i++)
+ s->ratio[i] = clamp(((float*)arg)[i],1.0,10.0);
+ return AF_OK;
+ case AF_CONTROL_COMP_RATIO | AF_CONTROL_GET:
+ for(i=0;i<AF_NCH;i++)
+ ((float*)arg)[i] = s->ratio[i];
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance_s* af)
+{
+ if(af->data)
+ free(af->data);
+ if(af->setup)
+ free(af->setup);
+}
+
+// Filter data through filter
+static af_data_t* play(struct af_instance_s* af, af_data_t* data)
+{
+ af_data_t* c = data; // Current working data
+ af_comp_t* s = (af_comp_t*)af->setup; // Setup for this instance
+ float* a = (float*)c->audio; // Audio data
+ int len = c->len/4; // Number of samples
+ int ch = 0; // Channel counter
+ register int nch = c->nch; // Number of channels
+ register int i = 0;
+
+ // Compress/expand
+ for(ch = 0; ch < nch ; ch++){
+ if(s->enable[ch]){
+ float t = 1.0 - s->time[ch];
+ for(i=ch;i<len;i+=nch){
+ register float x = a[i];
+ register float pow = x*x;
+ s->pow[ch] = t*s->pow[ch] +
+ pow*s->time[ch]; // LP filter
+ if(pow < s->pow[ch]){
+ ;
+ }
+ else{
+ ;
+ }
+ a[i] = x;
+ }
+ }
+ }
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int open(af_instance_t* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul.n=1;
+ af->mul.d=1;
+ af->data=calloc(1,sizeof(af_data_t));
+ af->setup=calloc(1,sizeof(af_comp_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ return AF_OK;
+}
+
+// Description of this filter
+af_info_t af_info_comp = {
+ "Compressor/expander audio filter",
+ "comp",
+ "Anders",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ open
+};
diff --git a/libaf/af_delay.c b/libaf/af_delay.c
index 6ee94c2427..2f35e73a6f 100644
--- a/libaf/af_delay.c
+++ b/libaf/af_delay.c
@@ -27,14 +27,15 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
af->data->format = ((af_data_t*)arg)->format;
af->data->bps = ((af_data_t*)arg)->bps;
- return af->control(af,AF_CONTROL_DELAY_SET_LEN,&((af_delay_t*)af->setup)->tlen);
+ return af->control(af,AF_CONTROL_DELAY_LEN | AF_CONTROL_SET,
+ &((af_delay_t*)af->setup)->tlen);
}
case AF_CONTROL_COMMAND_LINE:{
float d = 0;
sscanf((char*)arg,"%f",&d);
- return af->control(af,AF_CONTROL_DELAY_SET_LEN,&d);
+ return af->control(af,AF_CONTROL_DELAY_LEN | AF_CONTROL_SET,&d);
}
- case AF_CONTROL_DELAY_SET_LEN:{
+ case AF_CONTROL_DELAY_LEN | AF_CONTROL_SET:{
af_delay_t* s = (af_delay_t*)af->setup;
void* bt = s->buf; // Old buffer
int lt = s->len; // Old len
@@ -50,8 +51,7 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
// Set new len and allocate new buffer
s->tlen = *((float*)arg);
af->delay = s->tlen * 1000.0;
-// s->len = af->data->rate*af->data->bps*af->data->nch*(int)s->tlen;
- s->len = ((int)(af->data->rate*s->tlen))*af->data->bps*af->data->nch;
+ s->len = af->data->rate*af->data->bps*af->data->nch*(int)s->tlen;
s->buf = malloc(s->len);
af_msg(AF_MSG_DEBUG0,"[delay] Delaying audio output by %0.2fs\n",s->tlen);
af_msg(AF_MSG_DEBUG1,"[delay] Delaying audio output by %i bytes\n",s->len);
@@ -74,6 +74,9 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
}
return AF_OK;
}
+ case AF_CONTROL_DELAY_LEN | AF_CONTROL_GET:
+ *((float*)arg) = ((af_delay_t*)af->setup)->tlen;
+ return AF_OK;
}
return AF_UNKNOWN;
}
diff --git a/libaf/af_equalizer.c b/libaf/af_equalizer.c
index d33e84d47f..256c7e7e5d 100644
--- a/libaf/af_equalizer.c
+++ b/libaf/af_equalizer.c
@@ -22,16 +22,27 @@
#include <math.h>
#include "af.h"
-#include "equalizer.h"
-#define NCH AF_NCH // Number of channels
#define L 2 // Storage for filter taps
#define KM 10 // Max number of bands
#define Q 1.2247449 /* Q value for band-pass filters 1.2247=(3/2)^(1/2)
gives 4dB suppression @ Fc*2 and Fc/2 */
-// Center frequencies for band-pass filters
+/* Center frequencies for band-pass filters
+ The different frequency bands are:
+ nr. center frequency
+ 0 31.25 Hz
+ 1 62.50 Hz
+ 2 125.0 Hz
+ 3 250.0 Hz
+ 4 500.0 Hz
+ 5 1.000 kHz
+ 6 2.000 kHz
+ 7 4.000 kHz
+ 8 8.000 kHz
+ 9 16.00 kHz
+*/
#define CF {31.25,62.5,125,250,500,1000,2000,4000,8000,16000}
// Maximum and minimum gain for the bands
@@ -41,12 +52,12 @@
// Data for specific instances of this filter
typedef struct af_equalizer_s
{
- float a[KM][L]; // A weights
- float b[KM][L]; // B weights
- float wq[NCH][KM][L]; // Circular buffer for W data
- float g[NCH][KM]; // Gain factor for each channel and band
- int K; // Number of used eq bands
- int channels; // Number of channels
+ float a[KM][L]; // A weights
+ float b[KM][L]; // B weights
+ float wq[AF_NCH][KM][L]; // Circular buffer for W data
+ float g[AF_NCH][KM]; // Gain factor for each channel and band
+ int K; // Number of used eq bands
+ int channels; // Number of channels
} af_equalizer_t;
// 2nd order Band-pass Filter design
@@ -76,8 +87,8 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
af->data->rate = ((af_data_t*)arg)->rate;
af->data->nch = ((af_data_t*)arg)->nch;
- af->data->format = AF_FORMAT_NE | AF_FORMAT_SI;
- af->data->bps = 2;
+ af->data->format = AF_FORMAT_NE | AF_FORMAT_F;
+ af->data->bps = 4;
// Calculate number of active filters
s->K=KM;
@@ -85,7 +96,8 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
s->K--;
if(s->K != KM)
- af_msg(AF_MSG_INFO,"Limiting the number of filters to %i due to low sample rate.\n",s->K);
+ af_msg(AF_MSG_INFO,"[equalizer] Limiting the number of filters to"
+ " %i due to low sample rate.\n",s->K);
// Generate filter taps
for(k=0;k<s->K;k++)
@@ -94,18 +106,14 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
// Calculate how much this plugin adds to the overall time delay
af->delay += 2000.0/((float)af->data->rate);
- // Only signed 16 bit little endian is supported
- if(af->data->format != ((af_data_t*)arg)->format ||
- af->data->bps != ((af_data_t*)arg)->bps)
- return AF_FALSE;
- return AF_OK;
+ return af_test_output(af,arg);
}
case AF_CONTROL_COMMAND_LINE:{
float g[10]={0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
int i,j;
sscanf((char*)arg,"%f:%f:%f:%f:%f:%f:%f:%f:%f:%f", &g[0], &g[1],
&g[2], &g[3], &g[4], &g[5], &g[6], &g[7], &g[8] ,&g[9]);
- for(i=0;i<NCH;i++){
+ for(i=0;i<AF_NCH;i++){
for(j=0;j<KM;j++){
((af_equalizer_t*)af->setup)->g[i][j] =
pow(10.0,clamp(g[j],G_MIN,G_MAX)/20.0)-1.0;
@@ -113,23 +121,28 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
}
return AF_OK;
}
- case AF_CONTROL_EQUALIZER_SET_GAIN:{
- float gain = ((equalizer_t*)arg)->gain;
- int ch = ((equalizer_t*)arg)->channel;
- int band = ((equalizer_t*)arg)->band;
- if(ch > NCH || ch < 0 || band > KM || band < 0)
+ case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_SET:{
+ float* gain = ((af_control_ext_t*)arg)->arg;
+ int ch = ((af_control_ext_t*)arg)->ch;
+ int k;
+ if(ch > AF_NCH || ch < 0)
return AF_ERROR;
-
- s->g[ch][band] = pow(10.0,clamp(gain,G_MIN,G_MAX)/20.0)-1.0;
+
+ for(k = 0 ; k<KM ; k++)
+ s->g[ch][k] = pow(10.0,clamp(gain[k],G_MIN,G_MAX)/20.0)-1.0;
+
return AF_OK;
}
- case AF_CONTROL_EQUALIZER_GET_GAIN:{
- int ch =((equalizer_t*)arg)->channel;
- int band =((equalizer_t*)arg)->band;
- if(ch > NCH || ch < 0 || band > KM || band < 0)
+ case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_GET:{
+ float* gain = ((af_control_ext_t*)arg)->arg;
+ int ch = ((af_control_ext_t*)arg)->ch;
+ int k;
+ if(ch > AF_NCH || ch < 0)
return AF_ERROR;
-
- ((equalizer_t*)arg)->gain = log10(s->g[ch][band]+1.0) * 20.0;
+
+ for(k = 0 ; k<KM ; k++)
+ gain[k] = log10(s->g[ch][k]+1.0) * 20.0;
+
return AF_OK;
}
}
@@ -155,13 +168,13 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
while(ci--){
float* g = s->g[ci]; // Gain factor
- int16_t* in = ((int16_t*)c->audio)+ci;
- int16_t* out = ((int16_t*)c->audio)+ci;
- int16_t* end = in + c->len/2; // Block loop end
+ float* in = ((float*)c->audio)+ci;
+ float* out = ((float*)c->audio)+ci;
+ float* end = in + c->len/4; // Block loop end
while(in < end){
- register uint32_t k = 0; // Frequency band index
- register float yt = (float)(*in); // Current input sample
+ register uint32_t k = 0; // Frequency band index
+ register float yt = *in; // Current input sample
in+=nch;
// Run the filters
@@ -177,7 +190,7 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
wq[0] = w;
}
// Calculate output
- *out=(int16_t)(yt/(4.0*10.0));
+ *out=yt/(4.0*10.0);
out+=nch;
}
}
diff --git a/libaf/af_format.c b/libaf/af_format.c
index 98dc63ad30..55d151abdf 100644
--- a/libaf/af_format.c
+++ b/libaf/af_format.c
@@ -126,26 +126,32 @@ static char* fmt2str(int format, char* str, size_t size)
return str;
}
-// Helper function to check sanity for input arguments
-int check_sanity(af_data_t* data)
+// Helper functions to check sanity for input arguments
+
+// Sanity check for bytes per sample
+int check_bps(int bps)
{
- char buf[256];
- // Sanity check for bytes per sample
- if(data->bps != 4 && data->bps != 2 && data->bps != 1){
- af_msg(AF_MSG_ERROR,"[format] The number of bytes per sample must be 1, 2 or 4. Current value is %i \n",data->bps);
+ if(bps != 4 && bps != 2 && bps != 1){
+ af_msg(AF_MSG_ERROR,"[format] The number of bytes per sample"
+ " must be 1, 2 or 4. Current value is %i \n",bps);
return AF_ERROR;
}
+ return AF_OK;
+}
- // Check for unsupported formats
- switch(data->format & AF_FORMAT_SPECIAL_MASK){
+// Check for unsupported formats
+int check_format(int format)
+{
+ char buf[256];
+ switch(format & AF_FORMAT_SPECIAL_MASK){
case(AF_FORMAT_MPEG2):
case(AF_FORMAT_AC3):
af_msg(AF_MSG_ERROR,"[format] Sample format %s not yet supported \n",
- fmt2str(data->format,buf,255));
+ fmt2str(format,buf,255));
return AF_ERROR;
}
return AF_OK;
-}
+}
// Initialization and runtime control
static int control(struct af_instance_s* af, int cmd, void* arg)
@@ -158,10 +164,12 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
if(af->data->format == ((af_data_t*)arg)->format &&
af->data->bps == ((af_data_t*)arg)->bps)
return AF_DETACH;
-
+
// Check for errors in configuraton
- if(AF_OK != check_sanity((af_data_t*)arg) ||
- AF_OK != check_sanity(af->data))
+ if((AF_OK != check_bps(((af_data_t*)arg)->bps)) ||
+ (AF_OK != check_format(((af_data_t*)arg)->format)) ||
+ (AF_OK != check_bps(af->data->bps)) ||
+ (AF_OK != check_format(af->data->format)))
return AF_ERROR;
af_msg(AF_MSG_VERBOSE,"[format] Changing sample format from %ibit %sto %ibit %s \n",
@@ -175,35 +183,47 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
return AF_OK;
}
case AF_CONTROL_COMMAND_LINE:{
- af_data_t d={NULL,0,0,0,0,2};
+ int bps = 2;
+ int format = AF_FORMAT_NE;
char str[256];
str[0] = '\0';
- sscanf((char*)arg,"%i:%s",&(d.bps),str);
+ sscanf((char*)arg,"%i:%s",&bps,str);
// Convert string to format
- d.format = str2fmt(str);
+ format = str2fmt(str);
// Automatic correction of errors
- switch(d.format & AF_FORMAT_SPECIAL_MASK){
+ switch(format & AF_FORMAT_SPECIAL_MASK){
case(AF_FORMAT_A_LAW):
case(AF_FORMAT_MU_LAW):
- d.bps=1; break;
+ bps=1; break;
case(AF_FORMAT_AC3):
- d.bps=4; break; // I think
+ bps=4; break; // I think
}
- if(AF_FORMAT_F == (d.format & AF_FORMAT_POINT_MASK))
- d.bps=4;
-
- return af->control(af,AF_CONTROL_FORMAT,&d);
+ if(AF_FORMAT_F == (format & AF_FORMAT_POINT_MASK))
+ bps=4;
+
+ if((AF_OK != af->control(af,AF_CONTROL_FORMAT_BPS | AF_CONTROL_SET,&bps)) ||
+ (AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format)))
+ return AF_ERROR;
+ return AF_OK;
}
- case AF_CONTROL_FORMAT:
+ case AF_CONTROL_FORMAT_BPS | AF_CONTROL_SET:
// Reinit must be called after this function has been called
// Check for errors in configuraton
- if(AF_OK != check_sanity((af_data_t*)arg))
+ if(AF_OK != check_bps(*(int*)arg))
+ return AF_ERROR;
+
+ af->data->bps = *(int*)arg;
+ return AF_OK;
+ case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET:
+ // Reinit must be called after this function has been called
+
+ // Check for errors in configuraton
+ if(AF_OK != check_format(*(int*)arg))
return AF_ERROR;
- af->data->format = ((af_data_t*)arg)->format;
- af->data->bps=((af_data_t*)arg)->bps;
+ af->data->format = *(int*)arg;
return AF_OK;
}
return AF_UNKNOWN;
diff --git a/libaf/af_gate.c b/libaf/af_gate.c
new file mode 100644
index 0000000000..033c8603df
--- /dev/null
+++ b/libaf/af_gate.c
@@ -0,0 +1,157 @@
+/*=============================================================================
+//
+// This software has been released under the terms of the GNU Public
+// license. See http://www.gnu.org/copyleft/gpl.html for details.
+//
+// Copyright 2002 Anders Johansson ajh@atri.curtin.edu.au
+//
+//=============================================================================
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+typedef struct af_gate_s
+{
+ int enable[AF_NCH]; // Enable/disable / channel
+ float time[AF_NCH]; // Forgetting factor for power estimate
+ float pow[AF_NCH]; // Estimated power level [dB]
+ float tresh[AF_NCH]; // Threshold [dB]
+ float attack[AF_NCH]; // Attack time [ms]
+ float release[AF_NCH]; // Release time [ms]
+ float range[AF_NCH]; // Range level [dB]
+}af_gate_t;
+
+// Initialization and runtime control
+static int control(struct af_instance_s* af, int cmd, void* arg)
+{
+ af_gate_t* s = (af_gate_t*)af->setup;
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((af_data_t*)arg)->rate;
+ af->data->nch = ((af_data_t*)arg)->nch;
+ af->data->format = AF_FORMAT_F | AF_FORMAT_NE;
+ af->data->bps = 4;
+
+ // Time constant set to 0.1s
+ // s->alpha = (1.0/0.2)/(2.0*M_PI*(float)((af_data_t*)arg)->rate);
+ return af_test_output(af,(af_data_t*)arg);
+ case AF_CONTROL_COMMAND_LINE:{
+/* float v=-10.0; */
+/* float vol[AF_NCH]; */
+/* float s=0.0; */
+/* float clipp[AF_NCH]; */
+/* int i; */
+/* sscanf((char*)arg,"%f:%f", &v, &s); */
+/* for(i=0;i<AF_NCH;i++){ */
+/* vol[i]=v; */
+/* clipp[i]=s; */
+/* } */
+/* if(AF_OK != control(af,AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_SET, clipp)) */
+/* return AF_ERROR; */
+/* return control(af,AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vol); */
+ }
+
+ case AF_CONTROL_GATE_ON_OFF | AF_CONTROL_SET:
+ memcpy(s->enable,(int*)arg,AF_NCH*sizeof(int));
+ return AF_OK;
+ case AF_CONTROL_GATE_ON_OFF | AF_CONTROL_GET:
+ memcpy((int*)arg,s->enable,AF_NCH*sizeof(int));
+ return AF_OK;
+ case AF_CONTROL_GATE_THRESH | AF_CONTROL_SET:
+ return af_from_dB(AF_NCH,(float*)arg,s->tresh,20.0,-60.0,-1.0);
+ case AF_CONTROL_GATE_THRESH | AF_CONTROL_GET:
+ return af_to_dB(AF_NCH,s->tresh,(float*)arg,10.0);
+ case AF_CONTROL_GATE_ATTACK | AF_CONTROL_SET:
+ return af_from_ms(AF_NCH,(float*)arg,s->attack,af->data->rate,500.0,0.1);
+ case AF_CONTROL_GATE_ATTACK | AF_CONTROL_GET:
+ return af_to_ms(AF_NCH,s->attack,(float*)arg,af->data->rate);
+ case AF_CONTROL_GATE_RELEASE | AF_CONTROL_SET:
+ return af_from_ms(AF_NCH,(float*)arg,s->release,af->data->rate,3000.0,10.0);
+ case AF_CONTROL_GATE_RELEASE | AF_CONTROL_GET:
+ return af_to_ms(AF_NCH,s->release,(float*)arg,af->data->rate);
+ case AF_CONTROL_GATE_RANGE | AF_CONTROL_SET:
+ return af_from_dB(AF_NCH,(float*)arg,s->range,20.0,100.0,0.0);
+ case AF_CONTROL_GATE_RANGE | AF_CONTROL_GET:
+ return af_to_dB(AF_NCH,s->range,(float*)arg,10.0);
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance_s* af)
+{
+ if(af->data)
+ free(af->data);
+ if(af->setup)
+ free(af->setup);
+}
+
+// Filter data through filter
+static af_data_t* play(struct af_instance_s* af, af_data_t* data)
+{
+ af_data_t* c = data; // Current working data
+ af_gate_t* s = (af_gate_t*)af->setup; // Setup for this instance
+ float* a = (float*)c->audio; // Audio data
+ int len = c->len/4; // Number of samples
+ int ch = 0; // Channel counter
+ register int nch = c->nch; // Number of channels
+ register int i = 0;
+
+
+ // Noise gate
+ for(ch = 0; ch < nch ; ch++){
+ if(s->enable[ch]){
+ float t = 1.0 - s->time[ch];
+ for(i=ch;i<len;i+=nch){
+ register float x = a[i];
+ register float pow = x*x;
+ s->pow[ch] = t*s->pow[ch] +
+ pow*s->time[ch]; // LP filter
+ if(pow < s->pow[ch]){
+ ;
+ }
+ else{
+ ;
+ }
+ a[i] = x;
+ }
+ }
+ }
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int open(af_instance_t* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul.n=1;
+ af->mul.d=1;
+ af->data=calloc(1,sizeof(af_data_t));
+ af->setup=calloc(1,sizeof(af_gate_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ return AF_OK;
+}
+
+// Description of this filter
+af_info_t af_info_gate = {
+ "Noise gate audio filter",
+ "gate",
+ "Anders",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ open
+};
diff --git a/libaf/af_pan.c b/libaf/af_pan.c
new file mode 100644
index 0000000000..49e9e4cb2e
--- /dev/null
+++ b/libaf/af_pan.c
@@ -0,0 +1,184 @@
+/*=============================================================================
+//
+// This software has been released under the terms of the GNU Public
+// license. See http://www.gnu.org/copyleft/gpl.html for details.
+//
+// Copyright 2002 Anders Johansson ajh@atri.curtin.edu.au
+//
+//=============================================================================
+*/
+
+/* */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+typedef struct af_pan_s
+{
+ float level[AF_NCH][AF_NCH]; // Gain level for each channel
+}af_pan_t;
+
+// Initialization and runtime control
+static int control(struct af_instance_s* af, int cmd, void* arg)
+{
+ af_pan_t* s = af->setup;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((af_data_t*)arg)->rate;
+ af->data->format = AF_FORMAT_F | AF_FORMAT_NE;
+ af->data->bps = 4;
+ af->mul.n = af->data->nch;
+ af->mul.d = ((af_data_t*)arg)->nch;
+
+ if((af->data->format != ((af_data_t*)arg)->format) ||
+ (af->data->bps != ((af_data_t*)arg)->bps)){
+ ((af_data_t*)arg)->format = af->data->format;
+ ((af_data_t*)arg)->bps = af->data->bps;
+ return AF_FALSE;
+ }
+ return control(af,AF_CONTROL_PAN_NOUT | AF_CONTROL_SET, &af->data->nch);
+ case AF_CONTROL_COMMAND_LINE:{
+ int nch = 0;
+ int n = 0;
+ char* cp = NULL;
+ int j,k;
+ // Read number of outputs
+ sscanf((char*)arg,"%i%n", &nch,&n);
+ if(AF_OK != control(af,AF_CONTROL_PAN_NOUT | AF_CONTROL_SET, &nch))
+ return AF_ERROR;
+
+ // Read pan values
+ cp = &((char*)arg)[n];
+ j = 0; k = 0;
+ while((*cp == ':') && (k < AF_NCH)){
+ sscanf(cp, ":%f%n" , &s->level[k][j], &n);
+ s->level[k][j] = clamp(s->level[k][j],0.0,1.0);
+ af_msg(AF_MSG_VERBOSE,"[pan] Pan level from channel %i to"
+ " channel %i = %f\n",j,k,s->level[k][j]);
+ cp =&cp[n];
+ j++;
+ if(j>=nch){
+ j = 0;
+ k++;
+ }
+ }
+ return AF_OK;
+ }
+ case AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET:{
+ int i;
+ int ch = ((af_control_ext_t*)arg)->ch;
+ float* level = ((af_control_ext_t*)arg)->arg;
+ for(i=0;i<AF_NCH;i++)
+ s->level[ch][i] = clamp(level[i],0.0,1.0);
+ return AF_OK;
+ }
+ case AF_CONTROL_PAN_LEVEL | AF_CONTROL_GET:{
+ int i;
+ int ch = ((af_control_ext_t*)arg)->ch;
+ float* level = ((af_control_ext_t*)arg)->arg;
+ for(i=0;i<AF_NCH;i++)
+ level[i] = s->level[ch][i];
+ return AF_OK;
+ }
+ case AF_CONTROL_PAN_NOUT | AF_CONTROL_SET:
+ // Reinit must be called after this function has been called
+
+ // Sanity check
+ if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > 6){
+ af_msg(AF_MSG_ERROR,"[pan] The number of output channels must be"
+ " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
+ return AF_ERROR;
+ }
+ af->data->nch=((int*)arg)[0];
+ return AF_OK;
+ case AF_CONTROL_PAN_NOUT | AF_CONTROL_GET:
+ *(int*)arg = af->data->nch;
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance_s* af)
+{
+ if(af->data)
+ free(af->data);
+ if(af->setup)
+ free(af->setup);
+}
+
+// Filter data through filter
+static af_data_t* play(struct af_instance_s* af, af_data_t* data)
+{
+ af_data_t* c = data; // Current working data
+ af_data_t* l = af->data; // Local data
+ af_pan_t* s = af->setup; // Setup for this instance
+ float* in = c->audio; // Input audio data
+ float* out = NULL; // Output audio data
+ float* end = in+c->len/4; // End of loop
+ int nchi = c->nch; // Number of input channels
+ int ncho = l->nch; // Number of output channels
+ register int j,k;
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
+ return NULL;
+
+ out = l->audio;
+ // Execute panning
+ // FIXME: Too slow
+ while(in < end){
+ for(j=0;j<ncho;j++){
+ register float x = 0.0;
+ register float* tin = in;
+ for(k=0;k<nchi;k++)
+ x += tin[k] * s->level[j][k];
+ out[j] = x;
+ }
+ out+= ncho;
+ in+= nchi;
+ }
+
+ // Set output data
+ c->audio = l->audio;
+ c->len = (c->len*af->mul.n)/af->mul.d;
+ c->nch = l->nch;
+
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int open(af_instance_t* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul.n=1;
+ af->mul.d=1;
+ af->data=calloc(1,sizeof(af_data_t));
+ af->setup=calloc(1,sizeof(af_pan_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ // Set initial pan to pass-through.
+ return AF_OK;
+}
+
+// Description of this filter
+af_info_t af_info_pan = {
+ "Panning audio filter",
+ "pan",
+ "Anders",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ open
+};
diff --git a/libaf/af_resample.c b/libaf/af_resample.c
index c8067ddc57..99353f971a 100644
--- a/libaf/af_resample.c
+++ b/libaf/af_resample.c
@@ -25,45 +25,36 @@
slow and to 16 if the machine is fast and has MMX.
*/
-#if !defined(HAVE_SSE) && !defined(HAVE_3DNOW) // This machine is slow
+#if !defined(HAVE_MMX) // This machine is slow
+#define L8
+#else
+#define L16
+#endif
-#define L 8 // Filter length
-// Unrolled loop to speed up execution
-#define FIR(x,w,y) \
- (y[0]) = ( w[0]*x[0]+w[1]*x[1]+w[2]*x[2]+w[3]*x[3] \
- + w[4]*x[4]+w[5]*x[5]+w[6]*x[6]+w[7]*x[7] ) >> 16
-
-#else /* Fast machine */
-
-#define L 16
-// Unrolled loop to speed up execution
-#define FIR(x,w,y) \
- y[0] = ( w[0] *x[0] +w[1] *x[1] +w[2] *x[2] +w[3] *x[3] \
- + w[4] *x[4] +w[5] *x[5] +w[6] *x[6] +w[7] *x[7] \
- + w[8] *x[8] +w[9] *x[9] +w[10]*x[10]+w[11]*x[11] \
- + w[12]*x[12]+w[13]*x[13]+w[14]*x[14]+w[15]*x[15] ) >> 16
-
-#endif /* Fast machine */
-
-// Macro to add data to circular que
-#define ADDQUE(xi,xq,in)\
- xq[xi]=xq[xi+L]=(*in);\
- xi=(xi-1)&(L-1);
+#include "af_resample.h"
+// Filtering types
+#define TYPE_LIN 0 // Linear interpolation
+#define TYPE_INT 1 // 16 bit integer
+#define TYPE_FLOAT 2 // 32 bit floating point
+// Accuracy for linear interpolation
+#define STEPACCURACY 32
// local data
typedef struct af_resample_s
{
- int16_t* w; // Current filter weights
- int16_t** xq; // Circular buffers
+ void* w; // Current filter weights
+ void** xq; // Circular buffers
uint32_t xi; // Index for circular buffers
uint32_t wi; // Index for w
- uint32_t i; // Number of new samples to put in x queue
+ uint32_t i; // Number of new samples to put in x queue
uint32_t dn; // Down sampling factor
uint32_t up; // Up sampling factor
+ uint64_t step; // Step size for linear interpolation
+ uint64_t pt; // Pointer remainder for linear interpolation
int sloppy; // Enable sloppy resampling to reduce memory usage
- int fast; // Enable linear interpolation instead of filtering
+ int type; // Filter type
} af_resample_t;
// Euclids algorithm for calculating Greatest Common Divisor GCD(a,b)
@@ -82,96 +73,50 @@ static inline int gcd(register int a, register int b)
return b;
}
-static int upsample(af_data_t* c,af_data_t* l, af_resample_t* s)
+// Fast linear interpolation resample with modest audio quality
+static int linint(af_data_t* c,af_data_t* l, af_resample_t* s)
{
- uint32_t ci = l->nch; // Index for channels
- uint32_t len = 0; // Number of input samples
- uint32_t nch = l->nch; // Number of channels
- uint32_t inc = s->up/s->dn;
- uint32_t level = s->up%s->dn;
- uint32_t up = s->up;
- uint32_t dn = s->dn;
-
- register int16_t* w = s->w;
- register uint32_t wi = 0;
- register uint32_t xi = 0;
-
- // Index current channel
- while(ci--){
- // Temporary pointers
- register int16_t* x = s->xq[ci];
- register int16_t* in = ((int16_t*)c->audio)+ci;
- register int16_t* out = ((int16_t*)l->audio)+ci;
- int16_t* end = in+c->len/2; // Block loop end
- wi = s->wi; xi = s->xi;
-
- while(in < end){
- register uint32_t i = inc;
- if(wi<level) i++;
-
- ADDQUE(xi,x,in);
- in+=nch;
- while(i--){
- // Run the FIR filter
- FIR((&x[xi]),(&w[wi*L]),out);
- len++; out+=nch;
- // Update wi to point at the correct polyphase component
- wi=(wi+dn)%up;
- }
- }
- }
- // Save values that needs to be kept for next time
- s->wi = wi;
- s->xi = xi;
- return len;
-}
-
-static int downsample(af_data_t* c,af_data_t* l, af_resample_t* s)
-{
- uint32_t ci = l->nch; // Index for channels
- uint32_t len = 0; // Number of output samples
- uint32_t nch = l->nch; // Number of channels
- uint32_t inc = s->dn/s->up;
- uint32_t level = s->dn%s->up;
- uint32_t up = s->up;
- uint32_t dn = s->dn;
-
- register int32_t i = 0;
- register uint32_t wi = 0;
- register uint32_t xi = 0;
+ uint32_t len = 0; // Number of input samples
+ uint32_t nch = l->nch; // Words pre transfer
+ uint64_t step = s->step;
+ int16_t* in16 = ((int16_t*)c->audio);
+ int16_t* out16 = ((int16_t*)l->audio);
+ int32_t* in32 = ((int32_t*)c->audio);
+ int32_t* out32 = ((int32_t*)l->audio);
+ uint64_t end = ((((uint64_t)c->len)/2LL)<<STEPACCURACY);
+ uint64_t pt = s->pt;
+ uint16_t tmp;
- // Index current channel
- while(ci--){
- // Temporary pointers
- register int16_t* x = s->xq[ci];
- register int16_t* in = ((int16_t*)c->audio)+ci;
- register int16_t* out = ((int16_t*)l->audio)+ci;
- register int16_t* end = in+c->len/2; // Block loop end
- i = s->i; wi = s->wi; xi = s->xi;
-
- while(in < end){
-
- ADDQUE(xi,x,in);
- in+=nch;
- if((--i)<=0){
- // Run the FIR filter
- FIR((&x[xi]),(&s->w[wi*L]),out);
- len++; out+=nch;
-
- // Update wi to point at the correct polyphase component
- wi=(wi+dn)%up;
-
- // Insert i number of new samples in queue
- i = inc;
- if(wi<level) i++;
- }
+ switch (nch){
+ case 1:
+ while(pt < end){
+ out16[len++]=in16[pt>>STEPACCURACY];
+ pt+=step;
+ }
+ s->pt=pt & ((1LL<<STEPACCURACY)-1);
+ break;
+ case 2:
+ end/=2;
+ while(pt < end){
+ out32[len++]=in32[pt>>STEPACCURACY];
+ pt+=step;
}
+ len=(len<<1);
+ s->pt=pt & ((1LL<<STEPACCURACY)-1);
+ break;
+ default:
+ end /=nch;
+ while(pt < end){
+ tmp=nch;
+ do {
+ tmp--;
+ out16[len+tmp]=in16[tmp+(pt>>STEPACCURACY)*nch];
+ } while (tmp);
+ len+=nch;
+ pt+=step;
+ }
+ s->pt=pt & ((1LL<<STEPACCURACY)-1);
}
- // Save values that needs to be kept for next time
- s->wi = wi;
- s->xi = xi;
- s->i = i;
-
return len;
}
@@ -184,13 +129,24 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
af_data_t* n = (af_data_t*)arg; // New configureation
int i,d = 0;
int rv = AF_OK;
+ size_t tsz = (s->type==TYPE_INT) ? sizeof(int16_t) : sizeof(float);
// Make sure this filter isn't redundant
if(af->data->rate == n->rate)
return AF_DETACH;
+ // If linear interpolation
+ if(s->type == TYPE_LIN){
+ s->pt=0LL;
+ s->step=((uint64_t)n->rate<<STEPACCURACY)/(uint64_t)af->data->rate+1LL;
+ af_msg(AF_MSG_VERBOSE,"[resample] Linear interpolation step: 0x%016X.\n",
+ s->step);
+ af->mul.n = af->data->rate;
+ af->mul.d = n->rate;
+ }
+
// Create space for circular bufers (if nesessary)
- if(af->data->nch != n->nch){
+ if((af->data->nch != n->nch) && (s->type != TYPE_LIN)){
// First free the old ones
if(s->xq){
for(i=1;i<af->data->nch;i++)
@@ -199,20 +155,30 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
free(s->xq);
}
// ... then create new
- s->xq = malloc(n->nch*sizeof(int16_t*));
+ s->xq = malloc(n->nch*sizeof(void*));
for(i=0;i<n->nch;i++)
- s->xq[i] = malloc(2*L*sizeof(int16_t));
+ s->xq[i] = malloc(2*L*tsz);
s->xi = 0;
}
// Set parameters
af->data->nch = n->nch;
- af->data->format = AF_FORMAT_NE | AF_FORMAT_SI;
- af->data->bps = 2;
+ if(s->type == TYPE_INT || s->type == TYPE_LIN){
+ af->data->format = AF_FORMAT_NE | AF_FORMAT_SI;
+ af->data->bps = 2;
+ }
+ else{
+ af->data->format = AF_FORMAT_NE | AF_FORMAT_F;
+ af->data->bps = 4;
+ }
if(af->data->format != n->format || af->data->bps != n->bps)
rv = AF_FALSE;
- n->format = AF_FORMAT_NE | AF_FORMAT_SI;
- n->bps = 2;
+ n->format = af->data->format;
+ n->bps = af->data->bps;
+
+ // If linear interpolation is used the setup is done.
+ if(s->type == TYPE_LIN)
+ return rv;
// Calculate up and down sampling factors
d=gcd(af->data->rate,n->rate);
@@ -244,7 +210,7 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
w = malloc(sizeof(float) * s->up *L);
if(NULL != s->w)
free(s->w);
- s->w = malloc(L*s->up*sizeof(int16_t));
+ s->w = malloc(L*s->up*tsz);
// Design prototype filter type using Kaiser window with beta = 10
if(NULL == w || NULL == s->w ||
@@ -256,13 +222,18 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
wt=w;
for(j=0;j<L;j++){//Columns
for(i=0;i<s->up;i++){//Rows
- float t=(float)s->up*32767.0*(*wt);
- s->w[i*L+j] = (int16_t)((t>=0.0)?(t+0.5):(t-0.5));
+ if(s->type == TYPE_INT){
+ float t=(float)s->up*32767.0*(*wt);
+ ((int16_t*)s->w)[i*L+j] = (int16_t)((t>=0.0)?(t+0.5):(t-0.5));
+ }
+ else
+ ((float*)s->w)[i*L+j] = (float)s->up*(*wt);
wt++;
}
}
free(w);
- af_msg(AF_MSG_VERBOSE,"[resample] New filter designed up: %i down: %i\n", s->up, s->dn);
+ af_msg(AF_MSG_VERBOSE,"[resample] New filter designed up: %i "
+ "down: %i\n", s->up, s->dn);
}
// Set multiplier and delay
@@ -274,20 +245,30 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
case AF_CONTROL_COMMAND_LINE:{
af_resample_t* s = (af_resample_t*)af->setup;
int rate=0;
- sscanf((char*)arg,"%i:%i:%i",&rate,&(s->sloppy), &(s->fast));
- return af->control(af,AF_CONTROL_RESAMPLE,&rate);
+ int lin=0;
+ sscanf((char*)arg,"%i:%i:%i", &rate, &(s->sloppy), &lin);
+ if(lin)
+ s->type = TYPE_LIN;
+ return af->control(af,AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET, &rate);
}
- case AF_CONTROL_RESAMPLE:
+ case AF_CONTROL_POST_CREATE:
+ ((af_resample_t*)af->setup)->type =
+ ((af_cfg_t*)arg)->force == AF_INIT_SLOW ? TYPE_INT : TYPE_FLOAT;
+ return AF_OK;
+ case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET:
// Reinit must be called after this function has been called
// Sanity check
if(((int*)arg)[0] < 8000 || ((int*)arg)[0] > 192000){
- af_msg(AF_MSG_ERROR,"[resample] The output sample frequency must be between 8kHz and 192kHz. Current value is %i \n",((int*)arg)[0]);
+ af_msg(AF_MSG_ERROR,"[resample] The output sample frequency "
+ "must be between 8kHz and 192kHz. Current value is %i \n",
+ ((int*)arg)[0]);
return AF_ERROR;
}
af->data->rate=((int*)arg)[0];
- af_msg(AF_MSG_VERBOSE,"[resample] Changing sample rate to %iHz\n",af->data->rate);
+ af_msg(AF_MSG_VERBOSE,"[resample] Changing sample rate "
+ "to %iHz\n",af->data->rate);
return AF_OK;
}
return AF_UNKNOWN;
@@ -312,14 +293,42 @@ static af_data_t* play(struct af_instance_s* af, af_data_t* data)
return NULL;
// Run resampling
- if(s->up>s->dn)
- len = upsample(c,l,s);
- else
- len = downsample(c,l,s);
+ switch(s->type){
+ case(TYPE_INT):
+# define FORMAT_I 1
+ if(s->up>s->dn){
+# define UP
+# include "af_resample.h"
+# undef UP
+ }
+ else{
+# define DN
+# include "af_resample.h"
+# undef DN
+ }
+ break;
+ case(TYPE_FLOAT):
+# undef FORMAT_I
+# define FORMAT_F 1
+ if(s->up>s->dn){
+# define UP
+# include "af_resample.h"
+# undef UP
+ }
+ else{
+# define DN
+# include "af_resample.h"
+# undef DN
+ }
+ break;
+ case(TYPE_LIN):
+ len = linint(c, l, s);
+ break;
+ }
// Set output data
c->audio = l->audio;
- c->len = len*2;
+ c->len = len*l->bps;
c->rate = l->rate;
return c;
diff --git a/libaf/af_resample.h b/libaf/af_resample.h
new file mode 100644
index 0000000000..4f373e2c06
--- /dev/null
+++ b/libaf/af_resample.h
@@ -0,0 +1,161 @@
+/*=============================================================================
+//
+// This software has been released under the terms of the GNU Public
+// license. See http://www.gnu.org/copyleft/gpl.html for details.
+//
+// Copyright 2002 Anders Johansson ajh@atri.curtin.edu.au
+//
+//=============================================================================
+*/
+
+/* This file contains the resampling engine, the sample format is
+ controlled by the FORMAT parameter, the filter length by the L
+ parameter and the resampling type by UP and DN. This file should
+ only be included by af_resample.c
+*/
+
+#undef L
+#undef SHIFT
+#undef FORMAT
+#undef FIR
+#undef ADDQUE
+
+/* The lenght Lxx definition selects the length of each poly phase
+ component. Valid definitions are L8 and L16 where the number
+ defines the nuber of taps. This definition affects the
+ computational complexity, the performance and the memory usage.
+*/
+
+/* The FORMAT_x parameter selects the sample format type currently
+ float and int16 are supported. Thes two formats are selected by
+ defining eiter FORMAT_F or FORMAT_I. The advantage of using float
+ is that the amplitude and therefore the SNR isn't affected by the
+ filtering, the disadvantage is that it is a lot slower.
+*/
+
+#if defined(FORMAT_I)
+#define SHIFT >>16
+#define FORMAT int16_t
+#else
+#define SHIFT
+#define FORMAT float
+#endif
+
+// Short filter
+#if defined(L8)
+
+#define L 8 // Filter length
+// Unrolled loop to speed up execution
+#define FIR(x,w,y) \
+ (y[0]) = ( w[0]*x[0]+w[1]*x[1]+w[2]*x[2]+w[3]*x[3] \
+ + w[4]*x[4]+w[5]*x[5]+w[6]*x[6]+w[7]*x[7] ) SHIFT
+
+
+
+#else /* L8/L16 */
+
+#define L 16
+// Unrolled loop to speed up execution
+#define FIR(x,w,y) \
+ y[0] = ( w[0] *x[0] +w[1] *x[1] +w[2] *x[2] +w[3] *x[3] \
+ + w[4] *x[4] +w[5] *x[5] +w[6] *x[6] +w[7] *x[7] \
+ + w[8] *x[8] +w[9] *x[9] +w[10]*x[10]+w[11]*x[11] \
+ + w[12]*x[12]+w[13]*x[13]+w[14]*x[14]+w[15]*x[15] ) SHIFT
+
+#endif /* L8/L16 */
+
+// Macro to add data to circular que
+#define ADDQUE(xi,xq,in)\
+ xq[xi]=xq[xi+L]=(*in);\
+ xi=(xi-1)&(L-1);
+
+#if defined(UP)
+
+ uint32_t ci = l->nch; // Index for channels
+ uint32_t nch = l->nch; // Number of channels
+ uint32_t inc = s->up/s->dn;
+ uint32_t level = s->up%s->dn;
+ uint32_t up = s->up;
+ uint32_t dn = s->dn;
+ uint32_t ns = c->len/l->bps;
+ register FORMAT* w = s->w;
+
+ register uint32_t wi = 0;
+ register uint32_t xi = 0;
+
+ // Index current channel
+ while(ci--){
+ // Temporary pointers
+ register FORMAT* x = s->xq[ci];
+ register FORMAT* in = ((FORMAT*)c->audio)+ci;
+ register FORMAT* out = ((FORMAT*)l->audio)+ci;
+ FORMAT* end = in+ns; // Block loop end
+ wi = s->wi; xi = s->xi;
+
+ while(in < end){
+ register uint32_t i = inc;
+ if(wi<level) i++;
+
+ ADDQUE(xi,x,in);
+ in+=nch;
+ while(i--){
+ // Run the FIR filter
+ FIR((&x[xi]),(&w[wi*L]),out);
+ len++; out+=nch;
+ // Update wi to point at the correct polyphase component
+ wi=(wi+dn)%up;
+ }
+ }
+
+ }
+ // Save values that needs to be kept for next time
+ s->wi = wi;
+ s->xi = xi;
+#endif /* UP */
+
+#if defined(DN) /* DN */
+ uint32_t ci = l->nch; // Index for channels
+ uint32_t nch = l->nch; // Number of channels
+ uint32_t inc = s->dn/s->up;
+ uint32_t level = s->dn%s->up;
+ uint32_t up = s->up;
+ uint32_t dn = s->dn;
+ uint32_t ns = c->len/l->bps;
+ FORMAT* w = s->w;
+
+ register int32_t i = 0;
+ register uint32_t wi = 0;
+ register uint32_t xi = 0;
+
+ // Index current channel
+ while(ci--){
+ // Temporary pointers
+ register FORMAT* x = s->xq[ci];
+ register FORMAT* in = ((FORMAT*)c->audio)+ci;
+ register FORMAT* out = ((FORMAT*)l->audio)+ci;
+ register FORMAT* end = in+ns; // Block loop end
+ i = s->i; wi = s->wi; xi = s->xi;
+
+ while(in < end){
+
+ ADDQUE(xi,x,in);
+ in+=nch;
+ if((--i)<=0){
+ // Run the FIR filter
+ FIR((&x[xi]),(&w[wi*L]),out);
+ len++; out+=nch;
+
+ // Update wi to point at the correct polyphase component
+ wi=(wi+dn)%up;
+
+ // Insert i number of new samples in queue
+ i = inc;
+ if(wi<level) i++;
+ }
+ }
+ }
+ // Save values that needs to be kept for next time
+ s->wi = wi;
+ s->xi = xi;
+ s->i = i;
+#endif /* DN */
diff --git a/libaf/af_tools.c b/libaf/af_tools.c
new file mode 100644
index 0000000000..6c2111f981
--- /dev/null
+++ b/libaf/af_tools.c
@@ -0,0 +1,79 @@
+#include <math.h>
+#include <af.h>
+
+/* Convert to gain value from dB. Returns AF_OK if of and AF_ERROR if
+ fail */
+inline int af_from_dB(int n, float* in, float* out, float k, float mi, float ma)
+{
+ int i = 0;
+ // Sanity check
+ if(!in || !out)
+ return AF_ERROR;
+
+ for(i=0;i<n;i++){
+ if(in[i]<=-200)
+ out[i]=0.0;
+ else
+ out[i]=pow(10.0,clamp(in[i],mi,ma)/k);
+ }
+ return AF_OK;
+}
+
+/* Convert from gain value to dB. Returns AF_OK if of and AF_ERROR if
+ fail */
+inline int af_to_dB(int n, float* in, float* out, float k)
+{
+ int i = 0;
+ // Sanity check
+ if(!in || !out)
+ return AF_ERROR;
+
+ for(i=0;i<AF_NCH;i++){
+ if(in[i] == 0.0)
+ out[i]=-200.0;
+ else
+ out[i]=k*log10(in[i]);
+ }
+ return AF_OK;
+}
+
+/* Convert from ms to sample time*/
+inline int af_from_ms(int n, float* in, float* out, int rate, float mi, float ma)
+{
+ int i = 0;
+ // Sanity check
+ if(!in || !out)
+ return AF_ERROR;
+
+ for(i=0;i<AF_NCH;i++)
+ out[i]=clamp(in[i],ma,mi);
+
+ return AF_OK;
+}
+
+/* Convert from sample time to ms */
+inline int af_to_ms(int n, float* in, float* out, int rate)
+{
+ int i = 0;
+ // Sanity check
+ if(!in || !out)
+ return AF_ERROR;
+
+ for(i=0;i<AF_NCH;i++)
+ out[i]=in[i];
+
+ return AF_OK;
+}
+
+/* Helper function for testing the output format */
+inline int af_test_output(struct af_instance_s* af, af_data_t* out)
+{
+ if((af->data->format != out->format) ||
+ (af->data->bps != out->bps) ||
+ (af->data->rate != out->rate) ||
+ (af->data->nch != out->nch)){
+ memcpy(out,af->data,sizeof(af_data_t));
+ return AF_FALSE;
+ }
+ return AF_OK;
+}
diff --git a/libaf/af_volume.c b/libaf/af_volume.c
index 370b05662d..60355f4abb 100644
--- a/libaf/af_volume.c
+++ b/libaf/af_volume.c
@@ -1,19 +1,27 @@
+/*=============================================================================
+//
+// This software has been released under the terms of the GNU Public
+// license. See http://www.gnu.org/copyleft/gpl.html for details.
+//
+// Copyright 2002 Anders Johansson ajh@atri.curtin.edu.au
+//
+//=============================================================================
+*/
+
/* This audio filter changes the volume of the sound, and can be used
- when the mixer doesn't support the PCM channel. It can handel
+ when the mixer doesn't support the PCM channel. It can handle
between 1 and 6 channels. The volume can be adjusted between -60dB
- to +20dB and is set on a per channels basis. The volume can be
- written ad read by AF_CONTROL_VOLUME_SET and AF_CONTROL_VOLUME_GET
- respectivly.
+ to +20dB and is set on a per channels basis. The is accessed through
+ AF_CONTROL_VOLUME_LEVEL.
- The plugin has support for softclipping, it is enabled by
+ The filter has support for soft-clipping, it is enabled by
AF_CONTROL_VOLUME_SOFTCLIPP. It has also a probing feature which
can be used to measure the power in the audio stream, both an
- instantanious value and the maximum value can be probed. The
+ instantaneous value and the maximum value can be probed. The
probing is enable by AF_CONTROL_VOLUME_PROBE_ON_OFF and is done on a
per channel basis. The result from the probing is obtained using
AF_CONTROL_VOLUME_PROBE_GET and AF_CONTROL_VOLUME_PROBE_GET_MAX. The
- probed values are calculated in dB. The control of the volume can
- be turned off by the AF_CONTROL_VOLUME_ON_OFF switch.
+ probed values are calculated in dB.
*/
#include <stdio.h>
@@ -22,66 +30,22 @@
#include <unistd.h>
#include <inttypes.h>
#include <math.h>
+#include <limits.h>
#include "af.h"
-// Some limits
-#define NCH AF_NCH // Number of channels
-
-#define MIN_S16 -32650
-#define MAX_S16 32650
-
-#define MAX_VOL +40.0
-#define MIN_VOL -200.0
-
// Data for specific instances of this filter
typedef struct af_volume_s
{
- float volume[NCH]; // Volume for each channel
- float power[NCH]; // Instantaneous power in each channel
- float maxpower[NCH]; // Maximum power in each channel
- float alpha; // Forgetting factor for power estimate
- int softclip; // Soft clippng on/off
- int probe; // Probing on/off
- int onoff; // Volume control on/off
+ int enable[AF_NCH]; // Enable/disable / channel
+ float pow[AF_NCH]; // Estimated power level [dB]
+ float max[AF_NCH]; // Max Power level [dB]
+ float level[AF_NCH]; // Gain level for each channel
+ float time; // Forgetting factor for power estimate
+ int soft; // Enable/disable soft clipping
+ int fast; // Use fix-point volume control
}af_volume_t;
-/* Convert to gain value from dB. Returns AF_OK if of and AF_ERROR if
- fail */
-inline int from_dB(float* in, float* out, float k)
-{
- int i = 0;
- // Sanity check
- if(!in || !out)
- return AF_ERROR;
-
- for(i=0;i<NCH;i++){
- if(in[i]<MIN_VOL)
- out[i]=0.0;
- else
- out[i]=pow(10.0,clamp(in[i],MIN_VOL,MAX_VOL)/k);
- }
- return AF_OK;
-}
-
-/* Convert from gain value to dB. Returns AF_OK if of and AF_ERROR if
- fail */
-inline int to_dB(float* in, float* out, float k)
-{
- int i = 0;
- // Sanity check
- if(!in || !out)
- return AF_ERROR;
-
- for(i=0;i<NCH;i++){
- if(in[i] == 0.0)
- out[i]=MIN_VOL;
- else
- out[i]=k*log10(clamp(in[i],MIN_VOL,MAX_VOL));
- }
- return AF_OK;
-}
-
// Initialization and runtime control
static int control(struct af_instance_s* af, int cmd, void* arg)
{
@@ -94,48 +58,57 @@ static int control(struct af_instance_s* af, int cmd, void* arg)
af->data->rate = ((af_data_t*)arg)->rate;
af->data->nch = ((af_data_t*)arg)->nch;
- af->data->format = AF_FORMAT_SI | AF_FORMAT_LE;
- af->data->bps = 2;
- // Time constant set to 0.1s
- s->alpha = (1.0/0.2)/(2.0*M_PI*(float)((af_data_t*)arg)->rate);
-
- // Only AFMT_S16_LE is supported for now
- if(af->data->format != ((af_data_t*)arg)->format ||
- af->data->bps != ((af_data_t*)arg)->bps)
- return AF_FALSE;
- return AF_OK;
+ if(s->fast){
+ af->data->format = AF_FORMAT_SI | AF_FORMAT_NE;
+ af->data->bps = 2;
+ }
+ else{
+ // Cutoff set to 10Hz for forgetting factor
+ float x = 2.0*M_PI*15.0/(float)af->data->rate;
+ float t = 2.0-cos(x);
+ s->time = 1.0 - (t - sqrt(t*t - 1));
+ af_msg(AF_MSG_DEBUG0,"[volume] Forgetting factor = %0.5f\n",s->time);
+ af->data->format = AF_FORMAT_F | AF_FORMAT_NE;
+ af->data->bps = 4;
+ }
+ return af_test_output(af,(af_data_t*)arg);
case AF_CONTROL_COMMAND_LINE:{
float v=-10.0;
- float vol[6];
- int i;
- sscanf((char*)arg,"%f:%i:%i:%i", &v,
- &(s->softclip), &(s->probe), &(s->onoff));
- for(i=0;i<NCH;i++) vol[i]=v;
- return from_dB(vol,s->volume,20.0);
+ float vol[AF_NCH];
+ int i;
+ sscanf((char*)arg,"%f:%i", &v, &s->soft);
+ for(i=0;i<AF_NCH;i++) vol[i]=v;
+ return control(af,AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vol);
}
- case AF_CONTROL_VOLUME_SET:
- return from_dB((float*)arg,s->volume,20.0);
- case AF_CONTROL_VOLUME_GET:
- return to_dB(s->volume,(float*)arg,20.0);
- case AF_CONTROL_VOLUME_PROBE_GET:
- return to_dB(s->power,(float*)arg,10.0);
- case AF_CONTROL_VOLUME_PROBE_GET_MAX:
- return to_dB(s->maxpower,(float*)arg,10.0);
- case AF_CONTROL_VOLUME_SOFTCLIP:
- s->softclip = (int)arg;
- return AF_OK;
- case AF_CONTROL_VOLUME_PROBE_ON_OFF:
- s->probe = (int)arg;
- return AF_OK;
- case AF_CONTROL_VOLUME_ON_OFF:
- s->onoff = (int)arg;
+ case AF_CONTROL_POST_CREATE:
+ s->fast = ((af_cfg_t*)arg)->force == AF_INIT_SLOW ? 1 : 0;
return AF_OK;
+ case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_SET:
+ memcpy(s->enable,(int*)arg,AF_NCH*sizeof(int));
+ return AF_OK;
+ case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_GET:
+ memcpy((int*)arg,s->enable,AF_NCH*sizeof(int));
+ return AF_OK;
+ case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_SET:
+ s->soft = *(int*)arg;
+ return AF_OK;
+ case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_GET:
+ *(int*)arg = s->soft;
+ return AF_OK;
+ case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET:
+ return af_from_dB(AF_NCH,(float*)arg,s->level,20.0,-200.0,60.0);
+ case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET:
+ return af_to_dB(AF_NCH,s->level,(float*)arg,20.0);
+ case AF_CONTROL_VOLUME_PROBE | AF_CONTROL_GET:
+ return af_to_dB(AF_NCH,s->pow,(float*)arg,10.0);
+ case AF_CONTROL_VOLUME_PROBE_MAX | AF_CONTROL_GET:
+ return af_to_dB(AF_NCH,s->max,(float*)arg,10.0);
case AF_CONTROL_PRE_DESTROY:{
float m = 0.0;
int i;
- for(i=0;i<NCH;i++)
- m=max(m,s->maxpower[i]);
+ for(i=0;i<AF_NCH;i++)
+ m=max(m,s->max[i]);
af_msg(AF_MSG_INFO,"The maximum volume was %0.2fdB \n",10*log10(m));
return AF_OK;
}
@@ -155,57 +128,66 @@ static void uninit(struct af_instance_s* af)
// Filter data through filter
static af_data_t* play(struct af_instance_s* af, af_data_t* data)
{
- af_data_t* c = data; // Current working data
- af_volume_t* s = (af_volume_t*)af->setup; // Setup for this instance
- int16_t* a = (int16_t*)c->audio; // Audio data
- int len = c->len/2; // Number of samples
- int ch = 0; // Channel counter
- register int nch = c->nch; // Number of channels
- register int i = 0;
-
- // Probe the data stream
- if(s->probe){
+ af_data_t* c = data; // Current working data
+ af_volume_t* s = (af_volume_t*)af->setup; // Setup for this instance
+ int ch = 0; // Channel counter
+ register int nch = c->nch; // Number of channels
+ register int i = 0;
+
+ // Basic operation volume control only (used on slow machines)
+ if(af->data->format == (AF_FORMAT_SI | AF_FORMAT_NE)){
+ int16_t* a = (int16_t*)c->audio; // Audio data
+ int len = c->len/2; // Number of samples
for(ch = 0; ch < nch ; ch++){
- float alpha = s->alpha;
- float beta = 1 - alpha;
- float pow = s->power[ch];
- float maxpow = s->maxpower[ch];
- register float t = 0;
- for(i=ch;i<len;i+=nch){
- t = ((float)a[i])/32768.0;
- t *= t;
- // Check maximum power value
- if(t>maxpow)
- maxpow=t;
- // Power estimate made using first order AR model
- if(t>pow)
- pow=t;
- else
- pow = beta*pow+alpha*t;
+ if(s->enable[ch]){
+ register int vol = (int)(255.0 * s->level[ch]);
+ for(i=ch;i<len;i+=nch){
+ register int x = (a[i] * vol) >> 8;
+ a[i]=clamp(x,SHRT_MIN,SHRT_MAX);
+ }
}
- s->power[ch] = pow;
- s->maxpower[ch] = maxpow;
}
}
-
- // Change the volume.
- if(s->onoff){
- register int sc = s->softclip;
+ // Machine is fast and data is floating point
+ else if(af->data->format == (AF_FORMAT_F | AF_FORMAT_NE)){
+ float* a = (float*)c->audio; // Audio data
+ int len = c->len/4; // Number of samples
for(ch = 0; ch < nch ; ch++){
- register int vol = (int)(255.0 * s->volume[ch]);
- for(i=ch;i<len;i+=nch){
- register int x;
- x=(a[i] * vol) >> 8;
- if(sc){
- int64_t t=x*x;
- t=(t*x) >> 30;
- x = (3*x - (int)t) >> 1;
+ // Volume control (fader)
+ if(s->enable[ch]){
+ float t = 1.0 - s->time;
+ for(i=ch;i<len;i+=nch){
+ register float x = a[i];
+ register float pow = x*x;
+ // Check maximum power value
+ if(pow > s->max[ch])
+ s->max[ch] = pow;
+ // Set volume
+ x *= s->level[ch];
+ // Peak meter
+ pow = x*x;
+ if(pow > s->pow[ch])
+ s->pow[ch] = pow;
+ else
+ s->pow[ch] = t*s->pow[ch] + pow*s->time; // LP filter
+ /* Soft clipping, the sound of a dream, thanks to Jon Wattes
+ post to Musicdsp.org */
+ if(s->soft){
+ if (x >= M_PI/2)
+ x = 1.0;
+ else if(x <= -M_PI/2)
+ x = -1.0;
+ else
+ x = sin(x);
+ }
+ // Hard clipping
+ else
+ x=clamp(x,-1.0,1.0);
+ a[i] = x;
}
- a[i]=clamp(x,MIN_S16,MAX_S16);
}
}
}
-
return c;
}
@@ -222,13 +204,13 @@ static int open(af_instance_t* af){
if(af->data == NULL || af->setup == NULL)
return AF_ERROR;
/* Enable volume control and set initial volume to 0.1 this is a
- safety mesure to ensure that the user doesn't blow his
+ safety measure to ensure that the user doesn't blow his
speakers. If the user isn't happy with this he can use the
- commandline parameters to set the initial volume */
- ((af_volume_t*)af->setup)->onoff = 1;
- for(i=0;i<NCH;i++)
- ((af_volume_t*)af->setup)->volume[i]=0.1;
-
+ command-line parameters to set the initial volume */
+ for(i=0;i<AF_NCH;i++){
+ ((af_volume_t*)af->setup)->enable[i] = 1;
+ ((af_volume_t*)af->setup)->level[i] = 0.1;
+ }
return AF_OK;
}
diff --git a/libaf/control.h b/libaf/control.h
index 1e160f460f..c5468ad256 100644
--- a/libaf/control.h
+++ b/libaf/control.h
@@ -2,6 +2,57 @@
#define __af_control_h
/*********************************************
+// Control info struct.
+//
+// This struct is the argument in a info call to a filter.
+*/
+
+// Argument types
+#define AF_CONTROL_TYPE_BOOL (0x0<<0)
+#define AF_CONTROL_TYPE_CHAR (0x1<<0)
+#define AF_CONTROL_TYPE_INT (0x2<<0)
+#define AF_CONTROL_TYPE_FLOAT (0x3<<0)
+#define AF_CONTROL_TYPE_STRUCT (0x4<<0)
+#define AF_CONTROL_TYPE_SPECIAL (0x5<<0) // a pointer to a function for example
+#define AF_CONTROL_TYPE_MASK (0x7<<0)
+// Argument geometry
+#define AF_CONTROL_GEOM_SCALAR (0x0<<3)
+#define AF_CONTROL_GEOM_ARRAY (0x1<<3)
+#define AF_CONTROL_GEOM_MATRIX (0x2<<3)
+#define AF_CONTROL_GEOM_MASK (0x3<<3)
+// Argument properties
+#define AF_CONTROL_PROP_READ (0x0<<5) // The argument can be read
+#define AF_CONTROL_PROP_WRITE (0x1<<5) // The argument can be written
+#define AF_CONTROL_PROP_SAVE (0x2<<5) // Can be saved
+#define AF_CONTROL_PROP_RUNTIME (0x4<<5) // Acessable during execution
+#define AF_CONTROL_PROP_CHANNEL (0x8<<5) // Argument is set per channel
+#define AF_CONTROL_PROP_MASK (0xF<<5)
+
+typedef struct af_control_info_s{
+ int def; // Control enumrification
+ char* name; // Name of argument
+ char* info; // Description of what it does
+ int flags; // Flags as defined above
+ float max; // Max and min value
+ float min; // (only aplicable on float and int)
+ int xdim; // 1st dimension
+ int ydim; // 2nd dimension (=0 for everything except matrix)
+ size_t sz; // Size of argument in bytes
+ int ch; // Channel number (for future use)
+ void* arg; // Data (for future use)
+}af_control_info_t;
+
+
+/*********************************************
+// Extended control used with arguments that operates on only one
+// channel at the time
+*/
+typedef struct af_control_ext_s{
+ void* arg; // Argument
+ int ch; // Chanel number
+}af_control_ext_t;
+
+/*********************************************
// Control parameters
*/
@@ -11,9 +62,9 @@
filter specific calls - applies only to some filters
*/
-#define AF_CONTROL_MANDATORY_BASE 0
-#define AF_CONTROL_OPTIONAL_BASE 100
-#define AF_CONTROL_FILTER_SPECIFIC_BASE 200
+#define AF_CONTROL_MANDATORY 0x10000000
+#define AF_CONTROL_OPTIONAL 0x20000000
+#define AF_CONTROL_FILTER_SPECIFIC 0x40000000
// MANDATORY CALLS
@@ -23,66 +74,136 @@
should be returned. If the incoming and outgoing data streams are
identical the filter can return AF_DETACH. This will remove the
filter. */
-#define AF_CONTROL_REINIT 01 + AF_CONTROL_MANDATORY_BASE
+#define AF_CONTROL_REINIT 0x00000100 | AF_CONTROL_MANDATORY
// OPTIONAL CALLS
/* Called just after creation with the af_cfg for the stream in which
the filter resides as input parameter this call can be used by the
filter to initialize itself */
-#define AF_CONTROL_POST_CREATE 1 + AF_CONTROL_OPTIONAL_BASE
+#define AF_CONTROL_POST_CREATE 0x00000100 | AF_CONTROL_OPTIONAL
// Called just before destruction of a filter
-#define AF_CONTROL_PRE_DESTROY 2 + AF_CONTROL_OPTIONAL_BASE
+#define AF_CONTROL_PRE_DESTROY 0x00000200 | AF_CONTROL_OPTIONAL
/* Commandline parameters. If there were any commandline parameters
for this specific filter, they will be given as a char* in the
argument */
-#define AF_CONTROL_COMMAND_LINE 3 + AF_CONTROL_OPTIONAL_BASE
+#define AF_CONTROL_COMMAND_LINE 0x00000300 | AF_CONTROL_OPTIONAL
// FILTER SPECIFIC CALLS
+// Basic operations: These can be ored with any of the below calls
+// Set argument
+#define AF_CONTROL_SET 0x00000000
+// Get argument
+#define AF_CONTROL_GET 0x00000001
+// Get info about the control, i.e fill in everything except argument
+#define AF_CONTROL_INFO 0x00000002
+
+// Resample
+
// Set output rate in resample
-#define AF_CONTROL_RESAMPLE 1 + AF_CONTROL_FILTER_SPECIFIC_BASE
+#define AF_CONTROL_RESAMPLE_RATE 0x00000100 | AF_CONTROL_FILTER_SPECIFIC
+
+// Enable sloppy resampling
+#define AF_CONTROL_RESAMPLE_SLOPPY 0x00000200 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set resampling accuracy
+#define AF_CONTROL_RESAMPLE_ACCURACY 0x00000300 | AF_CONTROL_FILTER_SPECIFIC
+
+// Format
-// Set output format in format
-#define AF_CONTROL_FORMAT 2 + AF_CONTROL_FILTER_SPECIFIC_BASE
+// Set output format bits per sample
+#define AF_CONTROL_FORMAT_BPS 0x00000400 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set output format sample format
+#define AF_CONTROL_FORMAT_FMT 0x00000500 | AF_CONTROL_FILTER_SPECIFIC
+
+// Channels
// Set number of output channels in channels
-#define AF_CONTROL_CHANNELS 3 + AF_CONTROL_FILTER_SPECIFIC_BASE
+#define AF_CONTROL_CHANNELS 0x00000600 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set number of channel routes
+#define AF_CONTROL_CHANNELS_ROUTES 0x00000700 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set channel routing pair, arg is int[2] and ch is used
+#define AF_CONTROL_CHANNELS_ROUTING 0x00000800 | AF_CONTROL_FILTER_SPECIFIC
-// Set delay length in delay
-#define AF_CONTROL_DELAY_SET_LEN 4 + AF_CONTROL_FILTER_SPECIFIC_BASE
+// Set nuber of channel routing pairs, arg is int*
+#define AF_CONTROL_CHANNELS_NR 0x00000900 | AF_CONTROL_FILTER_SPECIFIC
+// Set make af_channels into a router
+#define AF_CONTROL_CHANNELS_ROUTER 0x00000A00 | AF_CONTROL_FILTER_SPECIFIC
+
// Volume
+// Turn volume control on and off, arg is int*
+#define AF_CONTROL_VOLUME_ON_OFF 0x00000B00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Turn soft clipping of the volume on and off, arg is binary
+#define AF_CONTROL_VOLUME_SOFTCLIP 0x00000C00 | AF_CONTROL_FILTER_SPECIFIC
+
// Set volume level, arg is a float* with the volume for all the channels
-#define AF_CONTROL_VOLUME_SET 5 + AF_CONTROL_FILTER_SPECIFIC_BASE
+#define AF_CONTROL_VOLUME_LEVEL 0x00000D00 | AF_CONTROL_FILTER_SPECIFIC
-/* Get volume level for all channels, arg is a float* that will
- contain the volume for all the channels */
-#define AF_CONTROL_VOLUME_GET 6 + AF_CONTROL_FILTER_SPECIFIC_BASE
+// Probed power level for all channels, arg is a float*
+#define AF_CONTROL_VOLUME_PROBE 0x00000E00 | AF_CONTROL_FILTER_SPECIFIC
-// Turn volume control on and off, arg is binary
-#define AF_CONTROL_VOLUME_ON_OFF 7 + AF_CONTROL_FILTER_SPECIFIC_BASE
+// Maximum probed power level for all channels, arg is a float*
+#define AF_CONTROL_VOLUME_PROBE_MAX 0x00000F00 | AF_CONTROL_FILTER_SPECIFIC
-// Turn soft clipping of the volume on and off, arg is binary
-#define AF_CONTROL_VOLUME_SOFTCLIP 8 + AF_CONTROL_FILTER_SPECIFIC_BASE
+// Compressor/expander
+
+// Turn compressor/expander on and off
+#define AF_CONTROL_COMP_ON_OFF 0x00001000 | AF_CONTROL_FILTER_SPECIFIC
+
+// Compression/expansion threshold [dB]
+#define AF_CONTROL_COMP_THRESH 0x00001100 | AF_CONTROL_FILTER_SPECIFIC
+
+// Compression/expansion attack time [ms]
+#define AF_CONTROL_COMP_ATTACK 0x00001200 | AF_CONTROL_FILTER_SPECIFIC
+
+// Compression/expansion release time [ms]
+#define AF_CONTROL_COMP_RELEASE 0x00001300 | AF_CONTROL_FILTER_SPECIFIC
+
+// Compression/expansion gain level [dB]
+#define AF_CONTROL_COMP_RATIO 0x00001400 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate
+
+// Turn noise gate on an off
+#define AF_CONTROL_GATE_ON_OFF 0x00001500 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate threshold [dB]
+#define AF_CONTROL_GATE_THRESH 0x00001600 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate attack time [ms]
+#define AF_CONTROL_GATE_ATTACK 0x00001700 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate release time [ms]
+#define AF_CONTROL_GATE_RELEASE 0x00001800 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate release range level [dB]
+#define AF_CONTROL_GATE_RANGE 0x00001900 | AF_CONTROL_FILTER_SPECIFIC
+
+// Pan
+
+// Pan levels, arg is a control_ext with a float*
+#define AF_CONTROL_PAN_LEVEL 0x00001A00 | AF_CONTROL_FILTER_SPECIFIC
-// Get the probed power level for all channels, arg is a float*
-#define AF_CONTROL_VOLUME_PROBE_GET 9 + AF_CONTROL_FILTER_SPECIFIC_BASE
+// Number of outputs from pan, arg is int*
+#define AF_CONTROL_PAN_NOUT 0x00001B00 | AF_CONTROL_FILTER_SPECIFIC
+
-// Get the maximum probed power level for all channels, arg is a float*
-#define AF_CONTROL_VOLUME_PROBE_GET_MAX 10 + AF_CONTROL_FILTER_SPECIFIC_BASE
+// Set equalizer gain, arg is a control_ext with a float*
+#define AF_CONTROL_EQUALIZER_GAIN 0x00001C00 | AF_CONTROL_FILTER_SPECIFIC
-// Turn probing on and off, arg is binary
-#define AF_CONTROL_VOLUME_PROBE_ON_OFF 11 + AF_CONTROL_FILTER_SPECIFIC_BASE
-// Set equalizer gain, arg is an equalizer_t*
-#define AF_CONTROL_EQUALIZER_SET_GAIN 12 + AF_CONTROL_FILTER_SPECIFIC_BASE
+// Set delay length in seconds
+#define AF_CONTROL_DELAY_LEN 0x00001D00 | AF_CONTROL_FILTER_SPECIFIC
-// Get equalizer gain, arg is an equalizer_t*
-#define AF_CONTROL_EQUALIZER_GET_GAIN 13 + AF_CONTROL_FILTER_SPECIFIC_BASE
#endif /*__af_control_h */