diff libaf/af_volume.c @ 8607:d6f40a06867b

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
author anders
date Sat, 28 Dec 2002 13:59:53 +0000
parents b56fd4b1738d
children 440301fef3fe
line wrap: on
line diff
--- a/libaf/af_volume.c	Sat Dec 28 13:53:31 2002 +0000
+++ b/libaf/af_volume.c	Sat Dec 28 13:59:53 2002 +0000
@@ -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 @@
     
     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;
+  case AF_CONTROL_POST_CREATE:	
+    s->fast = ((af_cfg_t*)arg)->force  == AF_INIT_SLOW ? 1 : 0;
     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;
-    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 @@
 // 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;
-      }
-      s->power[ch]    = pow;
-      s->maxpower[ch] = maxpow;
-    }
-  }
-
-  // Change the volume.
-  if(s->onoff){
-    register int sc  = s->softclip;
-    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;
+      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);
 	}
-	a[i]=clamp(x,MIN_S16,MAX_S16);
       }
     }
   }
-
+  // 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++){
+      // 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;
+	}
+      }
+    }
+  }
   return c;
 }
 
@@ -222,13 +204,13 @@
   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;
 }