| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2006-2007 Thom Johansen |
| * Copyright (C) 2012 Michael Sevakis |
| * |
| * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| #include "rbcodecconfig.h" |
| #include "fixedpoint.h" |
| #include "fracmul.h" |
| #include "dsp_filter.h" |
| #include "dsp_proc_entry.h" |
| #include "dsp_core.h" |
| #include "dsp_misc.h" |
| #include "eq.h" |
| #include "pga.h" |
| #include "replaygain.h" |
| #include <string.h> |
| |
| /** |
| * Current setup is one lowshelf filters eight peaking filters and one |
| * 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. |
| */ |
| |
| #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 |
| |
| /* Cached band settings */ |
| static struct eq_band_setting settings[EQ_NUM_BANDS]; |
| |
| static struct eq_state |
| { |
| 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; |
| |
| #define FOR_EACH_ENB_BAND(b) \ |
| for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++) |
| |
| /* Clear histories of all enabled bands */ |
| static void eq_flush(void) |
| { |
| if (eq_data.enabled == 0) |
| return; /* Not initialized yet/no bands on */ |
| |
| FOR_EACH_ENB_BAND(b) |
| filter_flush(&eq_data.filters[*b]); |
| } |
| |
| 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); |
| } |
| |
| /** DSP interface **/ |
| |
| /* Set the precut gain value */ |
| void dsp_set_eq_precut(int precut) |
| { |
| pga_set_gain(PGA_EQ_PRECUT, get_replaygain_int(precut * -10)); |
| } |
| |
| /* 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; |
| |
| settings[band] = *setting; /* cache setting */ |
| |
| struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); |
| |
| /* 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); |
| update_band_filter(band, dsp_get_output_frequency(dsp)); |
| } |
| |
| if (mask == eq_data.enabled) |
| return; /* No change in band-enable state */ |
| |
| if (mask & BIT_N(band)) |
| filter_flush(&eq_data.filters[band]); /* Coming online */ |
| |
| eq_data.enabled = mask; |
| |
| /* 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); |
| |
| eq_data.bands[band] = EQ_NUM_BANDS; |
| } |
| |
| /* Enable or disable the equalizer */ |
| void dsp_eq_enable(bool enable) |
| { |
| struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); |
| bool enabled = dsp_proc_enabled(dsp, DSP_PROC_EQUALIZER); |
| |
| if (enable == enabled) |
| return; |
| |
| dsp_proc_enable(dsp, DSP_PROC_EQUALIZER, enable); |
| |
| if (enable && eq_data.enabled != 0) |
| dsp_proc_activate(dsp, DSP_PROC_EQUALIZER, true); |
| } |
| |
| /* 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) |
| { |
| struct dsp_buffer *buf = *buf_p; |
| int count = buf->remcount; |
| unsigned int channels = buf->format.num_channels; |
| |
| FOR_EACH_ENB_BAND(b) |
| filter_process(&eq_data.filters[*b], buf->p32, count, channels); |
| |
| (void)this; |
| } |
| |
| /* DSP message hook */ |
| static intptr_t eq_configure(struct dsp_proc_entry *this, |
| struct dsp_config *dsp, |
| unsigned int setting, |
| intptr_t value) |
| { |
| switch (setting) |
| { |
| case DSP_PROC_INIT: |
| this->process = eq_process; |
| /* Wouldn't have been getting frequency updates */ |
| update_samplerate(dsp_get_output_frequency(dsp)); |
| /* Fall-through */ |
| case DSP_PROC_CLOSE: |
| pga_enable_gain(PGA_EQ_PRECUT, setting == DSP_PROC_INIT); |
| break; |
| |
| case DSP_FLUSH: |
| eq_flush(); |
| break; |
| |
| case DSP_SET_OUT_FREQUENCY: |
| update_samplerate(value); |
| break; |
| } |
| |
| return 0; |
| (void)dsp; |
| } |
| |
| /* Database entry */ |
| DSP_PROC_DB_ENTRY(EQUALIZER, |
| eq_configure); |