mirror of
https://github.com/DrBeef/QuestZDoom.git
synced 2025-05-05 07:21:28 +00:00
145 lines
4.5 KiB
C
145 lines
4.5 KiB
C
/* Effect: firfit filter Copyright (c) 2009 robs@users.sourceforge.net
|
|
*
|
|
* This library is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This library 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 Lesser
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this library; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/* This is W.I.P. hence marked SOX_EFF_ALPHA for now.
|
|
* Need to add other interpolation types e.g. linear, bspline, window types,
|
|
* and filter length, maybe phase response type too.
|
|
*/
|
|
|
|
#include "sox_i.h"
|
|
#include "dft_filter.h"
|
|
|
|
typedef struct {
|
|
dft_filter_priv_t base;
|
|
char const * filename;
|
|
struct {double f, gain;} * knots;
|
|
int num_knots, n;
|
|
} priv_t;
|
|
|
|
static int create(sox_effect_t * effp, int argc, char **argv)
|
|
{
|
|
priv_t * p = (priv_t *)effp->priv;
|
|
dft_filter_priv_t * b = &p->base;
|
|
b->filter_ptr = &b->filter;
|
|
--argc, ++argv;
|
|
if (argc == 1)
|
|
p->filename = argv[0], --argc;
|
|
p->n = 2047;
|
|
return argc? lsx_usage(effp) : SOX_SUCCESS;
|
|
}
|
|
|
|
static double * make_filter(sox_effect_t * effp)
|
|
{
|
|
priv_t * p = (priv_t *)effp->priv;
|
|
double * log_freqs, * gains, * d, * work, * h;
|
|
sox_rate_t rate = effp->in_signal.rate;
|
|
int i, work_len;
|
|
|
|
lsx_valloc(log_freqs , p->num_knots);
|
|
lsx_valloc(gains, p->num_knots);
|
|
lsx_valloc(d , p->num_knots);
|
|
for (i = 0; i < p->num_knots; ++i) {
|
|
log_freqs[i] = log(max(p->knots[i].f, 1));
|
|
gains[i] = p->knots[i].gain;
|
|
}
|
|
lsx_prepare_spline3(log_freqs, gains, p->num_knots, HUGE_VAL, HUGE_VAL, d);
|
|
|
|
for (work_len = 8192; work_len < rate / 2; work_len <<= 1);
|
|
work = lsx_calloc(work_len + 2, sizeof(*work));
|
|
lsx_valloc(h, p->n);
|
|
|
|
for (i = 0; i <= work_len; i += 2) {
|
|
double f = rate * 0.5 * i / work_len;
|
|
double spl1 = f < max(p->knots[0].f, 1)? gains[0] :
|
|
f > p->knots[p->num_knots - 1].f? gains[p->num_knots - 1] :
|
|
lsx_spline3(log_freqs, gains, d, p->num_knots, log(f));
|
|
work[i] = dB_to_linear(spl1);
|
|
}
|
|
LSX_PACK(work, work_len);
|
|
lsx_safe_rdft(work_len, -1, work);
|
|
for (i = 0; i < p->n; ++i)
|
|
h[i] = work[(work_len - p->n / 2 + i) % work_len] * 2. / work_len;
|
|
lsx_apply_blackman_nutall(h, p->n);
|
|
|
|
free(work);
|
|
return h;
|
|
}
|
|
|
|
static sox_bool read_knots(sox_effect_t * effp)
|
|
{
|
|
priv_t * p = (priv_t *) effp->priv;
|
|
FILE * file = lsx_open_input_file(effp, p->filename, sox_true);
|
|
sox_bool result = sox_false;
|
|
int num_converted = 1;
|
|
char c;
|
|
|
|
if (file) {
|
|
lsx_valloc(p->knots, 1);
|
|
while (fscanf(file, " #%*[^\n]%c", &c) >= 0) {
|
|
num_converted = fscanf(file, "%lf %lf",
|
|
&p->knots[p->num_knots].f, &p->knots[p->num_knots].gain);
|
|
if (num_converted == 2) {
|
|
if (p->num_knots && p->knots[p->num_knots].f <= p->knots[p->num_knots - 1].f) {
|
|
lsx_fail("knot frequencies must be strictly increasing");
|
|
break;
|
|
}
|
|
lsx_revalloc(p->knots, ++p->num_knots + 1);
|
|
} else if (num_converted != 0)
|
|
break;
|
|
}
|
|
lsx_report("%i knots", p->num_knots);
|
|
if (feof(file) && num_converted != 1)
|
|
result = sox_true;
|
|
else lsx_fail("error reading knot file `%s', line number %u", p->filename, 1 + p->num_knots);
|
|
if (file != stdin)
|
|
fclose(file);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int start(sox_effect_t * effp)
|
|
{
|
|
priv_t * p = (priv_t *) effp->priv;
|
|
dft_filter_t * f = p->base.filter_ptr;
|
|
|
|
if (!f->num_taps) {
|
|
double * h;
|
|
if (!p->num_knots && !read_knots(effp))
|
|
return SOX_EOF;
|
|
h = make_filter(effp);
|
|
if (effp->global_info->plot != sox_plot_off) {
|
|
lsx_plot_fir(h, p->n, effp->in_signal.rate,
|
|
effp->global_info->plot, "SoX effect: firfit", -30., +30.);
|
|
return SOX_EOF;
|
|
}
|
|
lsx_set_dft_filter(f, h, p->n, p->n >> 1);
|
|
}
|
|
return lsx_dft_filter_effect_fn()->start(effp);
|
|
}
|
|
|
|
sox_effect_handler_t const * lsx_firfit_effect_fn(void)
|
|
{
|
|
static sox_effect_handler_t handler;
|
|
handler = *lsx_dft_filter_effect_fn();
|
|
handler.name = "firfit";
|
|
handler.usage = "[knots-file]";
|
|
handler.flags |= SOX_EFF_ALPHA;
|
|
handler.getopts = create;
|
|
handler.start = start;
|
|
handler.priv_size = sizeof(priv_t);
|
|
return &handler;
|
|
}
|