You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdemultimedia/arts/modules/synth/synth_std_equalizer_impl.cc

208 lines
5.0 KiB

/*
Copyright (C) 2000 Jeff Tranter
tranter@pobox.com
(C) 1999 Stefan Westerfeld
stefan@space.twc.de
(C) 1999 Martin Lorenz
lorenz@ch.tum.de
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <math.h>
#include "artsmodulessynth.h"
#include "stdsynthmodule.h"
using namespace Arts;
class Synth_STD_EQUALIZER_impl : virtual public Synth_STD_EQUALIZER_skel,
virtual public StdSynthModule
{
protected:
float _low, _mid, _high, _frequency, _q;
float tlow, tmid, thigh, tfrequency;
float a1, a2, b0, b1, b2, x_0, x_1, x_2, y_1, y_2;
unsigned long all;
public:
float low() { return _low; }
void low(float newLow)
{
if(newLow != _low)
{
_low = newLow;
calcParameters();
high_changed(newLow);
}
}
float mid() { return _mid; }
void mid(float newMid)
{
if(newMid != _mid)
{
_mid = newMid;
calcParameters();
mid_changed(newMid);
}
}
float high() { return _high; }
void high(float newHigh)
{
if(newHigh != _high)
{
_high = newHigh;
calcParameters();
high_changed(newHigh);
}
}
float frequency() { return _frequency; }
void frequency(float newFrequency)
{
if(newFrequency != _frequency)
{
_frequency = newFrequency;
calcParameters();
frequency_changed(newFrequency);
}
}
float q() { return _q; }
void q(float newQ)
{
if(newQ != _q)
{
_q = newQ;
calcParameters();
q_changed(newQ);
}
}
Synth_STD_EQUALIZER_impl() {
_low = _mid = _high = 0; _q = 0.5;
_frequency = 300;
}
void calcParameters()
{
/*
* _low, _mid, _high are in dB, transform them to tlow, tmid,
* thigh using:
* -6dB => 0.5 ; 0dB => 1 ; 6dB = 2.0 ; ...
*/
tlow = exp(_low * 0.115524530093324); // exp(p[LOW]*ln(2)/6)
tmid = exp(_mid * 0.115524530093324);
thigh = exp(_high * 0.115524530093324);
// _frequency is given in Hz, we need the w-value (and do clipping if
// it exceeds SR/2)
const float SAMPLING_RATE = 44100.0;
tfrequency = _frequency;
if (tfrequency > SAMPLING_RATE / 2.01)
tfrequency = SAMPLING_RATE / 2.01;
float w = 2 * M_PI * tfrequency / SAMPLING_RATE;
// Calculations:
float t = 1/tan(w/2);
float tq = t/_q;
float t2 = t*t;
float a0 = 1+tq+t2;
float a0r = 1/a0;
// and now the real filter values:
a1 = (2 - 2 * t2) * a0r;
a2 = (1 - tq + t2) * a0r;
b0 = (tlow + tmid * tq + thigh * t2) * a0r;
b1 = (2 * tlow -2 * thigh * t2) * a0r;
b2 = (tlow - tmid * tq + thigh * t2) * a0r;
// TODO: try if we need that here, or if we can change filter
// coefficients without setting the state to 0
x_0 = x_1 = x_2 = y_1 = y_2 = 0.0;
all = 0;
}
void streamInit()
{
calcParameters();
}
void calculateBlock(unsigned long samples)
{
all += samples;
if (all > 1024)
{
/* The _problem_: (observed on a PII-350)
*
* I am not quite sure what happens here, but it seems to be like that:
*
* If an ordinary signal (a mp3 for instance) is sent through the
* equalizer, and then no more input is given (zeros as input),
* the y_1 and y_2 values oscillate for some time, coming closer and
* close to zero.
*
* But before the reach zero, they reach the smallest negative number
* (or smallest positive, or whatever), and stay there
* (because 0.005*smallest_negative will remain smallest_negative).
*
* Since then, the CPU usage for all operations on these floats
* increases, (since handling of smallest_negative seems to be a rare
* case).
*
* The _fix_:
*
* We observe the value of y_1. If it's very close to zero (may be as
* well smallest_positive/smallest_negative), we set it to zero,
* together with y_2. This shouldn't significantly influence
* correctness of the filter, but effectively solves the problem.
*
* If you don't believe me, try without this fix and tell me what
* happens on your computer.
*/
const float zero_lower =-0.00000001;
const float zero_upper = 0.00000001;
all = 0;
if(zero_lower < y_1 && y_1 < zero_upper)
y_1 = y_2 = 0.0;
}
unsigned long i;
float tmp;
for (i=0; i<samples; i++)
{
x_0 = invalue[i];
tmp = x_0 * b0 + x_1 * b1 + x_2 * b2 - y_1 * a1 - y_2 * a2;
x_2 = x_1; x_1 = x_0; y_2 = y_1; y_1 = tmp;
outvalue[i] = tmp;
}
}
};
REGISTER_IMPLEMENTATION(Synth_STD_EQUALIZER_impl);