gst/equalizer/gstiirequalizer.c: Replace filters with a bit better filters for which we can actually find documentati...

Original commit message from CVS:
* gst/equalizer/gstiirequalizer.c:
(gst_iir_equalizer_band_set_property),
(gst_iir_equalizer_band_get_property),
(gst_iir_equalizer_band_class_init), (arg_to_scale),
(setup_filter), (gst_iir_equalizer_compute_frequencies):
Replace filters with a bit better filters for which we can actually
find documentation, which don't change anything on zero gain, etc.
Make the frequency property of the bands writable, rename the
band-width property to bandwidth and change the	meaning to the
frequency difference between bandedges, change the meaning of the
gain property to dB instead of a weird scale between -1	and 1 that
has no real meaning.
This commit is contained in:
Sebastian Dröge 2007-10-30 21:18:45 +00:00
parent b3f1b71446
commit 7c8653f596

View file

@ -83,7 +83,7 @@ enum
{
ARG_GAIN = 1,
ARG_FREQ,
ARG_BAND_WIDTH
ARG_BANDWIDTH
};
typedef struct _GstIirEqualizerBandClass GstIirEqualizerBandClass;
@ -110,9 +110,8 @@ struct _GstIirEqualizerBand
gdouble width;
/* second order iir filter */
gdouble alpha; /* IIR coefficients for outputs */
gdouble beta; /* IIR coefficients for inputs */
gdouble gamma; /* IIR coefficients for inputs */
gdouble b1, b2; /* IIR coefficients for outputs */
gdouble a0, a1, a2; /* IIR coefficients for inputs */
};
struct _GstIirEqualizerBandClass
@ -148,7 +147,25 @@ gst_iir_equalizer_band_set_property (GObject * object, guint prop_id,
}
break;
}
case ARG_BAND_WIDTH:{
case ARG_FREQ:{
gdouble freq;
freq = g_value_get_double (value);
GST_DEBUG_OBJECT (band, "freq = %lf -> %lf", band->freq, freq);
if (freq != band->freq) {
GstIirEqualizer *equ =
GST_IIR_EQUALIZER (gst_object_get_parent (GST_OBJECT (band)));
band->freq = freq;
if (GST_AUDIO_FILTER (equ)->format.rate) {
setup_filter (equ, band);
}
gst_object_unref (equ);
GST_DEBUG_OBJECT (band, "changed freq = %lf ", band->freq);
}
break;
}
case ARG_BANDWIDTH:{
gdouble width;
width = g_value_get_double (value);
@ -185,7 +202,7 @@ gst_iir_equalizer_band_get_property (GObject * object, guint prop_id,
case ARG_FREQ:
g_value_set_double (value, band->freq);
break;
case ARG_BAND_WIDTH:
case ARG_BANDWIDTH:
g_value_set_double (value, band->width);
break;
default:
@ -204,18 +221,18 @@ gst_iir_equalizer_band_class_init (GstIirEqualizerBandClass * klass)
g_object_class_install_property (gobject_class, ARG_GAIN,
g_param_spec_double ("gain", "gain",
"gain for the frequency band ranging from -1.0 to +1.0",
-1.0, 1.0, 0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
"gain for the frequency band ranging from -24.0 dB to +12.0 dB",
-24.0, 12.0, 0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (gobject_class, ARG_FREQ,
g_param_spec_double ("freq", "freq",
"center frequency of the band",
0.0, 100000.0, 0.0, G_PARAM_READABLE));
0.0, 100000.0, 0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (gobject_class, ARG_BAND_WIDTH,
g_param_spec_double ("band-width", "band-width",
"band width calculated as distance between bands * this value", 0.1,
10.0, 1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (gobject_class, ARG_BANDWIDTH,
g_param_spec_double ("bandwidth", "bandwidth",
"difference between bandedges in Hz",
1.0, 100000.0, 1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
}
static void
@ -330,53 +347,57 @@ gst_iir_equalizer_finalize (GObject * object)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/*
* converts gain values to scale factors.
*
* we map -1 ... 1 to a db range.
* A suitable range would be -12db ... 0 ... + 6db which expressed as
* a factor is about 0.06 ... 1 ... 4.0
*
* We need to subtract one so that gain is centered around zero
*
* visualize via gnuplot:
* set xrange [-1:1]
* plot 10.0 ** (12*x/10.0)
*/
static gdouble
static inline gdouble
arg_to_scale (gdouble arg)
{
return (pow (10.0, (6.0 * fabs (arg)) / 10.0) - 1.0);
return (pow (10.0, arg / 20.0));
}
/* Filter taken from
*
* The Equivalence of Various Methods of Computing
* Biquad Coefficients for Audio Parametric Equalizers
*
* by Robert Bristow-Johnson
*
* http://www.aes.org/e-lib/browse.cfm?elib=6326
* http://www.musicdsp.org/files/EQ-Coefficients.pdf
*
* The bandwidth method that we use here is the preferred
* one from this article transformed from octaves to frequency
* in Hz.
*/
static void
setup_filter (GstIirEqualizer * equ, GstIirEqualizerBand * band)
{
g_return_if_fail (GST_AUDIO_FILTER (equ)->format.rate);
/* FIXME: we need better filters
* - the band-width control is not good
* - we need shelf-filter for 1st and last band
*/
{
gdouble gain = arg_to_scale (band->gain);
gdouble frequency = band->freq / GST_AUDIO_FILTER (equ)->format.rate;
gdouble q = pow (HIGHEST_FREQ / LOWEST_FREQ,
1.0 / (equ->freq_band_count - 1)) * band->width;
gdouble omega = 2.0 * M_PI * frequency;
gdouble bw =
2.0 * M_PI * (band->width / GST_AUDIO_FILTER (equ)->format.rate);
gdouble theta = frequency * 2 * M_PI;
gdouble edge_gain = sqrt (gain);
gdouble gamma = tan (bw / 2.0);
band->beta = (q - theta / 2) / (2 * q + theta);
band->gamma = (0.5 + band->beta) * cos (theta);
band->alpha = (0.5 - band->beta) / 2;
gdouble alpha = gamma * edge_gain;
gdouble beta = gamma / edge_gain;
band->beta *= 2.0;
band->alpha *= 2.0 * gain;
band->gamma *= 2.0;
band->a0 = (1.0 + alpha) / (1.0 + beta);
band->a1 = (-2.0 * cos (omega)) / (1.0 + beta);
band->a2 = (1.0 - alpha) / (1.0 + beta);
band->b1 = (2.0 * cos (omega)) / (1.0 + beta);
band->b2 = -(1.0 - beta) / (1.0 + beta);
GST_INFO
("gain = %7.5g, frequency = %7.5g, alpha = %7.5g, beta = %7.5g, gamma=%7.5g",
gain, frequency, band->alpha, band->beta, band->gamma);
("gain = %7.5g, , bandwidth= %7.5g, frequency = %7.5g, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
gain, band->width, frequency, band->a0, band->a1, band->a2, band->b1,
band->b2);
}
}
@ -425,11 +446,13 @@ gst_iir_equalizer_compute_frequencies (GstIirEqualizer * equ, guint new_count)
* FIXME: arg! we can't change the name of parented objects :(
* application should read band->freq to get the name
*/
step = pow (HIGHEST_FREQ / LOWEST_FREQ, 1.0 / new_count);
freq0 = LOWEST_FREQ;
for (i = 0; i < new_count; i++) {
freq1 = freq0 * step;
equ->bands[i]->freq = freq0 + ((freq1 - freq0) / 2.0);
equ->bands[i]->width = freq1 - freq0;
GST_DEBUG ("band[%2d] = '%lf'", i, equ->bands[i]->freq);
/*
if(equ->bands[i]->freq<10000.0)
@ -442,6 +465,7 @@ gst_iir_equalizer_compute_frequencies (GstIirEqualizer * equ, guint new_count)
freq0 = freq1;
}
if (GST_AUDIO_FILTER (equ)->format.rate) {
for (i = 0; i < new_count; i++) {
setup_filter (equ, equ->bands[i]);
@ -462,16 +486,16 @@ one_step_ ## TYPE (GstIirEqualizerBand *filter, \
SecondOrderHistory ## TYPE *history, TYPE input) \
{ \
/* calculate output */ \
TYPE output = filter->alpha * (input - history->x2) + \
filter->gamma * history->y1 - filter->beta * history->y2; \
TYPE output = filter->a0 * input + filter->a1 * history->x1 + \
filter->a2 * history->x2 + filter->b1 * history->y1 + \
filter->b2 * history->y2; \
/* update history */ \
history->y2 = history->y1; \
history->y1 = output; \
history->x2 = history->x1; \
history->x1 = input; \
\
/* for negative gains we subtract */ \
return (filter->gain>0.0) ? output : -output; \
return output; \
} \
\
static const guint \
@ -484,17 +508,15 @@ guint size, guint channels) \
guint frames = size / channels / sizeof (TYPE); \
guint i, c, f; \
BIG_TYPE cur; \
TYPE val; \
\
for (i = 0; i < frames; i++) { \
for (c = 0; c < channels; c++) { \
SecondOrderHistory ## TYPE *history = equ->history; \
val = *((TYPE *) data); \
cur = 0.25 * val; /* FIXME: should be without factor*/ \
cur = *((TYPE *) data); \
for (f = 0; f < equ->freq_band_count; f++) { \
GstIirEqualizerBand *filter = equ->bands[f]; \
\
cur += one_step_ ## TYPE (filter, history, val); \
cur = one_step_ ## TYPE (filter, history, cur); \
history++; \
} \
cur = CLAMP (cur, MIN_VAL, MAX_VAL); \