2006-01-29 02:10:28 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
2012-03-27 23:52:15 +00:00
|
|
|
* Copyright (C) 2006-2007 Thom Johansen
|
|
|
|
* Copyright (C) 2012 Michael Sevakis
|
2006-01-29 02:10:28 +00:00
|
|
|
*
|
2008-06-28 18:10:04 +00:00
|
|
|
* 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.
|
2006-01-29 02:10:28 +00:00
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
2022-12-18 23:55:36 +00:00
|
|
|
#include "platform.h"
|
2009-07-05 18:06:07 +00:00
|
|
|
#include "fixedpoint.h"
|
|
|
|
#include "fracmul.h"
|
2012-03-27 23:52:15 +00:00
|
|
|
#include "dsp_filter.h"
|
2012-04-29 21:31:30 +00:00
|
|
|
#include "dsp_proc_entry.h"
|
|
|
|
#include "dsp_core.h"
|
2013-05-23 17:58:51 +00:00
|
|
|
#include "dsp_misc.h"
|
2012-04-29 21:31:30 +00:00
|
|
|
#include "eq.h"
|
|
|
|
#include "pga.h"
|
2007-02-05 01:01:15 +00:00
|
|
|
#include "replaygain.h"
|
2012-03-27 23:52:15 +00:00
|
|
|
#include <string.h>
|
2006-01-29 02:10:28 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/**
|
2013-01-22 18:08:16 +00:00
|
|
|
* Current setup is one lowshelf filters eight peaking filters and one
|
2012-03-27 23:52:15 +00:00
|
|
|
* highshelf filter. Varying the number of shelving filters make no sense,
|
|
|
|
* but adding peaking filters is possible. Check EQ_NUM_BANDS to have
|
|
|
|
* 2 shelving filters and EQ_NUM_BANDS-2 peaking filters.
|
2006-04-11 13:49:05 +00:00
|
|
|
*/
|
2012-03-27 23:52:15 +00:00
|
|
|
|
|
|
|
#if EQ_NUM_BANDS < 3
|
|
|
|
/* No good. Expect at least 1 peaking and low/high shelving filters */
|
|
|
|
#error Band count must be greater than or equal to 3
|
|
|
|
#endif
|
|
|
|
|
2013-05-23 17:58:51 +00:00
|
|
|
/* Cached band settings */
|
|
|
|
static struct eq_band_setting settings[EQ_NUM_BANDS];
|
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
static struct eq_state
|
2006-04-11 13:49:05 +00:00
|
|
|
{
|
2012-03-27 23:52:15 +00:00
|
|
|
uint32_t enabled; /* Mask of enabled bands */
|
|
|
|
uint8_t bands[EQ_NUM_BANDS+1]; /* Indexes of enabled bands */
|
|
|
|
struct dsp_filter filters[EQ_NUM_BANDS]; /* Data for each filter */
|
|
|
|
} eq_data IBSS_ATTR;
|
2006-04-11 13:49:05 +00:00
|
|
|
|
2013-05-23 17:58:51 +00:00
|
|
|
#define FOR_EACH_ENB_BAND(b) \
|
|
|
|
for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++)
|
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/* Clear histories of all enabled bands */
|
|
|
|
static void eq_flush(void)
|
|
|
|
{
|
|
|
|
if (eq_data.enabled == 0)
|
|
|
|
return; /* Not initialized yet/no bands on */
|
2006-04-11 13:49:05 +00:00
|
|
|
|
2013-05-23 17:58:51 +00:00
|
|
|
FOR_EACH_ENB_BAND(b)
|
2012-03-27 23:52:15 +00:00
|
|
|
filter_flush(&eq_data.filters[*b]);
|
2006-04-11 13:49:05 +00:00
|
|
|
}
|
|
|
|
|
2013-05-23 17:58:51 +00:00
|
|
|
static void update_band_filter(int band, unsigned int fout)
|
|
|
|
{
|
|
|
|
/* Convert user settings to format required by coef generator
|
|
|
|
functions */
|
|
|
|
typeof (filter_pk_coefs) *coef_gen = filter_pk_coefs;
|
|
|
|
|
|
|
|
/* Only first and last bands are not peaking filters */
|
|
|
|
if (band == 0)
|
|
|
|
coef_gen = filter_ls_coefs;
|
|
|
|
else if (band == EQ_NUM_BANDS-1)
|
|
|
|
coef_gen = filter_hs_coefs;
|
|
|
|
|
|
|
|
const struct eq_band_setting *setting = &settings[band];
|
|
|
|
struct dsp_filter *filter = &eq_data.filters[band];
|
|
|
|
|
|
|
|
coef_gen(fp_div(setting->cutoff, fout, 32), setting->q ?: 1,
|
|
|
|
setting->gain, filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resync all bands to a new DSP output frequency */
|
|
|
|
static void update_samplerate(unsigned int fout)
|
|
|
|
{
|
|
|
|
if (eq_data.enabled == 0)
|
|
|
|
return; /* Not initialized yet/no bands on */
|
|
|
|
|
|
|
|
FOR_EACH_ENB_BAND(b)
|
|
|
|
update_band_filter(*b, fout);
|
|
|
|
}
|
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/** DSP interface **/
|
|
|
|
|
|
|
|
/* Set the precut gain value */
|
|
|
|
void dsp_set_eq_precut(int precut)
|
2007-02-26 00:41:26 +00:00
|
|
|
{
|
2012-03-27 23:52:15 +00:00
|
|
|
pga_set_gain(PGA_EQ_PRECUT, get_replaygain_int(precut * -10));
|
|
|
|
}
|
2007-02-26 00:41:26 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/* Update the filter configuration for the band */
|
|
|
|
void dsp_set_eq_coefs(int band, const struct eq_band_setting *setting)
|
|
|
|
{
|
|
|
|
if (band < 0 || band >= EQ_NUM_BANDS)
|
|
|
|
return;
|
|
|
|
|
2013-05-23 17:58:51 +00:00
|
|
|
settings[band] = *setting; /* cache setting */
|
|
|
|
|
|
|
|
struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
|
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/* NOTE: The coef functions assume the EMAC unit is in fractional mode,
|
|
|
|
which it should be, since we're executed from the main thread. */
|
|
|
|
|
|
|
|
uint32_t mask = eq_data.enabled;
|
|
|
|
|
|
|
|
/* Assume a band is disabled if the gain is zero */
|
|
|
|
mask &= ~BIT_N(band);
|
|
|
|
|
|
|
|
if (setting->gain != 0)
|
|
|
|
{
|
|
|
|
mask |= BIT_N(band);
|
2013-05-23 17:58:51 +00:00
|
|
|
update_band_filter(band, dsp_get_output_frequency(dsp));
|
2012-03-27 23:52:15 +00:00
|
|
|
}
|
2007-02-26 00:41:26 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
if (mask == eq_data.enabled)
|
|
|
|
return; /* No change in band-enable state */
|
2007-02-26 00:41:26 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
if (mask & BIT_N(band))
|
2013-05-23 17:58:51 +00:00
|
|
|
filter_flush(&eq_data.filters[band]); /* Coming online */
|
2007-02-05 01:01:15 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
eq_data.enabled = mask;
|
2006-01-29 02:10:28 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/* Only be active if there are bands to process - if EQ is off, then
|
|
|
|
this call has no effect */
|
|
|
|
dsp_proc_activate(dsp, DSP_PROC_EQUALIZER, mask != 0);
|
|
|
|
|
|
|
|
/* Prepare list of enabled bands for efficient iteration */
|
|
|
|
for (band = 0; mask != 0; mask &= mask - 1, band++)
|
|
|
|
eq_data.bands[band] = (uint8_t)find_first_set_bit(mask);
|
2006-01-29 02:10:28 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
eq_data.bands[band] = EQ_NUM_BANDS;
|
2006-01-29 02:10:28 +00:00
|
|
|
}
|
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/* Enable or disable the equalizer */
|
|
|
|
void dsp_eq_enable(bool enable)
|
2006-01-29 02:10:28 +00:00
|
|
|
{
|
2012-03-27 23:52:15 +00:00
|
|
|
struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
|
2013-05-23 17:58:51 +00:00
|
|
|
bool enabled = dsp_proc_enabled(dsp, DSP_PROC_EQUALIZER);
|
|
|
|
|
|
|
|
if (enable == enabled)
|
|
|
|
return;
|
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
dsp_proc_enable(dsp, DSP_PROC_EQUALIZER, enable);
|
2006-01-29 02:10:28 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
if (enable && eq_data.enabled != 0)
|
|
|
|
dsp_proc_activate(dsp, DSP_PROC_EQUALIZER, true);
|
2006-01-29 02:10:28 +00:00
|
|
|
}
|
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/* Apply EQ filters to those bands that have got it switched on. */
|
|
|
|
static void eq_process(struct dsp_proc_entry *this,
|
|
|
|
struct dsp_buffer **buf_p)
|
2006-01-29 02:10:28 +00:00
|
|
|
{
|
2012-03-27 23:52:15 +00:00
|
|
|
struct dsp_buffer *buf = *buf_p;
|
|
|
|
int count = buf->remcount;
|
|
|
|
unsigned int channels = buf->format.num_channels;
|
2006-01-29 02:10:28 +00:00
|
|
|
|
2013-05-23 17:58:51 +00:00
|
|
|
FOR_EACH_ENB_BAND(b)
|
2012-03-27 23:52:15 +00:00
|
|
|
filter_process(&eq_data.filters[*b], buf->p32, count, channels);
|
2006-01-29 02:10:28 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
(void)this;
|
2006-01-29 02:10:28 +00:00
|
|
|
}
|
2007-02-05 01:01:15 +00:00
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/* DSP message hook */
|
|
|
|
static intptr_t eq_configure(struct dsp_proc_entry *this,
|
|
|
|
struct dsp_config *dsp,
|
|
|
|
unsigned int setting,
|
|
|
|
intptr_t value)
|
2006-01-29 02:10:28 +00:00
|
|
|
{
|
2012-03-27 23:52:15 +00:00
|
|
|
switch (setting)
|
|
|
|
{
|
|
|
|
case DSP_PROC_INIT:
|
2012-12-19 22:34:57 +00:00
|
|
|
this->process = eq_process;
|
2013-05-23 17:58:51 +00:00
|
|
|
/* Wouldn't have been getting frequency updates */
|
|
|
|
update_samplerate(dsp_get_output_frequency(dsp));
|
2012-12-19 22:34:57 +00:00
|
|
|
/* Fall-through */
|
2012-03-27 23:52:15 +00:00
|
|
|
case DSP_PROC_CLOSE:
|
|
|
|
pga_enable_gain(PGA_EQ_PRECUT, setting == DSP_PROC_INIT);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DSP_FLUSH:
|
|
|
|
eq_flush();
|
|
|
|
break;
|
2013-05-23 17:58:51 +00:00
|
|
|
|
|
|
|
case DSP_SET_OUT_FREQUENCY:
|
|
|
|
update_samplerate(value);
|
|
|
|
break;
|
2006-03-09 09:40:03 +00:00
|
|
|
}
|
2012-03-27 23:52:15 +00:00
|
|
|
|
2012-12-19 22:34:57 +00:00
|
|
|
return 0;
|
2012-03-27 23:52:15 +00:00
|
|
|
(void)dsp;
|
2006-01-29 02:10:28 +00:00
|
|
|
}
|
|
|
|
|
2012-03-27 23:52:15 +00:00
|
|
|
/* Database entry */
|
|
|
|
DSP_PROC_DB_ENTRY(EQUALIZER,
|
|
|
|
eq_configure);
|