diff options
author | anders <anders@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2002-12-28 13:59:53 +0000 |
---|---|---|
committer | anders <anders@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2002-12-28 13:59:53 +0000 |
commit | 6adaa78ee935ef89439d4b38550165f13e880320 (patch) | |
tree | 6612adc09121e661363b1370cb43981007d36b60 /libaf/af_volume.c | |
parent | 0e9c0e8aa2aa7df6aad5d78c4b664927a9d2421e (diff) | |
download | mpv-6adaa78ee935ef89439d4b38550165f13e880320.tar.bz2 mpv-6adaa78ee935ef89439d4b38550165f13e880320.tar.xz |
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
Diffstat (limited to 'libaf/af_volume.c')
-rw-r--r-- | libaf/af_volume.c | 266 |
1 files changed, 124 insertions, 142 deletions
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; } |