# HG changeset patch # User alex # Date 1096917065 0 # Node ID 81e62cbe57d917f1425e8a1c57c4e3053a8cbb13 # Parent 4604fc855b3ad6cfe8bf4a273646109ddd46b693 reimplementation of the pl_extrastereo and pl_volnorm plugins diff -r 4604fc855b3a -r 81e62cbe57d9 libaf/Makefile --- a/libaf/Makefile Mon Oct 04 19:07:09 2004 +0000 +++ b/libaf/Makefile Mon Oct 04 19:11:05 2004 +0000 @@ -2,7 +2,7 @@ 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 af_tools.c af_comp.c af_gate.c af_pan.c af_surround.c af_sub.c af_export.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 af_surround.c af_sub.c af_export.c af_volnorm.c af_extrastereo.c OBJS=$(SRCS:.c=.o) diff -r 4604fc855b3a -r 81e62cbe57d9 libaf/af.c --- a/libaf/af.c Mon Oct 04 19:07:09 2004 +0000 +++ b/libaf/af.c Mon Oct 04 19:11:05 2004 +0000 @@ -23,6 +23,8 @@ extern af_info_t af_info_surround; extern af_info_t af_info_sub; extern af_info_t af_info_export; +extern af_info_t af_info_volnorm; +extern af_info_t af_info_extrastereo; static af_info_t* filter_list[]={ &af_info_dummy, @@ -40,6 +42,8 @@ #ifdef HAVE_SYS_MMAN_H &af_info_export, #endif + &af_info_volnorm, + &af_info_extrastereo, NULL }; diff -r 4604fc855b3a -r 81e62cbe57d9 libaf/af_extrastereo.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/af_extrastereo.c Mon Oct 04 19:11:05 2004 +0000 @@ -0,0 +1,120 @@ +/*============================================================================= +// +// This software has been released under the terms of the GNU Public +// license. See http://www.gnu.org/copyleft/gpl.html for details. +// +// Copyright 2004 Alex Beregszaszi & Pierre Lombard +// +//============================================================================= +*/ + +#include +#include +#include + +#include +#include +#include +#include + +#include "af.h" + +// Data for specific instances of this filter +typedef struct af_extrastereo_s +{ + float mul; +}af_extrastereo_t; + +// Initialization and runtime control +static int control(struct af_instance_s* af, int cmd, void* arg) +{ + af_extrastereo_t* s = (af_extrastereo_t*)af->setup; + + switch(cmd){ + case AF_CONTROL_REINIT: + // Sanity check + if(!arg) return AF_ERROR; + + if(((af_data_t*)arg)->format != (AF_FORMAT_SI | AF_FORMAT_NE) || + (((af_data_t*)arg)->nch != 2)) + return AF_ERROR; + + af->data->rate = ((af_data_t*)arg)->rate; + af->data->nch = 2; + af->data->format = AF_FORMAT_SI | AF_FORMAT_NE; + af->data->bps = 2; + + return af_test_output(af,(af_data_t*)arg); + case AF_CONTROL_COMMAND_LINE:{ + float f; + sscanf((char*)arg,"%f", &f); + s->mul = f; + return AF_OK; + } + case AF_CONTROL_ES_MUL | AF_CONTROL_SET: + s->mul = *(float*)arg; + return AF_OK; + case AF_CONTROL_ES_MUL | AF_CONTROL_GET: + *(float*)arg = s->mul; + 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_extrastereo_t *s = af->setup; + register int i = 0; + int16_t *a = (int16_t*)data->audio; // Audio data + int len = data->len/2; // Number of samples + int avg, l, r; + + for (i = 0; i < len; i+=2) + { + avg = (a[i] + a[i + 1]) / 2; + + l = avg + (int)(s->mul * (a[i] - avg)); + r = avg + (int)(s->mul * (a[i + 1] - avg)); + + a[i] = clamp(l, SHRT_MIN, SHRT_MAX); + a[i + 1] = clamp(r, SHRT_MIN, SHRT_MAX); + } + + return data; +} + +// 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_extrastereo_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + + ((af_extrastereo_t*)af->setup)->mul = 2.5; + return AF_OK; +} + +// Description of this filter +af_info_t af_info_extrastereo = { + "Extra stereo", + "extrastereo", + "Alex Beregszaszi & Pierre Lombard", + "", + AF_FLAGS_NOT_REENTRANT, + open +}; diff -r 4604fc855b3a -r 81e62cbe57d9 libaf/af_format.h --- a/libaf/af_format.h Mon Oct 04 19:07:09 2004 +0000 +++ b/libaf/af_format.h Mon Oct 04 19:11:05 2004 +0000 @@ -18,7 +18,7 @@ #define AF_FORMAT_US (1<<1) // Un Signed #define AF_FORMAT_SIGN_MASK (1<<1) -// Fixed of floating point +// Fixed or floating point #define AF_FORMAT_I (0<<2) // Int #define AF_FORMAT_F (1<<2) // Foating point #define AF_FORMAT_POINT_MASK (1<<2) diff -r 4604fc855b3a -r 81e62cbe57d9 libaf/af_volnorm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libaf/af_volnorm.c Mon Oct 04 19:11:05 2004 +0000 @@ -0,0 +1,344 @@ +/*============================================================================= +// +// This software has been released under the terms of the GNU Public +// license. See http://www.gnu.org/copyleft/gpl.html for details. +// +// Copyright 2004 Alex Beregszaszi & Pierre Lombard +// +//============================================================================= +*/ + +#include +#include +#include + +#include +#include +#include +#include + +#include "af.h" + +// Methods: +// 1: uses a 1 value memory and coefficients new=a*old+b*cur (with a+b=1) +// 2: uses several samples to smooth the variations (standard weighted mean +// on past samples) + +// Size of the memory array +// FIXME: should depend on the frequency of the data (should be a few seconds) +#define NSAMPLES 128 + +// If summing all the mem[].len is lower than MIN_SAMPLE_SIZE bytes, then we +// choose to ignore the computed value as it's not significant enough +// FIXME: should depend on the frequency of the data (0.5s maybe) +#define MIN_SAMPLE_SIZE 32000 + +// mul is the value by which the samples are scaled +// and has to be in [MUL_MIN, MUL_MAX] +#define MUL_INIT 1.0 +#define MUL_MIN 0.1 +#define MUL_MAX 5.0 +// "Ideal" level +#define MID_S16 (SHRT_MAX * 0.25) +#define MID_FLOAT (INT_MAX * 0.25) + +// Silence level +// FIXME: should be relative to the level of the samples +#define SIL_S16 (SHRT_MAX * 0.01) +#define SIL_FLOAT (INT_MAX * 0.01) // FIXME + +// smooth must be in ]0.0, 1.0[ +#define SMOOTH_MUL 0.06 +#define SMOOTH_LASTAVG 0.06 + +// Data for specific instances of this filter +typedef struct af_volume_s +{ + int method; // method used + float mul; + // method 1 + float lastavg; // history value of the filter + // method 2 + int idx; + struct { + float avg; // average level of the sample + int len; // sample size (weight) + } mem[NSAMPLES]; +}af_volnorm_t; + +// Initialization and runtime control +static int control(struct af_instance_s* af, int cmd, void* arg) +{ + af_volnorm_t* s = (af_volnorm_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; + + if(((af_data_t*)arg)->format != (AF_FORMAT_F | AF_FORMAT_NE) && + ((af_data_t*)arg)->format != (AF_FORMAT_SI | AF_FORMAT_NE)) + return AF_ERROR; + + if(((af_data_t*)arg)->format == (AF_FORMAT_SI | AF_FORMAT_NE)){ + af->data->format = AF_FORMAT_SI | AF_FORMAT_NE; + af->data->bps = 2; + }else{ + 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:{ + int i; + sscanf((char*)arg,"%d", &i); + if (i != 1 && i != 2) + return AF_ERROR; + s->method = i-1; + 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); +} + +static void method1_int16(af_volnorm_t *s, af_data_t *c) +{ + register int i = 0; + int16_t *data = (int16_t*)c->audio; // Audio data + int len = c->len/2; // Number of samples + float curavg = 0.0, newavg, neededmul; + int tmp; + + for (i = 0; i < len; i++) + { + tmp = data[i]; + curavg += tmp * tmp; + } + curavg = sqrt(curavg / (float) len); + + // Evaluate an adequate 'mul' coefficient based on previous state, current + // samples level, etc + + if (curavg > SIL_S16) + { + neededmul = MID_S16 / (curavg * s->mul); + s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul; + + // clamp the mul coefficient + s->mul = clamp(s->mul, MUL_MIN, MUL_MAX); + } + + // Scale & clamp the samples + for (i = 0; i < len; i++) + { + tmp = s->mul * data[i]; + tmp = clamp(tmp, SHRT_MIN, SHRT_MAX); + data[i] = tmp; + } + + // Evaulation of newavg (not 100% accurate because of values clamping) + newavg = s->mul * curavg; + + // Stores computed values for future smoothing + s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg; +} + +static void method1_float(af_volnorm_t *s, af_data_t *c) +{ + register int i = 0; + float *data = (float*)c->audio; // Audio data + int len = c->len/4; // Number of samples + float curavg = 0.0, newavg, neededmul, tmp; + + for (i = 0; i < len; i++) + { + tmp = data[i]; + curavg += tmp * tmp; + } + curavg = sqrt(curavg / (float) len); + + // Evaluate an adequate 'mul' coefficient based on previous state, current + // samples level, etc + + if (curavg > SIL_FLOAT) // FIXME + { + neededmul = MID_FLOAT / (curavg * s->mul); + s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul; + + // clamp the mul coefficient + s->mul = clamp(s->mul, MUL_MIN, MUL_MAX); + } + + // Scale & clamp the samples + for (i = 0; i < len; i++) + data[i] *= s->mul; + + // Evaulation of newavg (not 100% accurate because of values clamping) + newavg = s->mul * curavg; + + // Stores computed values for future smoothing + s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg; +} + +static void method2_int16(af_volnorm_t *s, af_data_t *c) +{ + register int i = 0; + int16_t *data = (int16_t*)c->audio; // Audio data + int len = c->len/2; // Number of samples + float curavg = 0.0, newavg, avg = 0.0; + int tmp, totallen = 0; + + for (i = 0; i < len; i++) + { + tmp = data[i]; + curavg += tmp * tmp; + } + curavg = sqrt(curavg / (float) len); + + // Evaluate an adequate 'mul' coefficient based on previous state, current + // samples level, etc + for (i = 0; i < NSAMPLES; i++) + { + avg += s->mem[i].avg * (float)s->mem[i].len; + totallen += s->mem[i].len; + } + + if (totallen > MIN_SAMPLE_SIZE) + { + avg /= (float)totallen; + if (avg >= SIL_S16) + { + s->mul = MID_S16 / avg; + s->mul = clamp(s->mul, MUL_MIN, MUL_MAX); + } + } + + // Scale & clamp the samples + for (i = 0; i < len; i++) + { + tmp = s->mul * data[i]; + tmp = clamp(tmp, SHRT_MIN, SHRT_MAX); + data[i] = tmp; + } + + // Evaulation of newavg (not 100% accurate because of values clamping) + newavg = s->mul * curavg; + + // Stores computed values for future smoothing + s->mem[s->idx].len = len; + s->mem[s->idx].avg = newavg; + s->idx = (s->idx + 1) % NSAMPLES; +} + +static void method2_float(af_volnorm_t *s, af_data_t *c) +{ + register int i = 0; + float *data = (float*)c->audio; // Audio data + int len = c->len/4; // Number of samples + float curavg = 0.0, newavg, avg = 0.0, tmp; + int totallen = 0; + + for (i = 0; i < len; i++) + { + tmp = data[i]; + curavg += tmp * tmp; + } + curavg = sqrt(curavg / (float) len); + + // Evaluate an adequate 'mul' coefficient based on previous state, current + // samples level, etc + for (i = 0; i < NSAMPLES; i++) + { + avg += s->mem[i].avg * (float)s->mem[i].len; + totallen += s->mem[i].len; + } + + if (totallen > MIN_SAMPLE_SIZE) + { + avg /= (float)totallen; + if (avg >= SIL_FLOAT) + { + s->mul = MID_FLOAT / avg; + s->mul = clamp(s->mul, MUL_MIN, MUL_MAX); + } + } + + // Scale & clamp the samples + for (i = 0; i < len; i++) + data[i] *= s->mul; + + // Evaulation of newavg (not 100% accurate because of values clamping) + newavg = s->mul * curavg; + + // Stores computed values for future smoothing + s->mem[s->idx].len = len; + s->mem[s->idx].avg = newavg; + s->idx = (s->idx + 1) % NSAMPLES; +} + +// Filter data through filter +static af_data_t* play(struct af_instance_s* af, af_data_t* data) +{ + af_volnorm_t *s = af->setup; + + if(af->data->format == (AF_FORMAT_SI | AF_FORMAT_NE)) + { + if (s->method) + method2_int16(s, data); + else + method1_int16(s, data); + } + else if(af->data->format == (AF_FORMAT_F | AF_FORMAT_NE)) + { + if (s->method) + method2_float(s, data); + else + method1_float(s, data); + } + return data; +} + +// Allocate memory and set function pointers +static int open(af_instance_t* af){ + int i = 0; + 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_volnorm_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + + ((af_volnorm_t*)af->setup)->mul = MUL_INIT; + ((af_volnorm_t*)af->setup)->lastavg = MID_S16; + ((af_volnorm_t*)af->setup)->idx = 0; + for (i = 0; i < NSAMPLES; i++) + { + ((af_volnorm_t*)af->setup)->mem[i].len = 0; + ((af_volnorm_t*)af->setup)->mem[i].avg = 0; + } + return AF_OK; +} + +// Description of this filter +af_info_t af_info_volnorm = { + "Volume normalizer filter", + "volnorm", + "Alex Beregszaszi & Pierre Lombard", + "", + AF_FLAGS_NOT_REENTRANT, + open +}; diff -r 4604fc855b3a -r 81e62cbe57d9 libaf/control.h --- a/libaf/control.h Mon Oct 04 19:07:09 2004 +0000 +++ b/libaf/control.h Mon Oct 04 19:11:05 2004 +0000 @@ -218,4 +218,8 @@ // Export #define AF_CONTROL_EXPORT_SZ 0x00002000 | AF_CONTROL_FILTER_SPECIFIC + +// ExtraStereo Multiplier +#define AF_CONTROL_ES_MUL 0x00002100 | AF_CONTROL_FILTER_SPECIFIC + #endif /*__af_control_h */